Control LEDs with UART

Thread Starter

Kittu20

Joined Oct 12, 2022
434
Hello Everyone

I am planning to develop a program that controls three LEDs using a PIC microcontroller and UART communication. The program should respond to specific commands received via UART and adjust the flashing speed of the LEDs accordingly.

Here's how I want it to work:

  1. When 'Command 1' is received, the program should flash the LEDs every 2 seconds.
  2. When 'Command 2' is received, the program should flash the LEDs every 1 second.
  3. When 'Command 3' is received, the program should flash the LEDs every half a second.

The key requirement here is that the speed of LED flashing should change only when a new command is received. Once a speed is set, it should keep flashing at that speed until a new command alters the speed.

Additionally, I have already written code that can receive commands from the serial port and send command back to serial port to verify two-way communication via UART.

C:
/*
* File:   main.c
* Author: kittu
* Created on 14 September, 2023, 8:35 AM
*
*/

#include <xc.h>
#include <stdio.h>
#include"config.h"


void initializeOscillator(void);
void initializeEUSART1(void);

/*****
*   NOTE:    This function configures the oscillator settings for the microcontroller.
*            It sets the IRCF bits to 110 for HF-INTOSC/2 (8 MHz, default).
*****/
void initializeOscillator(void)
{
    OSCCON  = 0x60;     // Set IRCF bits to 110 for HF-INTOSC/2 (8 MHz, default)
    OSCCON2 = 0x00; 
    OSCTUNE = 0x00; 
    REFOCON = 0x00; 
}


void initializePort() {
    // Set LATx registers to initial states (all low)
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
  

    // Set TRISx registers to configure pins as outputs (all output)
    TRISA = 0x00;   
    TRISB = 0x00;     // RB0 is RED, RB1 is YELLOW and RB2 is GREEN LED
    TRISC = 0x80;     // RC7 is TX and RC6 TX
    TRISD = 0x00;   
    TRISE = 0x00;   

    // Additional configuration for unused peripherals
    ANCON0 = 0x00;     // Set all analog pins to digital mode
    ANCON1 = 0x00;     // Set all analog pins to digital mode
    CM1CON = 0x00;     // Turn off Comparator 1
    CM2CON = 0x00;     // Turn off Comparator 2
    ADCON0 = 0x00;     // Disable A/D conversion
    ADCON1 = 0x00;     // Disable A/D conversion
    ADCON2 = 0x00;     // Disable A/D conversion
}

/*****
*
*   NOTE:    This function configures the EUSART1 settings for communication.
*
*****/
void initializeUART1() {
  
    // Configure BAUDCON1 register
    // Bit 7: ABDOVF = 0 (Auto-Baud Acquisition Rollover Status bit - Cleared in software)
    // Bit 6: RCIDL = 0 (Receive Operation Idle Status bit - Receive operation is active)
    // Bit 5: RXDTP = 0 (Received Data Polarity Select bit - Receive data is not inverted)
    // Bit 4: TXCKP = 0 (Clock and Data Polarity Select bit - Idle state for transmit is high level)
    // Bit 3: BRG16 = 1 (16-Bit Baud Rate Register Enable bit - 16-bit Baud Rate Generator)
    // Bit 2: Unimplemented: Read as ?0? (Reserved, read as '0')
    // Bit 1: WUE = 0 (Wake-up Enable bit - RX pin is not monitored or rising edge is detected)
    // Bit 0: ABDEN = 0 (Auto-Baud Detect Enable bit - Baud rate measurement is disabled or completed)

    BAUDCON1 = 0b00001000; // Set BRG16 bit to 1 (16-Bit Baud Rate Register Enable)
  
    // Calculate and set SPBRG1 for a baud rate of approximately 9600 at 8 MHz
    SPBRG1 = 207;
    SPBRGH1 = 0;
  

    // TXSTA1 register configuration
    // Bit 7: CSRC = 0 (Clock Source Select bit - Don't care in Asynchronous mode)
    // Bit 6: TX9 = 0 (8-bit transmission)
    // Bit 5: TXEN = 1 (Transmit is enabled)
    // Bit 4: SYNC = 0 (Asynchronous mode)
    // Bit 3: SENDB = 0
    // Bit 2: BRGH = 1 (High-speed baud rate)
    // Bit 1: TRMT = 1 (TSR is empty initially)

    TXSTA1 = 0b00100100; // Configure TXSTA1 register


    // RCSTA1 register configuration
    // Bit 7: SPEN = 1 (Serial Port Enable bit - Serial port is enabled)
    // Bit 6: RX9 = 0 (8-bit reception)
    // Bit 5: SREN = 0 (Single Receive Enable bit - Don't care in Asynchronous mode)
    // Bit 4: CREN = 1 (Continuous Receive Enable bit - Enables receiver)
    // Bit 3: ADDEN = 0 (Address Detect Enable bit - Don't care in Asynchronous mode)
    // Bit 2: FERR = 0 (Framing Error bit - No framing error)
    // Bit 1: OERR = 0 (Overrun Error bit - No overrun error)

     RCSTA1 = 0b10010000; // Configure RCSTA1 register

}


void hardwareInitialize() {
  
    // Initialize the oscillator
    initializeOscillator();
  
    // Initialize Port Pins
    initializePort();
  
    // Initialize Uart1 
    initializeUART1();
  
}

/*****
*
*   NOTE:    This function waits until the transmitter buffer is empty (TXIF is set)
*            and then sends the character by placing it in the TXREG1 register.
*
*****/
void UARTWrite(char Character)
{
    // Wait until the transmitter buffer is empty (TXIF is set)
    while (TXIF == 0);

    // Send the character by placing it in the TXREG1 register
    TXREG1 = Character;
}


/*****
*
*   NOTE:    This function waits until the receiver buffer is full (RCIF is set)
*            and then reads the received character from the RCREG1 register.
*
*****/
char UARTRead(void)
{
    // Wait until the receiver buffer is full (RCIF is set)
    while (RCIF == 0);

    // Read the received character from the RCREG1 register
    return RCREG1;
}

/*****
*
*  main - Main program function
*
*****/
void main(void)
{
    // Initialize hardware
    hardwareInitialize();

    // Delay for 5 seconds
    __delay_ms(5000);

    while (1)
    {
        // Define a character to send
        char Character = 'U';
        char ReceivedChar;
      
        ReceivedChar = UARTRead();         // Read a command from UART
    
        UARTWrite(ReceivedChar);           // Send the command back

        // Delay for 5 seconds before the next transmission
        __delay_ms(5000);
    }
}
I've attached a screenshot of an experiment where I typed the number '1233' to showcase this communication

1694686893485.png


As part of my plan to implement the LED control functionality, I am also planning to generate a 1ms timer to count time intervals for LED flashing. How can I integrate this 1ms timer into my existing code to achieve the desired LED flashing behavior?

Are there any specific techniques I should consider for efficient UART communication and LED control on my microcontroller?"
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
Can the UART send more than one command within the 1ms period?
At a baud rate of 9600 bps, it takes approximately 104.17 microseconds to transmit a single bit. To send a command (e.g.single character ), we need to transmit 8 data bits, 1 start bit, and 1 stop bit ( no parity bit). So, the total time to send one character is approximately 10 times the time it takes to transmit one bit, which is about 1.0417 milliseconds (ms).

So we need to wait at least 1.0417 milliseconds (ms) between sending consecutive commands over UART at a baud rate of 9600

humans typically take more time to type on a terminal.
 

Ian0

Joined Aug 7, 2020
8,944
That’s good, because you can set up the entire program to run on a 1ms interrupt cycle, in the knowledge that you will not have lost any data from the UART. You just need to poll the UART every time the timer interrupt service routine runs.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
That’s good, because you can set up the entire program to run on a 1ms interrupt cycle, in the knowledge that you will not have lost any data from the UART. You just need to poll the UART every time the timer interrupt service routine runs.
I've integrated a timer code here. This timer generates a 1ms interrupt, , I can now count time intervals of half a second, 1 second, and 2 seconds.

I'm currently in the process of developing the LED flashing logic for each of the specific commands (Command 1, Command 2, and Command 3) based on the timer intervals.

C:
/*
* File:   main.c
* Author: kittu
* Created on 14 September, 2023, 8:35 AM
*
*/

#include <xc.h>
#include <stdio.h>
#include"config.h"

__bit timeFlag = 0;
//volatile int timeFlag = 0;


#define RED_LED LATBbits.LATB0
#define YELLOW_LED LATBbits.LATB1
#define RED_GREEN LATBbits.LATB2


// Calculate Reload_Value for 1ms with a 8 MHz oscillator and prescaler 8
#define Reload_Value 249 // (8,000,000 XTAL / 4 / 8 prescale) = 249

void initializeOscillator(void);
void initializeEUSART1(void);

// This function configures the oscillator settings for the microcontroller.
void initializeOscillator(void)
{
    OSCCON  = 0x60;     // Set IRCF bits to 110 for HF-INTOSC/2 (8 MHz, default)
    OSCCON2 = 0x00;  
    OSCTUNE = 0x00;  
    REFOCON = 0x00;  
}


void initializePort() {
    // Set LATx registers to initial states (all low)
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
   

    // Set TRISx registers to configure pins as outputs (all output)
    TRISA = 0x00;    
    TRISB = 0x00;     // RB0 is RED, RB1 is YELLOW and RB2 is GREEN LED
    TRISC = 0x80;     // RC7 is TX and RC6 TX
    TRISD = 0x00;    
    TRISE = 0x00;    

    // Additional configuration for unused peripherals
    ANCON0 = 0x00;     // Set all analog pins to digital mode
    ANCON1 = 0x00;     // Set all analog pins to digital mode
    CM1CON = 0x00;     // Turn off Comparator 1
    CM2CON = 0x00;     // Turn off Comparator 2
    ADCON0 = 0x00;     // Disable A/D conversion
    ADCON1 = 0x00;     // Disable A/D conversion
    ADCON2 = 0x00;     // Disable A/D conversion
}


//This function configures the EUSART1 settings for communication.

void initializeUART1() {
   
    // Configure BAUDCON1 register
    // Bit 7: ABDOVF = 0 (Auto-Baud Acquisition Rollover Status bit - Cleared in software)
    // Bit 6: RCIDL = 0 (Receive Operation Idle Status bit - Receive operation is active)
    // Bit 5: RXDTP = 0 (Received Data Polarity Select bit - Receive data is not inverted)
    // Bit 4: TXCKP = 0 (Clock and Data Polarity Select bit - Idle state for transmit is high level)
    // Bit 3: BRG16 = 1 (16-Bit Baud Rate Register Enable bit - 16-bit Baud Rate Generator)
    // Bit 2: Unimplemented: Read as ?0? (Reserved, read as '0')
    // Bit 1: WUE = 0 (Wake-up Enable bit - RX pin is not monitored or rising edge is detected)
    // Bit 0: ABDEN = 0 (Auto-Baud Detect Enable bit - Baud rate measurement is disabled or completed)
 
    BAUDCON1 = 0b00001000; // Set BRG16 bit to 1 (16-Bit Baud Rate Register Enable)
   
    // Calculate and set SPBRG1 for a baud rate of approximately 9600 at 8 MHz
    SPBRG1 = 207;
    SPBRGH1 = 0;
   

    // TXSTA1 register configuration
    // Bit 7: CSRC = 0 (Clock Source Select bit - Don't care in Asynchronous mode)
    // Bit 6: TX9 = 0 (8-bit transmission)
    // Bit 5: TXEN = 1 (Transmit is enabled)
    // Bit 4: SYNC = 0 (Asynchronous mode)
    // Bit 3: SENDB = 0
    // Bit 2: BRGH = 1 (High-speed baud rate)
    // Bit 1: TRMT = 1 (TSR is empty initially)

    TXSTA1 = 0b00100100; // Configure TXSTA1 register
 

    // RCSTA1 register configuration
    // Bit 7: SPEN = 1 (Serial Port Enable bit - Serial port is enabled)
    // Bit 6: RX9 = 0 (8-bit reception)
    // Bit 5: SREN = 0 (Single Receive Enable bit - Don't care in Asynchronous mode)
    // Bit 4: CREN = 1 (Continuous Receive Enable bit - Enables receiver)
    // Bit 3: ADDEN = 0 (Address Detect Enable bit - Don't care in Asynchronous mode)
    // Bit 2: FERR = 0 (Framing Error bit - No framing error)
    // Bit 1: OERR = 0 (Overrun Error bit - No overrun error)

     RCSTA1 = 0b10010000; // Configure RCSTA1 register

}


void initializeTimer1() {
    // Configure Timer1 in 8-bit mode with a prescaler of 8
     T1CON = 0b00110111; //

     // Configure CCP module
     CCP1CON = 0b00001000; // 0x08

     // Load the high and low bytes of Reload_Value into CCPR1
     CCPR1 = Reload_Value;
     // CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
     // CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value

     // Clear Timer1 registers
     TMR1H = 0;
     TMR1L = 0;

    // Clear CCP1 interrupt flag
     CCP1IF = 0;

     // Start Timer1 and CCP module
     TMR1ON = 1;
     CCP1IE = 1; // Enable CCP1 interrupt
}

void hardwareInitialize() {
   
    // Initialize the oscillator
    initializeOscillator();
   
    // Initialize Port Pins
    initializePort();
   
    // Initialize Uart1  
    initializeUART1();
   
    // Initialize Timer1
    initializeTimer1();

   // Initialize INTCON register
    INTCON = 0; // Clear INTCON register

    // Enable global and peripheral interrupts
    INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
    INTCONbits.GIE = 1; // Global Interrupt Enable
   
}

/*****
*   NOTE:    This function waits until the transmitter buffer is empty (TXIF is set)
*            and then sends the character by placing it in the TXREG1 register.
*****/
void UARTWrite(char Character)
{
    // Wait until the transmitter buffer is empty (TXIF is set)
    while (TXIF == 0);

    // Send the character by placing it in the TXREG1 register
    TXREG1 = Character;
}


/*****
*   NOTE:    This function waits until the receiver buffer is full (RCIF is set)
*            and then reads the received character from the RCREG1 register.
*****/
char UARTRead(void)
{
    // Wait until the receiver buffer is full (RCIF is set)
    while (RCIF == 0);

    // Read the received character from the RCREG1 register
    return RCREG1;
}


//  main - Main program function

void main(void){
    // Initialize hardware
    hardwareInitialize();

    // Delay for 5 seconds
    __delay_ms(5000);

    while (1)
    {
        // Define a character to send
        char ReceivedChar;
       
        ReceivedChar = UARTRead();         // Read a command from UART
       
        if (ReceivedChar == '1') {
            // Command 1 received
            // Flash LEDs every 2 seconds
         
           }
       
        if (ReceivedChar == '2') {
        // Command 2 received
        // Flash LEDs every 1 millisecond
       
        }
       
        if (ReceivedChar == '3') {
        // Command 3 received
        // Flash a  LEDs every half a second
     
    }
     
     
    }
}

void __interrupt() ISR() {
    if (CCP1IF){
     // Clear the CCP1 interrupt flag
        CCP1IF = 0;
        // advance COMPARE value
         CCPR1 += Reload_Value;

        timeFlag = 1; // Notify main routine
}
}
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
I can either count time in the main function or receive commands, I've encountered a challenge in my main routine. I'd greatly appreciate it if you @JohnInTX could take a look at my code and provide some guidance. I'm specifically struggling with the logic to flash LEDs based on the received commands.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
You can do both if your main routine is 1ms.
On each 1ms interrupt increment the timer, then poll the UART.
I'm specifically struggling with the logic to flash LEDs based on the received commands. I've posted my code here, but please note that it's not complete, and program will not compile . I'd like to share my approach and ask for guidance on how to improve it and address the issues. . Here's my code, and I'd appreciate any advice or suggestions for improvement.

C:
/*
 * File:   main.c
 * Author: kittu
 * Created on 14 September, 2023, 8:35 AM
 *
 */

#include <xc.h>
#include <stdio.h>
#include"config.h"

__bit timeFlag = 0;
//volatile int timeFlag = 0;

unsigned long timerCounter = 0;
unsigned long previousCounterValue1 = 0;
unsigned long previousCounterValue2 = 0;
unsigned long previousCounterValue3 = 0;


#define RED_LED LATBbits.LATB0
#define YELLOW_LED LATBbits.LATB1
#define RED_GREEN LATBbits.LATB2


// Calculate Reload_Value for 1ms with a 8 MHz oscillator and prescaler 8
#define Reload_Value 249 // (8,000,000 XTAL / 4 / 8 prescale) = 249

void initializeOscillator(void);
void initializeEUSART1(void);

// This function configures the oscillator settings for the microcontroller.
void initializeOscillator(void)
{
    OSCCON  = 0x60;     // Set IRCF bits to 110 for HF-INTOSC/2 (8 MHz, default)
    OSCCON2 = 0x00;   
    OSCTUNE = 0x00;   
    REFOCON = 0x00;   
}


void initializePort() {
    // Set LATx registers to initial states (all low)
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
    

    // Set TRISx registers to configure pins as outputs (all output)
    TRISA = 0x00;     
    TRISB = 0x00;     // RB0 is RED, RB1 is YELLOW and RB2 is GREEN LED
    TRISC = 0x80;     // RC7 is TX and RC6 TX
    TRISD = 0x00;     
    TRISE = 0x00;     

    // Additional configuration for unused peripherals
    ANCON0 = 0x00;     // Set all analog pins to digital mode
    ANCON1 = 0x00;     // Set all analog pins to digital mode
    CM1CON = 0x00;     // Turn off Comparator 1
    CM2CON = 0x00;     // Turn off Comparator 2
    ADCON0 = 0x00;     // Disable A/D conversion
    ADCON1 = 0x00;     // Disable A/D conversion
    ADCON2 = 0x00;     // Disable A/D conversion
}


//This function configures the EUSART1 settings for communication.

void initializeUART1() {
    
    // Configure BAUDCON1 register
    // Bit 7: ABDOVF = 0 (Auto-Baud Acquisition Rollover Status bit - Cleared in software)
    // Bit 6: RCIDL = 0 (Receive Operation Idle Status bit - Receive operation is active)
    // Bit 5: RXDTP = 0 (Received Data Polarity Select bit - Receive data is not inverted)
    // Bit 4: TXCKP = 0 (Clock and Data Polarity Select bit - Idle state for transmit is high level)
    // Bit 3: BRG16 = 1 (16-Bit Baud Rate Register Enable bit - 16-bit Baud Rate Generator)
    // Bit 2: Unimplemented: Read as ?0? (Reserved, read as '0')
    // Bit 1: WUE = 0 (Wake-up Enable bit - RX pin is not monitored or rising edge is detected)
    // Bit 0: ABDEN = 0 (Auto-Baud Detect Enable bit - Baud rate measurement is disabled or completed)
  
    BAUDCON1 = 0b00001000; // Set BRG16 bit to 1 (16-Bit Baud Rate Register Enable)
    
    // Calculate and set SPBRG1 for a baud rate of approximately 9600 at 8 MHz
    SPBRG1 = 207;
    SPBRGH1 = 0;
    

    // TXSTA1 register configuration
    // Bit 7: CSRC = 0 (Clock Source Select bit - Don't care in Asynchronous mode)
    // Bit 6: TX9 = 0 (8-bit transmission)
    // Bit 5: TXEN = 1 (Transmit is enabled)
    // Bit 4: SYNC = 0 (Asynchronous mode)
    // Bit 3: SENDB = 0
    // Bit 2: BRGH = 1 (High-speed baud rate)
    // Bit 1: TRMT = 1 (TSR is empty initially)

    TXSTA1 = 0b00100100; // Configure TXSTA1 register 
  

    // RCSTA1 register configuration
    // Bit 7: SPEN = 1 (Serial Port Enable bit - Serial port is enabled)
    // Bit 6: RX9 = 0 (8-bit reception)
    // Bit 5: SREN = 0 (Single Receive Enable bit - Don't care in Asynchronous mode)
    // Bit 4: CREN = 1 (Continuous Receive Enable bit - Enables receiver)
    // Bit 3: ADDEN = 0 (Address Detect Enable bit - Don't care in Asynchronous mode)
    // Bit 2: FERR = 0 (Framing Error bit - No framing error)
    // Bit 1: OERR = 0 (Overrun Error bit - No overrun error)

     RCSTA1 = 0b10010000; // Configure RCSTA1 register

}


void initializeTimer1() {
    // Configure Timer1 in 8-bit mode with a prescaler of 8
     T1CON = 0b00110111; // 

     // Configure CCP module
     CCP1CON = 0b00001000; // 0x08

     // Load the high and low bytes of Reload_Value into CCPR1
     CCPR1 = Reload_Value;
     // CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
     // CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value

     // Clear Timer1 registers
     TMR1H = 0;
     TMR1L = 0;

    // Clear CCP1 interrupt flag
     CCP1IF = 0;

     // Start Timer1 and CCP module
     TMR1ON = 1;
     CCP1IE = 1; // Enable CCP1 interrupt
}

void hardwareInitialize() {
    
    // Initialize the oscillator
    initializeOscillator();
    
    // Initialize Port Pins
    initializePort();
    
    // Initialize Uart1   
    initializeUART1();
    
    // Initialize Timer1
    initializeTimer1();

   // Initialize INTCON register
    INTCON = 0; // Clear INTCON register

    // Enable global and peripheral interrupts
    INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
    INTCONbits.GIE = 1; // Global Interrupt Enable
    
}

/*****
*   NOTE:    This function waits until the transmitter buffer is empty (TXIF is set)
*            and then sends the character by placing it in the TXREG1 register.
*****/
void UARTWrite(char Character)
{
    // Wait until the transmitter buffer is empty (TXIF is set)
    while (TXIF == 0);

    // Send the character by placing it in the TXREG1 register
    TXREG1 = Character;
}


/*****
*   NOTE:    This function waits until the receiver buffer is full (RCIF is set)
*            and then reads the received character from the RCREG1 register.
*****/
char UARTRead(void)
{
    // Wait until the receiver buffer is full (RCIF is set)
    while (RCIF == 0);

    // Read the received character from the RCREG1 register
    return RCREG1;
}





void main(void){
    // Initialize hardware
    hardwareInitialize();

    // Delay for 5 seconds
    __delay_ms(5000);

    while (1)
    {
        // Check if the timeFlag is set (indicating a timer interrupt)
        if (timeFlag) {
            // Increment timerCounter
            timerCounter++;
            // Reset the timeFlag
            timeFlag = 0;
        }

        // Define a character to send
        char ReceivedChar;
        
        ReceivedChar = UARTRead(); // Read a command from UART
        
        if (ReceivedChar == '1') {
            // Command 1 received
            // Flash LEDs every 2 seconds
            while (ReceivedChar == '1') {
                // LED flashing logic for Command 1
                // Continue to check the elapsed time
                //if ( check the elapsed time) { {
                // 2000 ms (2 seconds) have passed, toggle LEDs and reset previousCounterValue1
                // Toggle LEDs here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                // Check for a command change
                ReceivedChar = UARTRead();
            }
        }
        
        if (ReceivedChar == '2') {
            // Command 2 received
            // Flash LEDs every 1 millisecond
            while (ReceivedChar == '2') {
                //LED flashing logic for Command 2
                // Continue to check the elapsed time
                 //if ( check the elapsed time) { {
                    // 1000 ms has passed, toggle LEDs and reset previousCounterValue2
                  
                    // Toggle LEDs here

                }
                
                // Check for a command change
                ReceivedChar = UARTRead();
            }
        }
        
        if (ReceivedChar == '3') {
            // Command 3 received
            // Flash a LEDs every half a second
            while (ReceivedChar == '3') {
                // LED flashing logic for Command 3
                // Continue to check the elapsed time
                //if ( check the elapsed time) {
                    // 500 ms (half a second) has passed, toggle the LED and reset previousCounterValue3
                    
                    // Toggle the LED here
              
                }
                
                // Check for a command change
                ReceivedChar = UARTRead();
            }
        }
    }
}



void __interrupt() ISR() {
    if (CCP1IF){
     // Clear the CCP1 interrupt flag
        CCP1IF = 0;
        // advance COMPARE value
         CCPR1 += Reload_Value;

        timeFlag = 1; // Notify main routine
 }
}
 

Ian0

Joined Aug 7, 2020
8,944
Command number looks up “period” in a lookup table. Period is 1000 for command 1, 500 for command 2, 250 for command 3.
if counter==period, switch LED on.
if counter>=2*period, switch LED off, clear counter.
greater than or equal takes care of situations where the period gets shortened by a new command.

or you could simply go with:
if counter>=period LED=!LED, clear counter.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
Command number looks up “period” in a lookup table. Period is 1000 for command 1, 500 for command 2, 250 for command 3.
if counter==period, switch LED on.
if counter>=2*period, switch LED off, clear counter.
greater than or equal takes care of situations where the period gets shortened by a new command.

or you could simply go with:
if counter>=period LED=!LED, clear counter.
Thank you very much, Ian0.

I've been testing my code, and I've encountered a behavior that I need help with. When I enter command '1', all LEDs turn off. However, when I enter command '2', the LEDs remain high and don't turn off. I'd like to understand why this is happening and how I can modify my code to achieve the desired behavior of turning off the LEDs when command '2' is received.

Here's a my code for reference

C:
/*
 * File:   main.c
 * Author: kittu
 * Created on 14 September, 2023, 8:35 AM
 *
 */

#include <xc.h>
#include <stdio.h>
#include"config.h"

__bit timeFlag = 0;
//volatile int timeFlag = 0;

unsigned long timerCounter = 0;
unsigned long previousCounterValue1 = 0;
unsigned long previousCounterValue2 = 0;
unsigned long previousCounterValue3 = 0;


#define RED_LED LATBbits.LATB0
#define YELLOW_LED LATBbits.LATB1
#define RED_GREEN LATBbits.LATB2


// Calculate Reload_Value for 1ms with a 8 MHz oscillator and prescaler 8
#define Reload_Value 249 // (8,000,000 XTAL / 4 / 8 prescale) = 249

void initializeOscillator(void);
void initializeEUSART1(void);

// This function configures the oscillator settings for the microcontroller.
void initializeOscillator(void)
{
    OSCCON  = 0x60;     // Set IRCF bits to 110 for HF-INTOSC/2 (8 MHz, default)
    OSCCON2 = 0x00;   
    OSCTUNE = 0x00;   
    REFOCON = 0x00;   
}


void initializePort() {
    // Set LATx registers to initial states (all low)
    LATA = 0x00;
    LATB = 0x00;
    LATC = 0x00;
    LATD = 0x00;
    LATE = 0x00;
    

    // Set TRISx registers to configure pins as outputs (all output)
    TRISA = 0x00;     
    TRISB = 0x00;     // RB0 is RED, RB1 is YELLOW and RB2 is GREEN LED
    TRISC = 0x80;     // RC7 is TX and RC6 TX
    TRISD = 0x00;     
    TRISE = 0x00;     

    // Additional configuration for unused peripherals
    ANCON0 = 0x00;     // Set all analog pins to digital mode
    ANCON1 = 0x00;     // Set all analog pins to digital mode
    CM1CON = 0x00;     // Turn off Comparator 1
    CM2CON = 0x00;     // Turn off Comparator 2
    ADCON0 = 0x00;     // Disable A/D conversion
    ADCON1 = 0x00;     // Disable A/D conversion
    ADCON2 = 0x00;     // Disable A/D conversion
}


//This function configures the EUSART1 settings for communication.

void initializeUART1() {
    
    // Configure BAUDCON1 register
    // Bit 7: ABDOVF = 0 (Auto-Baud Acquisition Rollover Status bit - Cleared in software)
    // Bit 6: RCIDL = 0 (Receive Operation Idle Status bit - Receive operation is active)
    // Bit 5: RXDTP = 0 (Received Data Polarity Select bit - Receive data is not inverted)
    // Bit 4: TXCKP = 0 (Clock and Data Polarity Select bit - Idle state for transmit is high level)
    // Bit 3: BRG16 = 1 (16-Bit Baud Rate Register Enable bit - 16-bit Baud Rate Generator)
    // Bit 2: Unimplemented: Read as ?0? (Reserved, read as '0')
    // Bit 1: WUE = 0 (Wake-up Enable bit - RX pin is not monitored or rising edge is detected)
    // Bit 0: ABDEN = 0 (Auto-Baud Detect Enable bit - Baud rate measurement is disabled or completed)
  
    BAUDCON1 = 0b00001000; // Set BRG16 bit to 1 (16-Bit Baud Rate Register Enable)
    
    // Calculate and set SPBRG1 for a baud rate of approximately 9600 at 8 MHz
    SPBRG1 = 207;
    SPBRGH1 = 0;
    

    // TXSTA1 register configuration
    // Bit 7: CSRC = 0 (Clock Source Select bit - Don't care in Asynchronous mode)
    // Bit 6: TX9 = 0 (8-bit transmission)
    // Bit 5: TXEN = 1 (Transmit is enabled)
    // Bit 4: SYNC = 0 (Asynchronous mode)
    // Bit 3: SENDB = 0
    // Bit 2: BRGH = 1 (High-speed baud rate)
    // Bit 1: TRMT = 1 (TSR is empty initially)

    TXSTA1 = 0b00100100; // Configure TXSTA1 register 
  

    // RCSTA1 register configuration
    // Bit 7: SPEN = 1 (Serial Port Enable bit - Serial port is enabled)
    // Bit 6: RX9 = 0 (8-bit reception)
    // Bit 5: SREN = 0 (Single Receive Enable bit - Don't care in Asynchronous mode)
    // Bit 4: CREN = 1 (Continuous Receive Enable bit - Enables receiver)
    // Bit 3: ADDEN = 0 (Address Detect Enable bit - Don't care in Asynchronous mode)
    // Bit 2: FERR = 0 (Framing Error bit - No framing error)
    // Bit 1: OERR = 0 (Overrun Error bit - No overrun error)

     RCSTA1 = 0b10010000; // Configure RCSTA1 register

}


void initializeTimer1() {
    // Configure Timer1 in 8-bit mode with a prescaler of 8
     T1CON = 0b00110111; // 

     // Configure CCP module
     CCP1CON = 0b00001000; // 0x08

     // Load the high and low bytes of Reload_Value into CCPR1
     CCPR1 = Reload_Value;
     // CCPR1H = (Reload_Value >> 8); // Load high byte of Reload_Value
     // CCPR1L = (Reload_Value & 0xFF); // Load low byte of Reload_Value

     // Clear Timer1 registers
     TMR1H = 0;
     TMR1L = 0;

    // Clear CCP1 interrupt flag
     CCP1IF = 0;

     // Start Timer1 and CCP module
     TMR1ON = 1;
     CCP1IE = 1; // Enable CCP1 interrupt
}

void hardwareInitialize() {
    
    // Initialize the oscillator
    initializeOscillator();
    
    // Initialize Port Pins
    initializePort();
    
    // Initialize Uart1   
    initializeUART1();
    
    // Initialize Timer1
    initializeTimer1();

   // Initialize INTCON register
    INTCON = 0; // Clear INTCON register

    // Enable global and peripheral interrupts
    INTCONbits.PEIE = 1; // Peripheral Interrupt Enable
    INTCONbits.GIE = 1; // Global Interrupt Enable
    
}

/*****
*   NOTE:    This function waits until the transmitter buffer is empty (TXIF is set)
*            and then sends the character by placing it in the TXREG1 register.
*****/
void UARTWrite(char Character)
{
    // Wait until the transmitter buffer is empty (TXIF is set)
    while (TXIF == 0);

    // Send the character by placing it in the TXREG1 register
    TXREG1 = Character;
}


/*****
*   NOTE:    This function waits until the receiver buffer is full (RCIF is set)
*            and then reads the received character from the RCREG1 register.
*****/
char UARTRead(void)
{
    // Wait until the receiver buffer is full (RCIF is set)
    while (RCIF == 0);

    // Read the received character from the RCREG1 register
    return RCREG1;
}





void main(void){
    // Initialize hardware
    hardwareInitialize();

    // Delay for 5 seconds
    __delay_ms(5000);

    while (1)
    {
        // Check if the timeFlag is set (indicating a timer interrupt)
        if (timeFlag) {
            // Increment timerCounter
            timerCounter++;
            // Reset the timeFlag
            timeFlag = 0;
        }

        // Define a character to send
        char ReceivedChar;
        
        ReceivedChar = UARTRead(); // Read a command from UART
        
        if (ReceivedChar == '1') {
            // Command 1 received
            // Flash LEDs every 2 seconds
            while (ReceivedChar == '1') {
                // LED flashing logic for Command 1
                // Continue to check the elapsed time
                if (timerCounter - previousCounterValue1 >= 2000) {
                    // 2000 ms (2 seconds) have passed, toggle LEDs and reset previousCounterValue1
                    previousCounterValue1 = timerCounter;
                    // Toggle LEDs here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                // Check for a command change
                ReceivedChar = UARTRead();
            }
        }
        
        if (ReceivedChar == '2') {
            // Command 2 received
            // Flash LEDs every 1 millisecond
            while (ReceivedChar == '2') {
                // Implement LED flashing logic for Command 2
                // Continue to check the elapsed time
                if (timerCounter - previousCounterValue2 >= 1) {
                    // 1 ms has passed, toggle LEDs and reset previousCounterValue2
                    previousCounterValue2 = timerCounter;
                    // Toggle LEDs here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                // Check for a command change
                ReceivedChar = UARTRead();
            }
        }
        
        if (ReceivedChar == '3') {
            // Command 3 received
            // Flash a LEDs every half a second
            while (ReceivedChar == '3') {
                // LED flashing logic for Command 3
                // Continue to check the elapsed time
                if (timerCounter - previousCounterValue3 >= 500) {
                    // 500 ms (half a second) has passed, toggle the LED and reset previousCounterValue3
                    previousCounterValue3 = timerCounter;
                    // Toggle the LED here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                // Check for a command change
                ReceivedChar = UARTRead();
            }
        }
    }
}



void __interrupt() ISR() {
    if (CCP1IF){
     // Clear the CCP1 interrupt flag
        CCP1IF = 0;
        // advance COMPARE value
         CCPR1 += Reload_Value;

        timeFlag = 1; // Notify main routine
 }
}
 

Ian0

Joined Aug 7, 2020
8,944
Probably because of the UART read routine on line 184.
That will halt the program until a character arrives.

line 184 should say
if (RCIF) {

or

if (RCIF==true) {

so that it simply exits the routine without doing anything if there is no character to read.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
Probably because of the UART read routine on line 184.
That will halt the program until a character arrives.

line 184 should say
if (RCIF) {

or

if (RCIF==true) {

so that it simply exits the routine without doing anything if there is no character to read.
In this updated code:

  • The check for the availability of a character in the receive buffer is done using if (RCIF).
  • The code inside each command block (e.g., '1', '2', '3') continues to check for command changes using if (RCIF).

. However, this modification doesn't change the behaviour, and the program still behaves as it did previously.

C:
void main(void){
    // Initialize hardware
    hardwareInitialize();

    // Delay for 5 seconds
    __delay_ms(5000);

    while (1)
    {
        // Check if the timeFlag is set (indicating a timer interrupt)
        if (timeFlag) {
            // Increment timerCounter
            timerCounter++;
            // Reset the timeFlag
            timeFlag = 0;
        }

        // Define a character to send
        char ReceivedChar;
        
        ReceivedChar = UARTRead(); // Read a command from UART
        
        if (ReceivedChar == '1') {
            // Command 1 received
            // Flash LEDs every 2 seconds
            while (ReceivedChar == '1') {
                // LED flashing logic for Command 1
                // Continue to check the elapsed time
                if (timerCounter - previousCounterValue1 >= 2000) {
                    // 2000 ms (2 seconds) have passed, toggle LEDs and reset previousCounterValue1
                    previousCounterValue1 = timerCounter;
                    // Toggle LEDs here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                // Check for a command change
                    if (RCIF) {
                        ReceivedChar = UARTRead();
            
                     }
        }
        
        if (ReceivedChar == '2') {
            // Command 2 received
            // Flash LEDs every 1 millisecond
            while (ReceivedChar == '2') {
                // Implement LED flashing logic for Command 2
                // Continue to check the elapsed time
                if (timerCounter - previousCounterValue2 >= 1) {
                    // 1 ms has passed, toggle LEDs and reset previousCounterValue2
                    previousCounterValue2 = timerCounter;
                    // Toggle LEDs here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                if (RCIF) {
                    ReceivedChar = UARTRead();
            
                }
            }
        }
        
        if (ReceivedChar == '3') {
            // Command 3 received
            // Flash a LEDs every half a second
            while (ReceivedChar == '3') {
                // LED flashing logic for Command 3
                // Continue to check the elapsed time
                if (timerCounter - previousCounterValue3 >= 500) {
                    // 500 ms (half a second) has passed, toggle the LED and reset previousCounterValue3
                    previousCounterValue3 = timerCounter;
                    // Toggle the LED here
                    RED_LED = !RED_LED;
                    YELLOW_LED = !YELLOW_LED;
                    RED_GREEN = !RED_GREEN;
                }
                
                    if (RCIF) {
                        ReceivedChar = UARTRead();
            
                    }
            }
        }
    }
}


}
 

djsfantasi

Joined Apr 11, 2010
9,131
You are reading ReceivedChar in two (actually four!) places. Inside the if statements and within the while loop. I think the reads are out of synch…

If I were to do it, I wouldn’t read within the if statements. I’d simply set ReceivedChar to something to reset it. Blank is good or perhaps x which might simplify debugging. Then the read at the top of the while loop will be the only place to get the nect command.

Secondly, you don’t check to see if a character is available when you do the initial read in the while loop. I’d do something like
C-like:
while (RCIF == 0);
ReceivedChar = UARTRead();
Line 1 waits for a char in the input buffer. Line 2 reads that char.

Personally, I’d move the definition of ReceivedChar outside the while loop. I’m not sure but it could be that resets any value in ReceivedChar or the subsequent read which is performed without checking if a character is available could reset the value of ReceivedChar.
 

Ian0

Joined Aug 7, 2020
8,944
In the 1ms interrupt routine all you need is:
Code:
if (RCIF) {
ReceivedChar=RCREG1;
period=array[ReceivedChar];
}
counter++;
if (counter>period) {
LED=!LED;
counter=0;
}
why are you making it complicated?

You will also need to insert some code between lines 2 and 3 to deal with illegitimate commands.
 
Last edited:

BobTPH

Joined Jun 5, 2013
8,104
In the 1ms interrupt routine all you need is:
Code:
if (RCIF) {
ReceivedChar=RCREG1;
period=array[ReceivedChar];
}
counter++;
if (counter>period) {
LED=!LED;
counter=0;
}
why are you making it complicated?

You will also need to insert some code between lines 2 and 3 to deal with illegitimate commands.
Elegant. That is about as simple as it gets. And the main loop can do whatever it wants.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
In the 1ms interrupt routine all you need is:
Code:
if (RCIF) {
ReceivedChar=RCREG1;
period=array[ReceivedChar];
}
counter++;
if (counter>period) {
LED=!LED;
counter=0;
}
why are you making it complicated?

You will also need to insert some code between lines 2 and 3 to deal with illegitimate commands.
I'm having trouble understanding the logic in line 3 of the code. It appears that line 3 is storing a single character. Could you please explain what's happening in this line and how it relates to the rest of the code?
 

Ian0

Joined Aug 7, 2020
8,944
Elegant. That is about as simple as it gets. And the main loop can do whatever it wants.
Probably WFI
(that’s "wait for interrupt", if you don’t speak ARM)

Thanks for the compliment - when I learned programming, it was either elegant or it didn't fit in the ROM.
 
Last edited:

Ian0

Joined Aug 7, 2020
8,944
I'm having trouble understanding the logic in line 3 of the code. It appears that line 3 is storing a single character. Could you please explain what's happening in this line and how it relates to the rest of the code?
array[] is the lookup table that relates the character to the duration.
You need to initialise it to something like
array[]={2000,1000,500};
 

Thread Starter

Kittu20

Joined Oct 12, 2022
434
array[] is the lookup table that relates the character to the duration.
You need to initialise it to something like
array[]={2000,1000,500};
Now, let's say you receive a command character '2' through UART:
C:
ReceivedChar = '2'; // Character '2' received as a command
Here, '2' is a character, but it's also represented as its ASCII value, which is 50

When you execute the line period = array[ReceivedChar];, it's equivalent to
C:
period = array[50];
You have declared an array with a maximum length of 3 elements. However, in the line period = array[50] you're attempting to access array[50], which is outside the declared bounds of the array.

Could you please explain what happens when you try to access an element beyond the array's size like this
 

Ian0

Joined Aug 7, 2020
8,944
Now, let's say you receive a command character '2' through UART:
C:
ReceivedChar = '2'; // Character '2' received as a command
Here, '2' is a character, but it's also represented as its ASCII value, which is 50

When you execute the line period = array[ReceivedChar];, it's equivalent to
C:
period = array[50];
You have declared an array with a maximum length of 3 elements. However, in the line period = array[50] you're attempting to access array[50], which is outside the declared bounds of the array.

Could you please explain what happens when you try to access an element beyond the array's size like this
You didn't say whether you were sending ASCII'2' or 0x02 as a command.
That's what I said "You will also need to insert some code between lines 2 and 3 to deal with illegitimate commands."
 
Top