Posted on Leave a comment

How to Control the GPIO of PIC16F877A using MPLAB X IDE

The PIC16F877A microcontroller is a popular choice for embedded systems development due to its versatility and ease of use. One of the essential aspects of working with microcontrollers is controlling General Purpose Input/Output (GPIO) pins. In this blog post, we will explore how to control the GPIO pins of the PIC16F877A using MPLAB X IDE, a powerful Integrated Development Environment.

Prerequisites:
To follow along with this tutorial, you will need the following:

  1. PIC16F877A microcontroller.
  2. MPLAB X IDE installed on your computer.
  3. MPLAB XC8 Compiler.

Step 1: Create a New Project
Launch MPLAB X IDE and create a new project by navigating to File -> New Project. Select “Microchip Embedded” under “Categories” and “Standalone Project” under “Projects”. Choose the PIC16F877A as the device and specify a name and location for your project. Click “Finish” to create the project.

Step 2: Configure the GPIO Pins
To control the GPIO pins, we need to configure them as inputs or outputs. In the project window, open the “main.c” source file. Locate the main function and add the necessary code to configure the GPIO pins.

To set a pin as an output, use the TRISx register, where x represents the port name (A, B, C, etc.). For example, to set RB0 as an output pin, use the following code:

TRISBbits.TRISB0 = 0; // Set RB0 as an output pin

To set a pin as an input, use the same TRISx register and set the corresponding bit to 1. For example, to set RA2 as an input pin, use the following code:

TRISAbits.TRISA2 = 1; // Set RA2 as an input pin

Step 3: Control the GPIO Pins Once the GPIO pins are configured, we can control their state by manipulating the corresponding PORT registers. To set an output pin high (logic level 1), write 1 to the corresponding bit in the PORT register. For example, to set RB0 high, use the following code:

PORTBbits.RB0 = 1; // Set RB0 high

To set an output pin low (logic level 0), write 0 to the corresponding bit in the PORT register. For example, to set RB0 low, use the following code:

PORTBbits.RB0 = 0; // Set RB0 low

To read the state of an input pin, you can directly access the corresponding PORT register. For example, to read the state of RD2, use the following code:

if (PORTAbits.RD2 == 1) {
    // RD2 is high
} else {
    // RD2 is low
}

PORTA: It is configured as an analog port by default.
When you want to use it as a Digital I/O port, you have to configure the ADCON1 register.

Demo Code

/* 
 * File:   main.c
 * Author: abhay
 *
 * Created on July 14, 2023, 12:05 AM
 */

// PIC16F877A Configuration Bit Settings

// 'C' source line config statements

// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#define _XTAL_FREQ 16000000
#include <xc.h>
#include <pic16f877a.h>
#include <stdio.h>
#include <stdlib.h>
#include "board.h"

/*
 * 
 */
int main(int argc, char** argv) {
    /*
     * TRIS = Data Direction Register
     * 0 = OUTPUT
     * 1 = INPUT

     */
// Make the Pin 1 of PORT D as output 
      TRISD &= ~(1 << 1); // LED RD1 as OUTPUT
      TRISD1 = 0; // RD1 as OUTPUT
     
    
    // Make the Pin 0 of PORT A as digital input
    ADCON1bits.PCFG0 = 0;
    ADCON1bits.PCFG1 = 1;
    ADCON1bits.PCFG2 = 1;
    ADCON1bits.PCFG3 = 0;
    TRISA0 = 1; // button 
    
    while (1) {
        if((RA0 ) == 1)
        {
            RD1 = 1;
        }
        else {
            PORTD &= ~(1<<1);
        }
       
    }

    return (EXIT_SUCCESS);
}

Step 4: Build and Program the Microcontroller
Now that we have written the code, it’s time to build and program the microcontroller. Connect your PIC16F877A microcontroller to your computer via a suitable programmer/debugger. Ensure that the proper hardware connections are made.

Step 5: Test the GPIO Control
Once the programming is complete, disconnect the programming cable and power the microcontroller using an appropriate power supply. Connect LEDs, switches, or other devices to the configured GPIO pins. Execute the code on the microcontroller and observe the desired behavior of the GPIO pins based on the control logic implemented in your code.

Conclusion:
Controlling the GPIO pins of the PIC16F877A microcontroller using MPLAB X IDE is a fundamental skill in embedded systems development. By following this step-by-step guide, you have learned how to configure the GPIO pins as inputs or outputs and manipulate their states using the appropriate registers. With this knowledge, you can now start building a wide range of projects that involve interacting with the external world through GPIO pins.

Remember to refer to the PIC16F877A datasheet for detailed information on register names, bit assignments, and other specific details related to the microcontroller.

Happy coding and exploring the world of embedded systems!

Posted on Leave a comment

How to Blink LED on Mini STM32 v3.0 Discovery Board Using STM32CubeIDE

Step 2: Configure GPIO Pins

#define LED1_Pin GPIO_PIN_2
#define LED1_GPIO_Port GPIOA
#define LED2_Pin GPIO_PIN_3
#define LED2_GPIO_Port GPIOA

In the Project Explorer pane, expand the “Src” folder and open the “main.c” file. Scroll down to the main() function and add the following code to configure the GPIO pins:

/* Configure GPIO pins */
__HAL_RCC_GPIOA_CLK_ENABLE();      // Enable GPIOA clock
GPIO_InitTypeDef GPIO_InitStruct; // GPIO configuration structure
GPIO_InitStruct.Pin = GPIO_PIN_2; // Use pin 2 on GPIOA
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // Push-Pull output mode
GPIO_InitStruct.Pull = GPIO_NOPULL; // No pull-up or pull-down resistors
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // Low speed
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // Initialize GPIOA with settings

This code configures pin 2 on GPIOA as a push-pull output pin with no pull-up or pull-down resistors and low output speed.

Blink the LED using HAL_GPIO_WritePin

Add the following code inside the while(1) loop to blink the LED:

/* Blink LED */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); // Turn LED on
HAL_Delay(1000); // Wait for 1 second
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); // Turn LED off
HAL_Delay(1000); // Wait for 1 second

This code turns the LED on by setting the state of pin 2 on GPIOA to high, waits for 1 second, turns the LED off by setting the state of pin 2 on GPIOA to low, and then waits for 1 second again. This creates a blinking effect.

Blink the LED using HAL_GPIO_TogglePin

/* Blink LED */
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2); // Toggle the state of the LED
HAL_Delay(1000); // Wait for 1 second

This code toggles the state of pin 2 on GPIOA (which is connected to an LED on the Mini STM32 board) and then waits for 1 second before toggling it again. This creates a blinking effect.

HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);

Code to Blink both LED’s

HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);

The first line toggles the state of an LED connected to the LED1_GPIO_Port and LED1_Pin. The second line waits for 1000 milliseconds (1 second) using the HAL_Delay() function. The third line toggles the state of another LED connected to the LED2_GPIO_Port and LED2_Pin.

Posted on Leave a comment

AVR Input Output Port Programming

In AVR microcontroller programming, input/output ports are used to interface with external devices such as sensors, switches, LEDs, motors, and other peripherals. Here’s an example of how to program AVR input/output ports using C language:

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    // Set PORTB as output and PORTC as input
    DDRB = 0xFF;
    DDRC = 0x00;

    while(1)
    {
        // Read the value of PINC3
        if(PINC & (1 << PINC3))
        {
            // If PINC3 is high, turn on LED connected to PB0
            PORTB |= (1 << PB0);
        }
        else
        {
            // If PINC3 is low, turn off LED connected to PB0
            PORTB &= ~(1 << PB0);
        }
    }
}

In this example, we set PORTB as an output port by setting all of its pins to output mode. We set PORTC as an input port by setting all of its pins to input mode. Then, we use a while loop to continuously check the value of PINC3. If PINC3 is high, we turn on an LED connected to PB0 by setting the corresponding bit in PORTB to high. If PINC3 is low, we turn off the LED by setting the corresponding bit in PORTB to low.

Note that the & and | operators are used to manipulate individual bits in the port registers. The << operator is used to shift the binary value of 1 to the left by a certain number of bits to set a particular bit high or low. The ~ operator is used to invert the value of a bit. The util/delay.h library is used to create a delay between each loop iteration.

Posted on Leave a comment

Battery Monitoring with Led Light Control Using ESP32 Bluetooth for my Solar System

I need to monitor battery voltage to check weather my charging system is working correctly or not. But to do that i have get up and walk with my multimeter towards the battery and i have to take these reading in night.

I placed my battery in a corner where there is very little light. So I added a transistor switch which can be controlled using Bluetooth to turn on the led light. Which provides enough light to act as a night light.

The ESP32 also has an ADC built into it. Which is very poor in terms of accuracy. It gets you the idea that there is something to work with but it does not give very precise reading like a multimeter.

Also, the ESP32 ADC is non-linear. The ADC also has an attenuation feature. Which by default is set to 11db. which gives us a workable range of up to 3.3V. But there are flat-out regions which need to be taken into account if you want to measure anything from this ADC. There is a 0 – 0.2V region in which the value read is constant, and there is a 2.9V to 3.3V region which also gives you constant reading values.

Resolution is 12-bit by default.

To measure a large voltage using this device. I made a voltage divider.

Battery +ve———/\/\/ R1 \/\/\/ ———— Vout —————— /\/\/\/\/ R2 \/\/\/\/ ———-GND

R1 = 10 kilo Ohm

R2 = 1 kilo Ohm

Which gives me a dividing factor of 11.

So if 11 V is available at the battery anode. Then the V out is 1V.

Normally the lead acid battery voltage goes from 10V(fully discharged) to 14.6V(Maximum charge) to 15V(Over Charged)

The ADC values are converted to battery voltage using the following equation

( (analogValue * (3.3/4096 ) ) * ((9820+985)/985) ) + 3.3

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;


char var1;
int analogValue;
uint8_t i;
int avg_adc;

void setup()
{
  Serial.begin(115200);
  SerialBT.begin("ESP32test"); //Bluetooth device name
  Serial.println("pair it with bluetooth!");
pinMode(2, OUTPUT);
 
}

void loop()
{
  /*
   * UART 
   */
  if (Serial.available()) {
  
    SerialBT.write(Serial.read());
  }


/*
 * Bluetooth serial
 */
  if (SerialBT.available()) {
      var1 = SerialBT.read();
    Serial.write(var1);
  }
    if (var1 == '9')
{
  digitalWrite(2,HIGH);
}
else if(var1 == '1')
{
  digitalWrite(2,LOW);
}


avg_adc = 0;
for(i =0 ; i< 100; i++)
{
  avg_adc += analogRead(34);
}
analogValue = avg_adc / 100;

SerialBT.printf("ADC = %d\n",analogValue);
  SerialBT.printf("volt = %f\n",   ( (analogValue * (3.3/4096 ) ) * ((9820+985)/985) ) + 3.3 ) ;
    delay(1000);
}

Since the ADC is not accurate it goes all over the place. To dampen its effect on the reading I am averaging 100 readings of ADC.

avg_adc = 0;
for(i =0 ; i< 100; i++)
{
  avg_adc += analogRead(34);
}
analogValue = avg_adc / 100;

TIP3055 BJT is used as a low-side switch. R1 gives a base current of 330uA(=3.3/10000) which gets multiplied by the beta or hFE 70 of transistor to get a collector current of 0.023A or 23mA.

Posted on Leave a comment

How to make ATmega GPIO work as Input or Output

AVR Input Output Port programming means you have to program its GPIO pins.

GPIO – General Purpose Input and Output

The GPIO is a very important peripheral of the microcontroller. Using the GPIO we can use the same exact pin for Input or Output.

To make the PIN input or output we need to set a bit in the data direction register DDR

Now lets take the example of ATmega16. Atmega 16 has 4 ports.
-PORT A
-PORT B
-PORT C
-PORT D

Each PORT has 8 pin
Each pin can be addressed individually
PortA.1 = Pin 1 of Port A
PA.1 = Pin 1 of Port A

To make a PIN input/output
DDRx = 1 // make the pin output
DDRx = 0 // make the pin input

Here x is to be replaced by port number
DDRA = DDR for port A


To Set the value of the pin we use
PORTx = 1 // set all the pins of the port to HIGH
PORTx = 0// Set all the pins of the port to LOW

To Read the value of the pin we use
PINx
usage:
int read_in;
DDRx = 0;
PORTx = 1; // This will enable the internal Pull up
read_in = PINx; // read_in variable will store the value in PINx

If the internal pull-up is not enabled than then value of the pin will always be in a floating state and it will not tell the accurate result.
When the internal pull-up is not enabled then the external pull has to be enabled by the user.