cfreertostexas-instruments

Having trouble trying to do embedded programming with FreeRTOS in ccs


im trying to do som embedded programming using FreeRTOS in ccs on a tiva board with a keypad & lcd display extension (btw. im not an expert at c programming). I have made a task for the keypad and the lcd, and together they should work together to display the pressed key onto the lcd display. I am able to compile/debug the code in ccs but when I try to press a button on the keypad nothing is being shown on the lcd display. My suspicion is that they way im using FreeRTOS is wrong or that the functions to make it work are wrong. I would love for you to look over the code for the two tasks (lcd & keypad) and the main.c and see if you can spot why it isn't working.

Task 1 the keypad_task (written in a keypad.c file): With this task im trying to scan & detect any key press on the keypad and store the value of the key pressed into a queue (the queue is created in the main.c file). For this task to work i initialise the keypad and create functions like scan_keypad, Read_keypad.

Task 2 the lcd_task (written in a lcd.c file): With this task im trying to detect if there is anything in the queue and if there is write it to the lcd display. To make this work I made a lot of functions, such as lcd_init (initialising the lcd), lcd_command, lcd_pulse_enable, delay_us (not sure if this function is relevant because I could use vTaskDelay instead?), lcd_write_char and lcd_wait_busy

The main.c

/**
 * main.c
 */
#include <stdint.h>
#include "tm4c123gh6pm.h"
#include "emp_type.h"
#include "systick_frt.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "keypad.h"
#include "lcd.h"


#define USERTASK_STACK_SIZE configMINIMAL_STACK_SIZE
#define IDLE_PRIO 0
#define LOW_PRIO  1
#define MED_PRIO  2
#define HIGH_PRIO 3
#define QUEUE_SIZE 50


QueueHandle_t LCDQueue;

static void setupHardware(void)

{
 
  init_systick();
  keypad_init();
  lcd_init();

}

int main(void)
{
    setupHardware();
    LCDQueue = xQueueCreate(QUEUE_SIZE, sizeof(char));
    xTaskCreate( keypad_task, "keypad", USERTASK_STACK_SIZE, (void *)LCDQueue, HIGH_PRIO, NULL );
    xTaskCreate(lcd_write_task, "LCD Write Task", USERTASK_STACK_SIZE, (void *)LCDQueue, MED_PRIO, NULL);
    vTaskStartScheduler();
    return 0;
}

The keypad.c

#include <stdint.h>
#include "tm4c123gh6pm.h"
#include "FreeRTOS.h"
#include "Task.h"
#include "queue.h"
#include "semphr.h"
#include "emp_type.h"



void keypad_init(void)
{
    INT8S dummy;
    vTaskDelay(10);
    // Enable clock for GPIO Port A and Port E
    SYSCTL_RCGC2_R |= SYSCTL_RCGC2_GPIOA;

    vTaskDelay(10);
    SYSCTL_RCGC2_R |= SYSCTL_RCGC2_GPIOE;

    vTaskDelay(10);
    // Dummy read to ensure the clock is stable
    dummy = SYSCTL_RCGC2_R;

    vTaskDelay(10);
    // Configure PA2-PA4 as inputs for rows
    GPIO_PORTA_DIR_R &= ~0x1C;

    vTaskDelay(10);
    // Configure PE0-PE3 as outputs for columns
    GPIO_PORTE_DIR_R |= 0x0F;

    vTaskDelay(10);
    // Enable digital functionality for PA2-PA4 and PE0-PE3
    GPIO_PORTA_DEN_R |= 0x1C;

    vTaskDelay(10);
    GPIO_PORTE_DEN_R |= 0x0F;

    vTaskDelay(10);
    // Enable pull-up resistors for PA2-PA4 (rows)
    GPIO_PORTA_PUR_R |= 0x1C;
}


char scan_keypad(void)
{
    const char keys[4][3] = {
        {'1', '2', '3'},
        {'4', '5', '6'},
        {'7', '8', '9'},
        {'#', '0', '*'}
    };

    int row;
        for (row = 0; row < 4; row++) {
            // Drive one row (PE0-PE3) low
            GPIO_PORTE_DATA_R = ~(1U << row);
            vTaskDelay(pdMS_TO_TICKS(2));  // Debounce delay
            int col;
            for (col = 0; col < 3; col++) {
                if (!(GPIO_PORTA_DATA_R & (1 << (col + 2)))) { // If button is pressed
                    GPIO_PORTE_DATA_R = 0x0F; // Reset rows to high before exit
                    return keys[row][col];
                }
            }
            // Reset row to high before moving to next
            GPIO_PORTE_DATA_R = 0xFF;
        }
        return '\0'; // No key pressed
    }

char read_keypad(void)
{
    return scan_keypad();
}




void keypad_task(void *pvParameters)
{
    QueueHandle_t LCDQueue = (QueueHandle_t)pvParameters;
    char keyPressed;

    while(1)
    {
        keyPressed = read_keypad(); // Read the keypad input

        if(keyPressed != '\0') // If a key is pressed
        {

            xQueueSend(LCDQueue, &keyPressed, 10); // Send the key to LCD task
        }

        vTaskDelay(50 / portTICK_PERIOD_MS); // Delay to debounce the keypad
    }
}

The lcd.c

#include <stdint.h>
#include "tm4c123gh6pm.h"
#include "FreeRTOS.h"
#include "Task.h"
#include "queue.h"
#include "semphr.h"
#include "emp_type.h"
#include "systick_frt.h"



void delay_us(uint32_t us)
{
    // Convert microseconds to milliseconds
    uint32_t ms_delay = (us + 999) / 1000;

    // Use FreeRTOS delay function to wait
    vTaskDelay(ms_delay / portTICK_RATE_MS);
}

void lcd_pulse_enable(void) {
    // Pulse E pin for LCD
    GPIO_PORTD_DATA_R |= 0x08;  // E = 1
    delay_us(1);  // Short delay
    GPIO_PORTD_DATA_R &= ~0x08;  // E = 0
    delay_us(1);  // Short delay
}

void lcd_command(uint8_t command) {
    // Set RS to 0 for command mode
    GPIO_PORTD_DATA_R &= ~0x04;  // RS = 0

    // Send higher nibble first
    GPIO_PORTC_DATA_R = (GPIO_PORTC_DATA_R & 0x0F) | (command & 0xF0);
    lcd_pulse_enable();

    // Send lower nibble
    GPIO_PORTC_DATA_R = (GPIO_PORTC_DATA_R & 0x0F) | ((command & 0x0F) << 4);
    lcd_pulse_enable();

    // Delay after sending command
    delay_us(50);  // You can implement a delay function to wait for LCD to process the command
}

void lcd_init(void) {
    vTaskDelay(10);
    // Enable clock for GPIO Port C and Port D
    SYSCTL_RCGC2_R |= SYSCTL_RCGC2_GPIOC | SYSCTL_RCGC2_GPIOD;

    // Dummy read to ensure the clock is stable
    volatile uint32_t dummy = SYSCTL_RCGC2_R;

    vTaskDelay(10);
    // Configure PC4-PC7 as digital outputs for LCD data
    GPIO_PORTC_DIR_R |= 0xF0;  // PC4-PC7
    vTaskDelay(10);
    GPIO_PORTC_DEN_R |= 0xF0;  // PC4-PC7

    vTaskDelay(10);
    // Configure PD2 and PD3 as digital outputs for LCD control (RS and E)
    GPIO_PORTD_DIR_R |= 0x0C;  // PD2 and PD3
    vTaskDelay(10);
    GPIO_PORTD_DEN_R |= 0x0C;  // PD2 and PD3

    delay_us(2);
    // Initialize LCD (You can modify these initialization commands based on your LCD's requirements)
    lcd_command(0x28);  // 4-bit mode, 2-line display, 5x8 font
    delay_us(2);
    lcd_command(0x06);  // Increment cursor, no display shift
    delay_us(2);
    lcd_command(0x0C);  // Display on, cursor off, blinking off
    delay_us(2);
    lcd_command(0x01);  // Clear display
    delay_us(2);
    lcd_command(0x02);  // Return home
}

void lcd_write_char(uint8_t data) {
    // Set RS to 1 for data mode
    GPIO_PORTD_DATA_R |= 0x04;  // RS = 1

    // Send higher nibble first
    GPIO_PORTC_DATA_R = (GPIO_PORTC_DATA_R & 0x0F) | (data & 0xF0);
    lcd_pulse_enable();

    // Send lower nibble
    GPIO_PORTC_DATA_R = (GPIO_PORTC_DATA_R & 0x0F) | ((data & 0x0F) << 4);
    lcd_pulse_enable();

    // Delay after sending data
    delay_us(10);  // You can implement a delay function to wait for LCD to process the data
}

void lcd_wait_busy(void) {
    // Set RS to 0 for command mode
    GPIO_PORTD_DATA_R &= ~0x04;

    // Configure PC4-PC7 as inputs
    GPIO_PORTC_DIR_R &= ~0xF0;

    // Enable read mode (RW = 1)
    GPIO_PORTD_DATA_R |= 0x02;

    // Wait until BF becomes 0
    while (GPIO_PORTC_DATA_R & 0x80);

    // Restore original settings
    GPIO_PORTD_DATA_R &= ~0x02;
    GPIO_PORTC_DIR_R |= 0xF0;
}

void lcd_write_task(void *pvParameters) {
    QueueHandle_t LCDQueue = (QueueHandle_t)pvParameters;
    char receivedChar;
    lcd_command(0x01);
    while (1) {
        if (xQueueReceive(LCDQueue, &receivedChar, portMAX_DELAY) == pdTRUE) {

            // Write the received character to the LCD
            lcd_wait_busy();
            lcd_write_char((uint8_t)receivedChar);
        }
    }
}

I have written a lot of codes trying to do the same thing but I always end up at the same conclusion that the program can compile/debug but when I press a key on the keypad nothing is shown on the lcd display.

In the end I want to achieve the functionality of when I press a key on the keypad the value of that key is displayed on the lcd display and when a new key is pressed the value of that is displayed instead of the other.


Solution

    1. Test it without reading the keyboard. Simply queue some chars to see if it works.
    2. If it works then check if your key reading function is not in the dead loop and if it is sending anything else except 0
    3. If it does not work, check if your LCD write functions do not end in the dead loops. 3.1 Check freeRTOS configuration (interrupt priorities, stack etc)