• Interface LCD 16×2 Character in 4-Bit Mode with PIC16F877A

    To save the number of pins of the microcontroller we interface the character LCD in 4-bit mode.

    If we take a look at the PINS of the character LCD

    [ VSS | VDD | V0 | R/S | R/W | E | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | A | K ]

    If we were to use this LCD in 8-bit mode we have to use 8 GPIO pins for the DATA (D0-D7) and three GPIO Pins for control(R/S, R/W, E).

    But in 4-bit mode, we use three control pins and only four data pins D4-D7 .

    Schematics

    Schematics diagram for Home Automation using Bluetooth

    In the above schematic, I have interfaced the LCD in 4-bit mode.

    LCD BACKLIGHT
    I have connected a 220 ohm resistor to the K pin; since my lcd module has a 100 ohm resistor on board. It is labeled R8. So 100 + 220 = 320 ohm resistance total.
    If the LED takes approx. 3V forward volage then the current will be given by equation
    Current = (VCC – Forward Voltage) / Total resistance
    => (5v-3v)/320ohm = 2v / 320 ohm = 0.00625 A = 6.25 mA(approx.)

    LCD Command

    You can make your own commands using this table shown below.

    CODE

    main.hLCD_16x2.hboard.h
    /* 
     * 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 <stdint.h>
    #include "board.h"
    #include "LCD_16x2.h"
    #include<string.h>
    
    char GPIO_init(void);
    
    /*
     * 
     */
    int main(int argc, char** argv) {
        GPIO_init();
        InitLCD(); // Initialize LCD in 8bit mode
    
        //const char *msg = "Hello World!";
        // const char *msg1 = "Abhay";
        //WriteStringToLCD(msg1);
        char lcd_buff1[16] = "Abhay Kant";
        char lcd_buff2[16] = "Kant";
        
        
        lcd_set_cursor(1, 1);
        WriteDataToLCD('E');
        WriteDataToLCD('X');
        WriteDataToLCD('A');
        WriteDataToLCD('S');
        WriteDataToLCD('U');
        WriteDataToLCD('B');
        WriteDataToLCD('.');
        WriteDataToLCD('C');
        WriteDataToLCD('O');
        WriteDataToLCD('M');
    
        lcd_set_cursor(2, 1);
        WriteStringToLCD(lcd_buff1);
    
        while (1) {
    
        }
    
        return (EXIT_SUCCESS);
    }
    
    char GPIO_init(void) {
        /*
         * TRIS = Data Direction Register
         * 0 = OUTPUT
         * 1 = INPUT
         * TRISD &= ~(1 << 1); // LED RD1 as OUTPUT
         * TRISD1 = 0; // RD1 as OUTPUT
         */
    
    
    
        LED_PORT_DIR &= ~(1 << 1);
        LED_PIN = 0;
    
        Relay_1a_DIR &= ~(1 << 0);
        Relay_1a_PIN = 0;
    
        Relay_1b_DIR &= ~(1 << 2);
        Relay_1b_PIN = 0;
    
        Relay_2a_DIR &= ~(1 << 4);
        Relay_2a_PIN = 0;
    
        Relay_2b_DIR &= ~(1 << 5);
        Relay_2b_PIN = 0;
    
        /*
         * LCD Pins Initialize
         */
        TRISB = 0;
        return 0;
    }
    /*
     * File:   LCD_16x2.h
     * Author: abhay
     *
     * Created on July 16, 2023, 6:21 PM
     */
    
    #ifndef LCD_16X2_H
    #define	LCD_16X2_H
    
    #include "board.h"
    
    
    #ifdef	__cplusplus
    extern "C" {
    #endif
        /*
         *Sr.No.	Hex Code	Command to LCD instruction Register
            1       01          Clear display screen
            2       02          Return home
            3       04          Decrement cursor (shift cursor to left)
            4       06          Increment cursor (shift cursor to right)
            5       05          Shift display right
            6       07          Shift display left
            7       08          Display off, cursor off
            8       0A          Display off, cursor on
            9       0C          Display on, cursor off
            10  	0E          Display on, cursor blinking off
            11  	0F          Display on, cursor blinking on
            12      10          Shift cursor position to left
            13      14          Shift the cursor position to the right
            14      18          Shift the entire display to the left
            15      1C          Shift the entire display to the right
            16      80          Force cursor to the beginning ( 1st line)
            17  	C0          Force cursor to the beginning ( 2nd line)
            18  	38          2 lines and 5×7 matrix
         */
        // Define Pins
    
    #define LCD_RS         RB1     // RS pin for LCD
    #define LCD_RW         RB2     // RS pin for LCD
    #define LCD_E          RB3     // Enable pin for LCD
    #define LCD_Data_Bus_D4    RB4    // Data bus bit 4
    #define LCD_Data_Bus_D5    RB5    // Data bus bit 5
    #define LCD_Data_Bus_D6    RB6    // Data bus bit 6
    #define LCD_Data_Bus_D7    RB7    // Data bus bit 7
        // Define Pins direction registrers
    #define LCD_E_Dir           TRISB3
    #define LCD_RS_Dir          TRISB1
    #define LCD_RW_Dir          TRISB2
    #define LCD_Data_Bus_Dir_D4     TRISB4
    #define LCD_Data_Bus_Dir_D5     TRISB5
    #define LCD_Data_Bus_Dir_D6    TRISB6
    #define LCD_Data_Bus_Dir_D7   TRISB7
        // Constants
    #define E_Delay       1000
        // Function Declarations
        void WriteCommandToLCD(unsigned char);
        void WriteDataToLCD(char);
        void InitLCD(void);
        void WriteStringToLCD(const char*);
        void ClearLCDScreen(void);
    
        void ToggleEpinOfLCD(void) {
            LCD_E = 1; // Give a pulse on E pin
            __delay_us(E_Delay); // so that LCD can latch the
            LCD_E = 0; // data from data bus
            __delay_us(E_Delay);
        }
    
        void WriteCommandToLCD(unsigned char Command) {
            LCD_RS = 0; // It is a command
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= (Command & 0xF0); // Write Upper nibble of data
            ToggleEpinOfLCD(); // Give pulse on E pin
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= ((Command << 4)&0xF0); // Write Lower nibble of data
            ToggleEpinOfLCD(); // Give pulse on E pin
        }
    
        void WriteDataToLCD(char LCDChar) {
            LCD_RS = 1; // It is data
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= (LCDChar & 0xF0); // Write Upper nibble of data
            ToggleEpinOfLCD(); // Give pulse on E pin
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= ((LCDChar << 4)&0xF0); // Write Lower nibble of data
            ToggleEpinOfLCD(); // Give pulse on E pin
        }
    
        void InitLCD(void) {
            // Firstly make all pins output
            LCD_E = 0; // E  = 0
            LCD_RS = 0; // RS = 0
            LCD_Data_Bus_D4 = 0; // Data bus = 0
            LCD_Data_Bus_D5 = 0; // Data bus = 0
            LCD_Data_Bus_D6 = 0; // Data bus = 0
            LCD_Data_Bus_D7 = 0; // Data bus = 0
            LCD_E_Dir = 0; // Make Output
            LCD_RS_Dir = 0; // Make Output
            LCD_RW_Dir = 0;
            LCD_RW = 0;
            LCD_Data_Bus_Dir_D4 = 0; // Make Output
            LCD_Data_Bus_Dir_D5 = 0; // Make Output
            LCD_Data_Bus_Dir_D6 = 0; // Make Output
            LCD_Data_Bus_Dir_D7 = 0; // Make Output
            ///////////////// Reset process from datasheet //////////////
            __delay_ms(40);
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= 0x30; // Write 0x3 value on data bus
            ToggleEpinOfLCD(); // Give pulse on E pin
            __delay_ms(6);
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= 0x30; // Write 0x3 value on data bus
            ToggleEpinOfLCD(); // Give pulse on E pin
            __delay_us(300);
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= 0x30; // Write 0x3 value on data bus
            ToggleEpinOfLCD(); // Give pulse on E pin
            __delay_ms(2);
            PORTB &= 0x0F; // Make Data pins zero
            PORTB |= 0x20; // Write 0x2 value on data bus
            ToggleEpinOfLCD(); // Give pulse on E pin
            __delay_ms(2);
            /////////////// Reset Process End ////////////////
            WriteCommandToLCD(0x28); //function set
            WriteCommandToLCD(0x0c); //display on,cursor off,blink off
            WriteCommandToLCD(0x01); //clear display
            WriteCommandToLCD(0x06); //entry mode, set increment
            WriteCommandToLCD(0x0e); //display on,cursor on,blink off
            WriteCommandToLCD(0x0f); //display on,cursor on,blink on
        }
    
        void WriteStringToLCD(const char *s) {
            while (*s) {
                WriteDataToLCD(*s++); // print first character on LCD
            }
        }
    
        void ClearLCDScreen(void) // Clear the Screen and return cursor to zero position
        {
            WriteCommandToLCD(0x01); // Clear the screen
            __delay_ms(2); // Delay for cursor to return at zero position
        }
    
        void lcd_set_cursor(uint8_t row, uint8_t col) {
            if (row == 1) {
                WriteCommandToLCD(0x80 | (col - 1));
            } else if (row == 2) {
                WriteCommandToLCD(0xC0 | (col - 1));
            }
        }
    
    #ifdef	__cplusplus
    }
    #endif
    
    #endif	/* LCD_16X2_H */
    
    
    /* 
     * File:   board.h
     * Author: abhay
     *
     * Created on July 14, 2023, 12:41 AM
     */
    
    #ifndef BOARD_H
    #define	BOARD_H
    
    #ifdef	__cplusplus
    extern "C" {
    #endif
    /**
     * LED 
     * color: RED
     * @param PORT: D
     * @param PIN: 1
     */
    #define LED_PORT_DIR TRISD
    #define LED_Port PORTD
    #define LED_PIN PORTDbits.RD1
        
    #define Relay_1a_DIR TRISD
    #define Relay_1a_Port PORTD
    #define Relay_1a_PIN PORTDbits.RD0
    
    #define Relay_1b_DIR TRISD    
    #define Relay_1b_Port PORTD
    #define Relay_1b_PIN PORTDbits.RD2
    
    #define Relay_2a_DIR TRISD
    #define Relay_2a_Port PORTD
    #define Relay_2a_PIN PORTDbits.RD4
    
    #define Relay_2b_DIR TRISD
    #define Relay_2b_Port PORTD
    #define Relay_2b_PIN PORTDbits.RD5
    
    #ifdef	__cplusplus
    }
    #endif
    
    #endif	/* BOARD_H */
    
    
  • 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!

  • ATmega328PB 16-bit Timer TC3 – Fast PWM Mode

    1 millisecond pulse width
    50 Hz Frequency
    1.5 millisecond pulse width
    50 Hz Frequency
    2 millisecond pulse width
    50 Hz Frequency

    I have used this FAST PWM mode to trigger two interrupt service routines.
    The timer compares register A sets the frequency.
    I have not enabled the output compare pins. since it was used by some other peripheral. So by doing this, I generate a PWM signal by issuing a command in the ISR.

    /*
    * main.c
    *
    * Created: 10 July 2023 10:47:23 PM
    *  Author: abhay
    */
    #define F_CPU 16000000
    
    #include <xc.h>
    #include <stdio.h>
    #include "util/delay.h"
    #include <avr/interrupt.h>
    #include "uart.h"
    #define LED_ON PORTB |= (1<<5)
    #define LED_OFF PORTB &= ~(1<<5)
    
    
    // Function to send a character via UART
    int UART_putchar(char c, FILE *stream) {
    	if (c == '\n')
    	UART_putchar('\r', stream);  // Add carriage return before newline
    	while (!(UCSR0A & (1 << UDRE0))); // Wait for the transmit buffer to be empty
    	UDR0 = c; // Transmit the character
    	return 0;
    }
    
    // Create a FILE structure to redirect the printf stream to UART
    FILE uart_output = FDEV_SETUP_STREAM(UART_putchar, NULL, _FDEV_SETUP_WRITE);
    
    ISR(TIMER3_COMPA_vect){
    	PORTD |= (1<<6);
    }
    ISR(TIMER3_COMPB_vect){
    	PORTD &= ~(1<<6);
    	
    }
    
    int main(void)
    {
    	
    
    	USART_Init();
    	// Redirect stdout stream to UART
    	stdout = &uart_output;
    	DDRB |= (1<<5);	// set Data direction to output for PB5
    	LED_OFF; // set output to high
    	DDRD |= (1 << 6);	//set PD6 as output
    	
    	
    	/*
    	F_CPU = 16000000
    	Prescaler = 64
    	Frequency = 50Hz
    	Period = 0.020 s
    	step time = 1/(F_CPU/Prescaler) = 0.000004 s
    	number of steps = 0.020/0.000004 = 5000
    	*/
    	TCNT3 = 0;		// Timer counter initial value = 0
    	// Output Compare A value = 5000 or 20 Milli second
    	OCR3A = 5000;
    	// Output Compare B value = 500 or 2 Milli second 
    	OCR3B = 500;	
    	// Fast PWM
    	TCCR1A |= (1 << WGM31)|(1 << WGM30);	
    	//  Prescaler: 64
    	TCCR3B |= (1 << WGM32)|(1<<WGM32)|(1 << CS31)|(1 << CS30);	
    	// Enable Timer Interrupt for Overflow, Compare match A and Compare Match B
    	TIMSK3 |= (1 << OCIE3B)|(1 << OCIE3A)|(1<<TOIE3);	 
    	
    	// Enable Global Interrupt
    	sei();	
    	
    	while(1)
    	{
    		OCR3B = 250;//5% 1ms
    		_delay_ms(500);
    		OCR3B = 375;//7.5% 1.5ms
    		_delay_ms(100);
    		OCR3B = 500;//10% 2ms
    		_delay_ms(500);
    
    	}
    	
    	
    }
  • ATmega328PB 16-bit Timer TC1 – CTC Mode

    Timers are essential components in microcontrollers that allow precise timing and synchronization for various applications. The ATmega328PB microcontroller offers several timer/counters, including Timer/Counter 1 (TC1), which is a 16-bit timer with advanced features. In this blog post, I will explore how to utilize Timer 1 in CTC (Clear Timer on Compare Match) mode on the ATmega328PB microcontroller.

    Hardware Setup

    Before we proceed with the code, ensure you have the necessary hardware setup. You will need an ATmega328PB microcontroller, a 16MHz crystal oscillator, and any additional components required for your specific application. Connect the crystal oscillator to the XTAL1 and XTAL2 pins of the microcontroller to provide a stable clock signal.

    UART Communication Initialization

    In this example, we will utilize UART communication for debugging or output purposes. Make sure you have already implemented the necessary UART functions or library. The UART initialization code should include setting the baud rate, enabling the transmitter and receiver, and configuring the data format (e.g., number of data bits, parity, and stop bits). We will also redirect the stdout stream to the UART using the stdio.h library, allowing us to use the printf function for UART output.

    Timer 1 Configuration in CTC Mode

    Let’s dive into the code and configure Timer 1 in CTC mode. Here’s an example code snippet:

    /*
    * main.c
    *
    * Created: 7/9/2023 12:47:23 AM
    *  Author: abhay
    */
    #define F_CPU 16000000
    
    #include <xc.h>
    #include <stdio.h>
    #include "util/delay.h"
    #include <avr/interrupt.h>
    #include "uart.h"
    
    // Function to send a character via UART
    int UART_putchar(char c, FILE *stream) {
    	if (c == '\n')
    	UART_putchar('\r', stream);  // Add carriage return before newline
    	while (!(UCSR0A & (1 << UDRE0))); // Wait for the transmit buffer to be empty
    	UDR0 = c; // Transmit the character
    	return 0;
    }
    
    // Create a FILE structure to redirect the printf stream to UART
    FILE uart_output = FDEV_SETUP_STREAM(UART_putchar, NULL, _FDEV_SETUP_WRITE);
    ISR(TIMER1_COMPA_vect){
    	printf("2. compare match A\n");
    }
    ISR(TIMER1_COMPB_vect){
    	printf("1. compare match B\n");
    }
    int main(void)
    {
    	USART_Init();
    	// Redirect stdout stream to UART
    	stdout = &uart_output;
    	DDRB |= (1<<5);	// set Data direction to output for PB5
    	PORTB |= (1<<5); // set output to high
    	/*
    	* Timer 1 
    	* Mode of operation : CTC
    	*		When Output Compare A register value equals the 
    	*		Timer Counter register (TCNT1) it resets the Timer-Counter-register value
    	*		and generates a interrupt. 
    	* Only OCR1A will reset the timer counter. 
    	* OCR1B can be used to generate a compare match between TCNT1 = 0 and OCR1A
    	*
    	*/
    	TCNT1 = 0;		// Timer counter initial value = 0
    	OCR1BH = 0x3D;	// Output Compare B value = 0x3d09 or 1 second
    	OCR1BL = 0x09;
    	OCR1AH = 0x7a;	// Output Compare A value = 0x7a12 or 2 second
    	OCR1AL = 0x12;
    	TCCR1B |= (1<<WGM02)|(1 << CS12)|(1 << CS10);	// CTC Prescaler: 1024
    	TIMSK1 |= (1 << OCIE1B)|(1 << OCIE1A)|(1<<TOIE1);
    	
    	sei();	// Enable Global Interrupt
    	while(1)
    	{
    
    		
    		printf("  This is Main:\n");
     		_delay_ms(2500);
    	
    		//TODO:: Please write your application code
    		
    	}
    }

    In this code snippet, we first initialize the UART communication and redirect the stdout stream to the UART output using the FDEV_SETUP_STREAM macro. The UART_putchar function is used to send a character via UART, ensuring that newline characters (\n) are preceded by a carriage return character (\r) for proper line endings.

    Next, we configure Timer/Counter 1 (TC1) for CTC mode and set the prescaler to 1024, which divides the clock frequency to generate a suitable timebase. The TCCR1A and TCCR1B registers are set accordingly.

    We then set the compare values (OCR1A and OCR1B) to determine the time intervals at which we want to generate interrupts. In this example, OCR1A is set for 2 second delay, and OCR1B is set for approximately 1 seconds delay.

    Finally, we enable the Timer/Counter TC1 compare match interrupts (OCIE1A and OCIE1B) using the TIMSK1 register, and we enable global interrupts with the sei() function.

    Interrupt Service Routines (ISRs)

    The code snippet defines two Interrupt Service Routines (ISRs): TIMER1_COMPA_vect and TIMER1_COMPB_vect. These ISRs will be executed when a compare match occurs for Output Compare A and Output Compare B, respectively. In this example, we use these ISRs to print messages to the UART output. You can modify these ISRs to perform any desired actions based on your specific application requirements.

    Putting It All Together

    Once you have set up the UART communication, configured Timer 1 in CTC mode, and defined the necessary ISRs, you can utilize the precise timing capabilities of Timer 1 in your main program loop. Use the printf function to output information via UART, and the compare match interrupts will handle the precise timing events.

    while (1) {
       printf("  This is Main:\n");
     		_delay_ms(2500);
        // Additional code and operations
        // ...
    }

    In the above example, the main program loop will execute continuously, printing “This is the main program loop” every 1 second using the printf function. The _delay_ms function provides a delay of 2500 milliseconds (2.5 second) between each iteration of the loop.

    Conclusion

    Utilizing Timer 1 in CTC mode on the ATmega328PB microcontroller provides precise timing capabilities for various applications. By configuring Timer 1, setting compare match values, and utilizing compare match interrupts, you can achieve accurate timing control in your embedded systems. When combined with UART communication, you can easily monitor and debug your code by printing relevant information via the UART interface.

    Remember to consult the ATmega328PB datasheet and relevant documentation for more details on Timer 1, CTC mode, and other timer features. Ensure that you have correctly configured your hardware setup, including the crystal oscillator and UART connection, to match the code requirements.

    Using Timer 1 in CTC mode

    with UART communication opens up a range of possibilities for precise timing and debugging capabilities in your projects. Experiment with different compare match values and integrate this functionality into your applications to enhance timing accuracy and control.

  • ATmega328PB 8-Bit Timer TC0 – Normal Mode

    The simplest mode of operation is the Normal mode (WGM0[2:0] = 0x0). In this mode, the counting direction is always up (incrementing), and no counter clear is performed. The counter simply overruns when it passes its maximum 8-bit value (TOP=0xFF) and then restarts from the bottom (0x00). In Normal mode operation, the Timer/Counter Overflow flag (TOV0) will be set in the same clock cycle in which the TCNT0 becomes zero. In this case, the TOV0 flag behaves like a ninth bit, except that it is only set, not cleared. However, combined with the timer overflow interrupt that automatically clears the TOV0 flag, the timer resolution can be increased by software. There are no special cases to consider in the Normal
    mode, a new counter value can be written any time.

    The output compare unit can be used to generate interrupts at some given time. Using the output compare to generate waveforms in Normal mode is not recommended since this will occupy too much of the CPU time.

    The counter will always count from 0 to 255 and then after 255 it will set the overflow bit and start the counting from 0.

    Without Interrupt

    We can use this to generate delays. But This is a type of blocking code.

    /*
     * main.c
     *
     * Created: 7/9/2023 12:47:23 AM
     *  Author: abhay
     */ 
    #define F_CPU 16000000UL
    #include <xc.h>
    
    #include <util/delay.h>
    void T0delay();
    int main(void)
    {
    	DDRB |= (1<<5);
    	PORTB |= (1<<5);
        while(1)
        {
    		PORTB |= (1<<PINB5);
    		T0delay();
    		PORTB &= ~(1<<PINB5);
                    T0delay();
    		//TODO:: Please write your application code 
    		
        }
    }
    void T0delay()
    {
    	/*
    	F_CPU/prescaler = clock for timer
    	1/clock for timer = timer step time
    	for 1 second the timer will take : 1/timer step time
    	
    	16000000/1024 = 15625 clock
    	1/15625 = 0.000064 = 64us	(counter step size)
    	64us x 256 = 0.016384 sec (0verflow time)
    	64us x 255 - tcnt +1 = 0.016384 sec (0verflow time)
    		64us x (255 - tcnt +1 )= x
    		tcnt = x / 64us ) -256
    		
    	for 1 sec Delay
    	=> 1/0.016384 sec = 61.03515625
    	Taking only integer 
    	so 61 overflows needs to occur for 0.999424 Seconds
    	1-0.999424 = 0.000576 seconds
    	0.000576/ (counter step size) = required steps 
    	0.000576/0.00064 = 9 steps
    	Note: this will be close to 1 second. but it will be longer due to overhead added by the instructions.
    	*/
    	for(int i = 0; i< 61;i++){
    	TCNT0 = 0;
    	TCCR0A = 0;
    	TCCR0B |= (1<<CS00)|(1<<CS02);	//prescaler 1024
    	while( (TIFR0 & 0x1) == 0);	// Wait for overflow flag
    	TCCR0B = 0;
    	TIFR0 = 0x1;
    	}
    	//9 steps timer code
    	TCNT0 = 255-9;
    	TCCR0A = 0;
    	TCCR0B |= (1<<CS00)|(1<<CS02);	//prescaler 1024
    	while( (TIFR0 & 0x1) == 0);	// Wait for overflow flag
    	TCCR0B = 0;
    	TIFR0 = 0x1;
    }

    Normal Mode with Interrupt

    #define F_CPU 16000000UL
    #include <xc.h>
    #include <avr/interrupt.h>
    ISR(TIMER0_OVF_vect){
    	PORTB ^= (1<<PINB5);   // Toggle the GPIO
    }
    int main(void)
    {
    	
    	DDRB |= (1<<5);	// set Data direction to output for PB5
    	PORTB |= (1<<5);    // set output to high
    	
    	TCNT0 = 0;
    	TCCR0A = 0;     // Normal Mode
    	TCCR0B |= (1<<CS00)|(1<<CS02);	//prescaler 1024
    	TIMSK0 |= (1 << TOIE0);	// Overflow Interrupt Enable Bit
    	
    	sei();	// Enable Global Interrupt
    	while(1)
    	{
    		//TODO:: Please write your application code
    		
    	}
    }

  • AVR timer overflow calculator











    seconds
    seconds

    Code for Calculations

    <form>
      <label for="F_CPU">Main Clock Frequency (F_CPU):</label>
      <input type="number" id="F_CPU" step="0.01" min="0" value="16000000">
      <hr />
    
      <label for="Tres">Timer Resolution(8bit or 16bit):</label>
      <input type="number" id="Tres" step="8" value="8" min="8" max="16">
      <hr />
      
      <label for="TCNT">Timer Counter Value:</label>
      <input type="number" id="TCNT" step="1" min="0" max="255" value="0">
      <hr />
    
    
      <label for="prescaler">Timer Clock Prescaler:</label><br>
      <input type="radio" id="prescaler1" name="prescaler" value="1" checked>
      <label for="prescaler1">1</label><br>
      <input type="radio" id="prescaler8" name="prescaler" value="8">
      <label for="prescaler8">8</label><br>
      <input type="radio" id="prescaler64" name="prescaler" value="64">
      <label for="prescaler64">64</label><br>
      <input type="radio" id="prescaler256" name="prescaler" value="256">
      <label for="prescaler256">256</label><br>
      <input type="radio" id="prescaler1024" name="prescaler" value="1024">
      <label for="prescaler1024">1024</label><br>
      <hr />
      <label for="stepperiod">Timer Step Period:</label>
      <input type="number" id="stepperiod"  readonly>seconds</br>
      <label for="ovfperiod">Overflow Period:</label>
      <input type="number" id="ovfperiod"  readonly>seconds
      <hr />
    
      <button type="button" onclick="calculateValue()">Calculate Value</button>
      <button type="button" onclick="clearFields()">Clear Values</button>
    </form>
    
    <script>
    function calculateValue() {
      // Retrieve input values
      var F_CPU = parseFloat(document.getElementById("F_CPU").value);
      var Tres = parseInt(document.getElementById("Tres").value);
      var TCNT = parseInt(document.getElementById("TCNT").value);
      var prescaler = parseFloat(document.querySelector('input[name="prescaler"]:checked').value);
    
      // Calculate Timer Clock and Step Period
      var Timer_Clock = F_CPU / prescaler;
      var Step_Period = 1 / Timer_Clock;
    
      // Calculate Overflow Period based on Timer Resolution
      var ovfperiod;
      if (Tres === 8) {
        ovfperiod = (255 - TCNT + 1) * Step_Period;
      } else if (Tres === 16) {
        ovfperiod = Math.pow(2, 16) * Step_Period;
      }
    
      // Update the Overflow Period input field
      document.getElementById("stepperiod").value = Step_Period;
      document.getElementById("ovfperiod").value = ovfperiod;
    }
    
    function clearFields() {
      document.getElementById("F_CPU").value = "";
      document.getElementById("Tres").value = "";
      document.getElementById("ovfperiod").value = "";
    }
    </script>
    
  • Raspberry Pi Model 3b+ Custom Case with Camera Mounting

    I made this enclosure using the MDF sheet.

    It has all the port openings that raspberry pi has.

    I made an attachment on the side to mount the camera.

  • German Vocabulary 2 – Decoding the Knorr Süßkartoffel Linsen Curry Packet

    I got this pack from a local shop. It was a recycled item used to store Dosa in it. When I found out it was in German. So I decided to translate a few words written on it. I wrote them on a page.

    Translated Words

  • How to use S109AFTG Microstep Driver with ATmega328PB Programmed using Microchip Studio

    When I opened the case. I found a PCB which is screwed to a big heatsink. I unscrewed the bolts and saw that there is S109AFTG. The IC is sandwiched between the PCB and the heatsink. A small aluminum block is also used for heat transfer between the IC and the heatsink.

    It has a different step size which can be selected by the DIP switches.
    The motor driver has a maximum of 1/32 step size.

    which means 1.8°/ 32 = 0.05625°

    360°/ 0.05625° = 6400 steps

    So a full rotation will be in 6400 steps.

    You will need a power source such as a Switched Mode Power Supply which can supply at least 2 Amps.

    If your application needs more torque you will need a power source that can provide a high current without dropping the voltage.

    Or you can use the battery for a short duration.

    Schematic Diagram

    Code

    /*
    * main.c
    *
    * Created: 7/4/2023 5:51:21 PM
    *  Author: abhay
    */
    #define F_CPU 16000000
    #include <xc.h>
    #include <util/delay.h>
    int PUL=PIND6; //define Pulse pin
    int DIR=PINB1; //define Direction pin
    int ENA=PIND2; //define Enable Pin
    #define DirLow PORTB &= ~(1<<DIR)
    #define DirHigh PORTB |= (1<<DIR)
    #define PulLow PORTD &= ~(1<<PUL)
    #define PulHigh PORTD |= (1<<PUL)
    #define EnaLow PORTD &= ~(1<<ENA)
    #define EnaHigh PORTD |= (1<<ENA)
    #define delayus50 _delay_us(50)
    int main(void)
    {
    	DDRB |= (1<<DIR);
    	DDRD |= (1<<PUL)|(1<<ENA);
    	while(1)
    	{
    		//TODO:: Please write your application code
    		for (int i=0; i<6400; i++)    //Forward 6400 steps
    		{
    			DirLow;
    			EnaHigh;
    			PulHigh;
    			delayus50;
    			PulLow;
    			delayus50;
    		}
    		_delay_ms(5000);
    		for (int i=0; i<6400; i++)   //Backward 6400 steps
    		{
    			DirHigh;
    			EnaHigh;
    			PulHigh;
    			delayus50;
    			PulLow;
    			delayus50;
    		}
    		_delay_ms(2000);
    	}
    }
    
  • Derivative Control Demo in Control Systems Engineering Using Slider in Tkinter

    Control systems engineering plays a crucial role in various industries, enabling precise and efficient control of processes and systems. One fundamental concept in control systems is derivative control, which helps improve the system’s response to changes and disturbances. In this blog post, we’ll explore a simple demonstration of derivative control using a slider in Tkinter, a popular Python GUI toolkit.

    Understanding Derivative Control

    Derivative control is a control strategy that utilizes the derivative of the error signal to adjust the control output. By calculating the rate of change of the error, derivative control can anticipate the system’s response to changes and take corrective actions to minimize the error. It provides a damping effect and improves the system’s stability and responsiveness.

    The derivative control algorithm consists of three main components:

    1. Derivative Control Function: The derivative control function calculates the derivative term based on the current error, previous error, and a time interval. The derivative term is obtained by multiplying the derivative gain (Kd) with the difference in error divided by the time interval.
    2. Main Loop: The main loop of the control system continuously monitors the process variable and applies derivative control to update the control output. It calculates the error by subtracting the desired setpoint from the process variable. The derivative control function is then invoked to compute the derivative term. The control output is actuated, and the previous error is updated.
    3. Slider and GUI: To interact with the control system, we’ll create a graphical user interface (GUI) using Tkinter. A slider widget allows us to adjust the feedback stimulus, representing the process variable. Labels display the feedback stimulus value and the computed derivative term in real-time. Additionally, a hyperlink is provided to visit a website for further information.

    Implementation with Tkinter

    Let’s delve into the implementation of the derivative control demo using Tkinter. Here’s the code:

    import tkinter as tk
    import time
    import webbrowser
    
    # Derivative control function
    def derivative_control(error, prev_error, dt):
        # Derivative gain
        Kd = 0.2
        derivative_term = Kd * (error - prev_error) / dt
        return derivative_term
    
    # Main loop
    def main_loop():
        setpoint = 50  # Desired setpoint
        process_variable = 0  # Initial process variable
        prev_error = 0  # Previous error
        dt = 0.1 * 9  # Time interval for derivative control
        while True:
            # Read process variable from the slider
            process_variable = slider.get()
    
            # Calculate the error
            error = setpoint - process_variable
    
            # Apply derivative control
            derivative_term = derivative_control(error, prev_error, dt)
    
            # Actuate the control signal (in this example, update the label)
            control_label.configure(text="Derivative Term: {:.2f}".format(derivative_term))
    
            # Update the previous error
            prev_error = error
    
            time.sleep(dt)  # Sleep for the time interval
    
    
    # Callback function for the slider
    def slider_callback(value):
        feedback_label.configure(text="Feedback Stimulus: {:.2f}".format(float(value)))
    
    # Open exasub.com in a web browser
    def open_link(event):
        webbrowser.open("http://www.exasub.com")
    
    # Create the main Tkinter window
    window = tk.Tk()
    window.title("Derivative Control Demo")
    
    # Create the slider for adjusting the feedback stimulus
    slider = tk.Scale(window, from_=0, to=100, orient=tk.HORIZONTAL, length=300, command=slider_callback)
    slider.pack()
    
    # Create a label to display the feedback stimulus value
    feedback
    
    _label = tk.Label(window, text="Feedback Stimulus: {:.2f}".format(slider.get()))
    feedback_label.pack()
    
    # Create a label to display the derivative term value
    control_label = tk.Label(window, text="Derivative Term: ")
    control_label.pack()
    
    # Add a link to exasub.com
    link = tk.Label(window, text="Visit exasub.com", fg="blue", cursor="hand2", font=("Arial", 14))
    link.pack()
    link.bind("<Button-1>", open_link)
    
    # Start the main loop in a separate thread
    import threading
    main_loop_thread = threading.Thread(target=main_loop)
    main_loop_thread.start()
    
    # Start the Tkinter event loop
    window.mainloop()

    Exploring the Code

    Let’s break down the code to understand how the derivative control demo works:

    1. We begin by importing the necessary modules: tkinter for GUI, time for time-related operations, and webbrowser for opening web links.
    2. The derivative_control function calculates the derivative control term based on the error, previous error, and a specified time interval. It multiplies the derivative gain (Kd) with the difference in error and divides it by the time interval. Adjusting the value of Kd can impact the system’s response.
    3. The main_loop function serves as the central control loop of the demo. It sets the desired setpoint and initializes variables for the process variable and previous error. The time interval (dt) determines the frequency of derivative control updates. Within the loop, the process variable is read from the slider, the error is calculated, derivative control is applied, and the control output is displayed in the GUI label. The previous error is updated, and the loop pauses for the specified time interval.
    4. The slider_callback function is triggered whenever the slider value changes. It updates the feedback label to display the current value of the feedback stimulus, representing the process variable.
    5. The open_link function opens the “exasub.com” website in a web browser when the “Visit exasub.com” link is clicked. This functionality provides an opportunity to learn more about derivative control or related topics.
    6. The main Tkinter window is created, titled “Derivative Control Demo”.
    7. A slider widget is added to the window, allowing the user to adjust the feedback stimulus. It spans from 0 to 100, is oriented horizontally, and has a length of 300 pixels. The slider_callback function is bound to this slider to update the feedback label.
    8. A label is created to display the current value of the feedback stimulus.
    9. Another label is created to display the computed derivative term. Initially, it displays the placeholder text “Derivative Term: “.
    10. A hyperlink labeled “Visit exasub.com” is added to the window. It appears in blue and changes the cursor to a hand when hovered over. The open_link function is bound to this label to open the specified website.
    11. The main loop is started in a separate thread using the threading module. This allows the control loop to run concurrently with the Tkinter event loop and ensures the GUI remains responsive.
    12. Finally, the Tkinter event loop is started using the mainloop() method of the window object. It listens for user interactions and updates the GUI accordingly.

    Running the Derivative Control Demo

    To run the derivative control demo, you’ll need to have Python and the Tkinter library installed. Save the code in a Python file (e.g., derivative_control_demo.py) and execute it. A window will appear with a slider and two labels.

    Adjusting the slider will update the feedback stimulus

    value label in real-time. As you adjust the slider, the derivative control algorithm will calculate the derivative term, which will be displayed in the “Derivative Term” label. The calculated derivative term reflects the system’s response to changes in the feedback stimulus.

    Additionally, clicking the “Visit exasub.com” link will open a web browser and direct you to the “exasub.com” website, providing an opportunity to explore further resources on derivative control or related topics.

    Conclusion

    In this blog post, we’ve explored a derivative control demo implemented using Tkinter in Python. By adjusting a slider representing the feedback stimulus, you can observe the real-time calculation of the derivative term. This demonstration showcases the principles of derivative control and its role in control systems engineering.

    Understanding derivative control and its application can be valuable in various fields, such as robotics, industrial automation, and process control. By manipulating the derivative gain and other control parameters, engineers can fine-tune the system’s response to optimize performance, stability, and efficiency.

    By experimenting with this derivative control demo and further exploring control systems engineering, you can deepen your understanding of control strategies and their impact on system behavior.