I have an MCU (TI Tiva TM4C) that operates a GSM modem, LCD display, keypad, ADC inputs, and four relays for microgrid control/operation.
To date I have about 2000 lines of code, mostly for supporting peripherals. Now the hard work of creating an algorithm begins.
Before I do that, I am hoping a few embedded C experts could scan my code, notice my rookie mistakes, and point them out to me. Then I can do some digging and learn how to write more robust code, to make sure my foundational code is solid.
The main function is below. Other functions are on GitHub.
int
main(void)
{
char aString[2][128]; // Generic string
int anInt; // Generic int
int msgOpen = 0; // Message being processed
int ctr1; // Generic counter
uint32_t pui32ADC0Value[1]; // ADC0 data value
uint32_t ui32D0v; // mV value on external input D0
// Initial settings - from Anil
ROM_FPUEnable(); // Enable floating point unit
ROM_FPULazyStackingEnable(); // Enable lazy stacking of FPU
ROM_IntMasterEnable(); // Enable processor interrupts
// Enable device clocking
ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
// Enable peripherals
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); // ADC1
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EEPROM0); // EEPROM (2048 bytes in 32 blocks)
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); // Pins: UART0
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); // Pins: UART1, GSM, Relays, I2C0SCL & SDA
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); // Pins: Neopixel, keypad INT2
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD); // Pins: LCD screen
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE); // Pins: Relays
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF); // Pins: RGB LED, Relays
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0); // I2C for MPR121 touchpad controller
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI3); // SSI3 for EA DOGS102W6 LCD display
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); // Timer for keylock
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1); // Timer for keypad timeout
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0); // Console UART
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1); // GSM UART
// Configure GPIO outputs
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_5); // Rel3N
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_6); // GSM PWRKEY
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTB_BASE, GPIO_PIN_7); // GSM RESET
//ROM_GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_4); // Neopixel
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_2); // Rel3
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_3); // Rel2
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_5); // Rel2N
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3); // RGB LED
if (hwRev == 1) {
// ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_1); // Rel4
// ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_4); // Rel1N
// ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1); // Rel1 (conflict with red LED)
// ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_4); // Rel4N (conflict with USR SW1)
}
else if (hwRev == 2) {
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_0); // Rel4N
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_4); // Rel1N
// Disable NMI on PF0
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY; // Unlock the port
HWREG(GPIO_PORTF_BASE + GPIO_O_CR) |= GPIO_PIN_0; // Unlock the pin
HWREG(GPIO_PORTF_BASE + GPIO_O_AFSEL) &= ~GPIO_PIN_0;
HWREG(GPIO_PORTF_BASE + GPIO_O_DEN) |= GPIO_PIN_0;
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = 0; // Lock the port
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0); // Rel1 (conflict with USR SW2)
ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_4); // Rel4 (conflict with USR SW1)
}
// Turn on an LED to show that we're working
GPIOPinWrite(GPIO_PORTF_BASE, BL_LED, BL_LED);
// Start I2C module (for keypad)
initI2C();
// Start the MPR121 (keypad controller) and set thresholds (do this early
// since it takes a moment to calibrate)
initMPR121();
// Set up the timers used to lock/unlock the keypad
ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_ONE_SHOT);
ROM_TimerConfigure(TIMER1_BASE, TIMER_CFG_ONE_SHOT);
ROM_TimerLoadSet(TIMER1_BASE, TIMER_A, ROM_SysCtlClockGet()* 15);
// Setup the interrupts for the timer timeouts
ROM_IntEnable(INT_TIMER0A);
ROM_IntEnable(INT_TIMER1A);
ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
ROM_TimerIntEnable(TIMER1_BASE, TIMER_TIMA_TIMEOUT);
// Start SSI3 and the LCD display
initSSI3();
initLCD();
// Console UART0: Set PA0 and PA1 as UART0, configure for 115200,
// 8-N-1 operation, enable interrupts
ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UART0StdioConfig(0, 115200, 16000000);
ROM_IntEnable(INT_UART0);
ROM_UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT);
// Notify the user what's going on
UART0printf("\n\n\n\r>>> INITIALIZING");
// Get MCU ID
FlashUserGet(&boardID1,&boardID2);
// Print to LCD (along with header)
LCDstring(0,0,"MCU/IMEI/ON@/NUM:",INVERSE);
snprintf (aString[1],18,"%X-%X",boardID1,boardID2);
LCDstring(1,0,aString[1],NORMAL);
LCDstring(2,0,"...",NORMAL);
// GSM UART1: Set PB0 and PB1 as UART1, configure for 115200,
// 8-N-1 operation, enable interrupts
ROM_GPIOPinConfigure(GPIO_PB0_U1RX);
ROM_GPIOPinConfigure(GPIO_PB1_U1TX);
ROM_GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UART1StdioConfig(1, 115200, 16000000);
ROM_IntEnable(INT_UART1);
ROM_UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT);
/// GSM TEST AREA
if (testGSM)
{
// See if the GSM module is on: try three times to power up
GSMcheckPower(3);
// Request and print the IMEI (s/n) of the SIM module (used as board s/n)
GSMgetIMEI();
LCDstring(2,0,IMEI,NORMAL);
LCDstring(3,0,"...",NORMAL);
// Request SIM card status
GSMgetSIMstatus();
// Get the time. Use a loop here since the time doesn't always come
// through on the first try after powering up. Make ctr1 attempts.
ctr1 = 10;
YY = 2000;
while (YY == 2000 && ctr1 > 0)
{
// Turn off the LED to indicate what's going on
GPIOPinWrite(GPIO_PORTF_BASE, BL_LED, 0);
// Check the time, decrement counter
GSMcheckTime();
ctr1--;
// Wait a bit, turn the LED back on
ROM_SysCtlDelay(ROM_SysCtlClockGet()/3);
GPIOPinWrite(GPIO_PORTF_BASE, BL_LED, BL_LED);
}
// Print the on-time
UART0printf("\n\r> On-time from GSM: %s",fullOnTime);
LCDstring(3,0,fullOnTime,NORMAL);
LCDstring(4,0,"...",NORMAL);
// If SIM card is present, get the phone number and balance
if ( SIMpresent )
{
// Get number
GSMgetNum();
// Get balance
GSMcheckBalance();
}
// Print phone number / SIM status to LCD
LCDstring(4,0,SIMID,NORMAL);
}
/// ADC TEST AREA - start the ADC
if (testADC){ ADCinit(); }
// Notify the user what testing functions are active
UART0printf("\n\r> ----------Testing function status:----------");
if (testGSM) { UART0printf("\n\r> ENABLED : GSM power at boot"); }
else {UART0printf("\n\r> DISABLED: GSM power at boot");}
if (testEEPROM) { UART0printf("\n\r> ENABLED : Store/retrieve ontime from EEPROM"); }
else {UART0printf("\n\r> DISABLED: Store/retrieve ontime from EEPROM");}
if (testDelete) { UART0printf("\n\r> ENABLED : Delete messages during processing"); }
else {UART0printf("\n\r> DISABLED: Delete messages during processing");}
if (testNotify) { UART0printf("\n\r> ENABLED : Message controller at boot"); }
else {UART0printf("\n\r> DISABLED: Message controller at boot");}
if (testADC) { UART0printf("\n\r> ENABLED : Test ADC"); }
else {UART0printf("\n\r> DISABLED: Test ADC");}
UART0printf("\n\r> --------------------------------------------");
// Initialize the SysTick interrupt to process buttons
ButtonsInit();
SysTickPeriodSet(SysCtlClockGet() / APP_SYSTICKS_PER_SEC);
SysTickEnable();
SysTickIntEnable();
// Notify the user about buttons
UART0printf("\n\r> LEFT BUTTON: Enter \"talk to GSM\" mode (blue LED). Updates signal strength.");
UART0printf("\n\r> RIGHT BUTTON: Toggle power to GSM module (red LED).");
/// EEPROM TEST AREA: Store on-time, retrieve last on-time.
// Don't run this each time 'cause EEPROM wears out.
if (testEEPROM)
{
EEPROMInit();
struct E2S_TIME E2writeTime = {YY,MM,DD,hh,mm,ss,zz};
//Read from struct at EEPROM start from 0x0000
EEPROMRead((uint32_t *)&E2readTime, E2A_ONTIME, sizeof(E2readTime));
UART0printf("\n\r> Last on-time: %u/%u/%u, %u:%u:%u, %d", E2readTime.E2YY, E2readTime.E2MM, E2readTime.E2DD, E2readTime.E2hh, E2readTime.E2mm, E2readTime.E2ss, E2readTime.E2zz, E2A_ONTIME);
//Write struct to EEPROM start from 0x0000
EEPROMProgram((uint32_t *)&E2writeTime, E2A_ONTIME, sizeof(E2writeTime));
// Some EEPROM functions
/*esize = EEPROMSizeGet(); // Get EEPROM Size
UART0printf("E2> EEPROM Size %d bytes\n", e2size);
eblock = EEPROMBlockCountGet(); // Get EEPROM Block Count
UART0printf("E2> EEPROM Blok Count: %d\n", e2block);*/
}
// Clear the LCD and set up for normal use:
LCDclear(0,0,XMAX,YMAX);
// Print relay status:
if ( !testEEPROM ) { relaySet(0); }
else
{
// Read the last relay status from EEPROM
EEPROMRead(&E2relayStatus, E2A_RELAY_STATUS, sizeof(E2relayStatus));
// Notify user
UART0printf("\n\r> Relay status from EEPROM (%X): ",E2relayStatus);
for ( int r=0; r<4; r++ ) { UART0printf("%u",(E2relayStatus >> r) & 1); }
// Get only the LSB of E2relayStatus
E2relayStatus &= 0x000F;
// Toggle each bit in relayStatus in order to properly update the display
relayStatus = 15-E2relayStatus;
UART0printf("\n\r> 15-E2relayStatus = %u",relayStatus);
// Apply the previous relay states
relaySet(E2relayStatus);
}
// Get the GSM signal strength and print to LCD (along with balance)
if (testGSM)
{
GSMcheckSignal();
// Print balance to LCD
LCDstring(1,(18-sizeof(balance))*6,"$",NORMAL);
LCDstring(1,(19-sizeof(balance))*6,balance,NORMAL);
}
/// CONTROLLER NOTIFY
if (testNotify && SIMpresent){
snprintf(aString[1],83,"MCU %X-%X IMEI %s OT %s BAL %s",boardID1,boardID2,IMEI,fullOnTime,balance);
GSMsendSMS( ctrlID, aString[1] );
}
// Disable talk mode (was letting GSM notifs in during setup)
talkMode = false;
/// SETUP COMPLETE!
UART0printf("\n\r> Setup complete! \n\r>>> RUNNING MAIN PROGRAM");
GPIOPinWrite(GPIO_PORTF_BASE, BL_LED, 0);
// Lock keypad
MPR121toggleLock();
LCDstring(7,0,"SETUP COMPLETE! ", NORMAL);
/// MAIN LOOP -
// 1. Wait for new message notification and process.
// 2. Update ADC.
while(1){
// Process new messages.
while (msgCount > 0)
{
// Start working on the oldest message
msgOpen = msgCount;
msgCount--;
// Process message for envelope and content
GSMprocessMessage(msgOpen);
// If message content is good, act on message
if (strstr(msgSender,ctrlID) != NULL && strlen(msgContent) == 4) {
for ( ctr1=0;ctr1<4;ctr1++ ){
if ( msgContent[ctr1] == '1' ) { anInt |= 1 << ctr1; }
else if (msgContent[ctr1] == '0' ) { anInt &= ~(1 << ctr1); }
}
relaySet(anInt);
}
// After the last new message, update the balance and EEPROM
if ( msgCount == 0 )
{
GSMcheckBalance();
relayStatusE2();
}
}
// Run the ADC
if ( testADC && msgCount == 0 ) {
// Trigger the ADC conversion.
ADCProcessorTrigger(ADC0_BASE, 3);
// Wait for conversion to be completed.
while(!ADCIntStatus(ADC0_BASE, 3, false)){}
// Clear the ADC interrupt flag.
ADCIntClear(ADC0_BASE, 3);
// Read ADC Value.
ADCSequenceDataGet(ADC0_BASE, 3, pui32ADC0Value);
// Convert to millivolts
ui32D0v = pui32ADC0Value[0] * (3300.0/4095);
// Convert to a string (in volts, three decimal places)
snprintf (aString[1],7,"%d.%03dV", ui32D0v / 1000, ui32D0v % 1000);
// Display the AIN0 (PE0) digital value on the console.
LCDstring(2,11*6,aString[1],NORMAL);
// Wait a bit
ROM_SysCtlDelay(ROM_SysCtlClockGet()/4);
}
}
//return(0);
}