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.
0