Good Evening,
I have a sample program that introduces the use of an interrupt with UART. I am trying to modify it so that I can sample the ADC temperature but it gets stuck at an if statement. Within the modified code, while debugging I get stuck at if(RingBufEmpty(&rxRingBuf) == false) which is inside main().
I should state that I have two boards, the one sampling temperature and the one receiving it. I'm struggling to see how to specify which board does what. But for debugging purposes, I turn on the one which transmits and program it first. I then shut it off, turn on the second receiving board and program that. I have then had luck turning back on the transmitting board.
I use a putty connection on the receiving board to see if I am in fact receiving anything. I use the debugger to follow the code around.
Hopefully you all can assist me with this!
The working code:
#include <stdint.h>
#include <stdbool.h>
#include <inc/hw_memmap.h>
#include <inc/hw_ints.h>
#include <driverlib/gpio.h>
#include <driverlib/sysctl.h>
#include <driverlib/uart.h>
#include <driverlib/timer.h>
#include <driverlib/interrupt.h>
#include <driverlib/pin_map.h>
#include "ringbuf.h"
#include "utils/uartstdio.h"
#include "utils/uartstdio.c"
#include "driverlib/debug.h"
// This is a dummy message formatted as a struct. Note that while the ISA
// is 32 bits the message is not perfectly aligned. I did this to demonstrate
// that the RT UART interrupt is required.
typedef struct {
float voltage;
uint32_t id;
float otherVoltage;
int32_t code;
uint8_t random;
} message_t;
#define RING_BUF_SIZE 512
// Tx buffer and backend array.
static tRingBufObject txRingBuf;
static uint8_t txBuf[RING_BUF_SIZE];
// Rx buffer and back end array.
static tRingBufObject rxRingBuf;
static uint8_t rxBuf[RING_BUF_SIZE];
void UART1IntHandler(void)
{
uint32_t intStatus;
// Retrieve masked interrupt status (only enabled interrupts).
intStatus = UARTIntStatus(UART1_BASE, true);
// Clear interrupt(s) after retrieval.
UARTIntClear(UART1_BASE, intStatus);
// Important: Note that they're all IF statements. This is because you can have an NVIC
// system interrupt that is composed of all three sub-interrupts below. If you use the standard
// if-elseif-else then you might miss one and drop bytes.
// The receive timeout interrupt fires when you have received bytes in your FIFO but have not
// gotten enough to fire your Rx interrupt. This is because the FIFO level select determines when that
// interrupt goes off.
if((intStatus & UART_INT_RT) == UART_INT_RT)
{
// While there are bytes to read and there is space in the FIFO.
while(UARTCharsAvail(UART1_BASE) && RingBufFull(&rxRingBuf) == false)
{
// Write a byte straight from the hardware FIFO into our Rx FIFO for processing later.
RingBufWriteOne(&rxRingBuf, (uint8_t)UARTCharGet(UART1_BASE));
}
}
// The Rx interrupt fires when there are more than the fifo level select bytes in the FIFO.
if((intStatus & UART_INT_RX) == UART_INT_RX)
{
// While there are bytes to read and there is space in the FIFO.
while(UARTCharsAvail(UART1_BASE) && RingBufFull(&rxRingBuf) == false)
{
// Write a byte straight from the hardware FIFO into our Rx FIFO for processing later.
RingBufWriteOne(&rxRingBuf, (uint8_t)UARTCharGet(UART1_BASE));
}
}
// The transmit interrupt fires when there are less than the FIFO level select bytes waiting to
// be transmitted. This way you can add more ensuring it's continuous transmission.
if((intStatus & UART_INT_TX) == UART_INT_TX)
{
// While there is space left in the FIFO and we have bytes queued up for sending.
while(UARTSpaceAvail(UART1_BASE) == true && RingBufEmpty(&txRingBuf) == false)
{
// Remove a byte from the queued Tx FIFO and add it to our UART tx fifo.
UARTCharPut(UART1_BASE, RingBufReadOne(&txRingBuf));
}
}
}
void Timer0IntHandler(void)
{
int32_t intStatus;
message_t msg;
uint8_t * ptr,
byte;
// Retrieve timer interrupt status.
intStatus = TimerIntStatus(TIMER0_BASE, true);
// Clear interrupt.
TimerIntClear(TIMER0_BASE, intStatus);
// Check if the interrupt was the correct one (timeout).
if((intStatus & TIMER_TIMA_TIMEOUT) == TIMER_TIMA_TIMEOUT)
{
// This message population is just a placeholder for where an ADC or
// other sensor might be sampled.
msg.voltage = 3.3F;
msg.id = 1;
msg.otherVoltage = 2.5F;
msg.code = 200;
msg.random = 7;
// Cast the structure into a byte array.
ptr = (uint8_t *)&msg;
// Loop through the entire message.
for(byte = 0; byte < sizeof(message_t); byte++)
{
// This part is pretty important... the UART Tx FIFO has a limited size
// in fact it's smaller than our message. However the Tx interrupt will let us know
// when we have less than 4 bytes remainign to send so we can stash the extra
// bytes in our FIFO for later.
// Is there space in the Tx FIFO?
if(UARTSpaceAvail(UART1_BASE) == true)
{
// Put the byte into the Tx FIFO.
UARTCharPut(UART1_BASE, ptr[byte]);
}
else
{
// Otherwise, stash the byte in our FIFO for when there's space.
RingBufWriteOne(&txRingBuf, ptr[byte]);
}
}
}
}
int main(void)
{
// Run the microcontroller system clock at 80MHz.
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
// Enable peripheral and bank clocking.
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralReset(SYSCTL_PERIPH_UART1);
//#TEST Enables UART0 for console use.
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//Configure the pins, uh..., plated holes for UART0
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
// Initialize the UART for console I/O. on UART0/BASE0
UARTStdioConfig(0, 9600, SysCtlClockGet());
//Set the baud rate, 8 data bits, one stop bit and no parity for UART0
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 9600,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
// Configure pin-muxing for the UART.
GPIOPinConfigure(GPIO_PB0_U1RX);
GPIOPinConfigure(GPIO_PB1_U1TX);
GPIOPinTypeUART(GPIO_PORTB_BASE, (GPIO_PIN_0 | GPIO_PIN_1));
// Configure the UART for 9600 8-N-1.
UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 9600, (UART_CONFIG_WLEN_8 | UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE));
UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
UARTEnable(UART1_BASE);
// Initialize our FIFO's.
RingBufInit(&txRingBuf, &txBuf[0], RING_BUF_SIZE);
RingBufInit(&rxRingBuf, &rxBuf[0], RING_BUF_SIZE);
// Enable the NVIC interrupt, clear the UART individual interrupts and then enable.
IntEnable(INT_UART1);
UARTIntClear(UART1_BASE, UARTIntStatus(UART1_BASE, false));
UARTIntEnable(UART1_BASE, (UART_INT_TX | UART_INT_RX | UART_INT_RT));
// Setup the timer to go off at 1Hz (periodically). This will be used to generate a message
// for transmission.
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet());
IntEnable(INT_TIMER0A);
TimerIntClear(TIMER0_BASE, TimerIntStatus(TIMER0_BASE, false));
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
TimerEnable(TIMER0_BASE, TIMER_A);
while(true)
{
//Check if we have received some bytes.
if(RingBufEmpty(&rxRingBuf) == false)
{
// Did we receive enough bytes that it could be a message? In the real world you
// need more complicated logic for multiple messages but this works for now.
if(RingBufUsed(&rxRingBuf) == sizeof(message_t))
{
message_t receivedMsg;
// Read the bytes into our received message structure.
RingBufRead(&rxRingBuf, (uint8_t *)&receivedMsg, sizeof(message_t));
// At this point we have a complete message. Note that bytes would have
// to be endian swapped if it werent for the fact this is Little-Endian host
// to little-endian host.
bool stop;
stop = true;
UARTprintf("Code received is %d\n",receivedMsg.code);
}
}
}
}
My modifications...
#include <stdint.h>
#include <stdbool.h>
#include <inc/hw_memmap.h>
#include <inc/hw_ints.h>
#include "inc/hw_types.h"
#include <driverlib/gpio.h>
#include <driverlib/sysctl.h>
#include <driverlib/uart.h>
#include <driverlib/timer.h>
#include <driverlib/interrupt.h>
#include <driverlib/pin_map.h>
#include "ringbuf.h"
#include "utils/uartstdio.h"
#include "utils/uartstdio.c"
#include "driverlib/debug.h"
#include "driverlib/adc.h"
#include "driverlib/sysctl.h"
// This is a dummy message formatted as a struct. Note that while the ISA
// is 32 bits the message is not perfectly aligned. I did this to demonstrate
// that the RT UART interrupt is required.
typedef struct {
int32_t tempF;
} message_t;
#define RING_BUF_SIZE 512
// Tx buffer and backend array.
static tRingBufObject txRingBuf;
static uint8_t txBuf[RING_BUF_SIZE];
// Rx buffer and back end array.
static tRingBufObject rxRingBuf;
static uint8_t rxBuf[RING_BUF_SIZE];
uint32_t ui32ADC0Value[4];
uint32_t ui32TempAvg
uint32_t ui32TempValueC;
uint32_t ui32TempValueF;
void UART1IntHandler(void)
{
uint32_t intStatus;
// Retrieve masked interrupt status (only enabled interrupts).
intStatus = UARTIntStatus(UART1_BASE, true);
// Clear interrupt(s) after retrieval.
UARTIntClear(UART1_BASE, intStatus);
// Important: Note that they're all IF statements. This is because you can have an NVIC
// system interrupt that is composed of all three sub-interrupts below. If you use the standard
// if-elseif-else then you might miss one and drop bytes.
// The receive timeout interrupt fires when you have received bytes in your FIFO but have not
// gotten enough to fire your Rx interrupt. This is because the FIFO level select determines when that
// interrupt goes off.
if((intStatus & UART_INT_RT) == UART_INT_RT)
{
// While there are bytes to read and there is space in the FIFO.
while(UARTCharsAvail(UART1_BASE) && RingBufFull(&rxRingBuf) == false)
{
// Write a byte straight from the hardware FIFO into our Rx FIFO for processing later.
RingBufWriteOne(&rxRingBuf, (uint8_t)UARTCharGet(UART1_BASE));
}
}
// The Rx interrupt fires when there are more than the fifo level select bytes in the FIFO.
if((intStatus & UART_INT_RX) == UART_INT_RX)
{
// While there are bytes to read and there is space in the FIFO.
while(UARTCharsAvail(UART1_BASE) && RingBufFull(&rxRingBuf) == false)
{
// Write a byte straight from the hardware FIFO into our Rx FIFO for processing later.
RingBufWriteOne(&rxRingBuf, (uint8_t)UARTCharGet(UART1_BASE));
}
}
// The transmit interrupt fires when there are less than the FIFO level select bytes waiting to
// be transmitted. This way you can add more ensuring it's continuous transmission.
if((intStatus & UART_INT_TX) == UART_INT_TX)
{
// While there is space left in the FIFO and we have bytes queued up for sending.
while(UARTSpaceAvail(UART1_BASE) == true && RingBufEmpty(&txRingBuf) == false)
{
// Remove a byte from the queued Tx FIFO and add it to our UART tx fifo.
UARTCharPut(UART1_BASE, RingBufReadOne(&txRingBuf));
}
}
}
void Timer0IntHandler(void)
{
int32_t intStatus;
message_t msg;
uint8_t * ptr,
byte;
// Retrieve timer interrupt status.
intStatus = TimerIntStatus(TIMER0_BASE, true);
// Clear interrupt.
TimerIntClear(TIMER0_BASE, intStatus);
// Check if the interrupt was the correct one (timeout).
if((intStatus & TIMER_TIMA_TIMEOUT) == TIMER_TIMA_TIMEOUT)
{
// This message population is just a placeholder for where an ADC or
// other sensor might be sampled.
msg.tempF = ui32TempValueF;
// Cast the structure into a byte array.
ptr = (uint8_t *)&msg;
// Loop through the entire message.
for(byte = 0; byte < sizeof(message_t); byte++)
{
// This part is pretty important... the UART Tx FIFO has a limited size
// in fact it's smaller than our message. However the Tx interrupt will let us know
// when we have less than 4 bytes remainign to send so we can stash the extra
// bytes in our FIFO for later.
// Is there space in the Tx FIFO?
if(UARTSpaceAvail(UART1_BASE) == true)
{
// Put the byte into the Tx FIFO.
UARTCharPut(UART1_BASE, ptr[byte]);
}
else
{
// Otherwise, stash the byte in our FIFO for when there's space.
RingBufWriteOne(&txRingBuf, ptr[byte]);
}
}
}
}
int main(void)
{
// Run the microcontroller system clock at 80MHz.
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
// Enable peripheral and bank clocking.
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralReset(SYSCTL_PERIPH_UART1);
//#TEST Enables UART0 for console use.
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
//Configure the pins, uh..., plated holes for UART0
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
// Initialize the UART for console I/O. on UART0/BASE0
UARTStdioConfig(0, 9600, SysCtlClockGet());
//Set the baud rate, 8 data bits, one stop bit and no parity for UART0
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 9600,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
// Configure pin-muxing for the UART.
GPIOPinConfigure(GPIO_PB0_U1RX);
GPIOPinConfigure(GPIO_PB1_U1TX);
GPIOPinTypeUART(GPIO_PORTB_BASE, (GPIO_PIN_0 | GPIO_PIN_1));
// Configure the UART for 9600 8-N-1.
UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 9600, (UART_CONFIG_WLEN_8 | UART_CONFIG_PAR_NONE | UART_CONFIG_STOP_ONE));
UARTFIFOLevelSet(UART1_BASE, UART_FIFO_TX4_8, UART_FIFO_RX4_8);
UARTEnable(UART1_BASE);
// Initialize our FIFO's.
RingBufInit(&txRingBuf, &txBuf[0], RING_BUF_SIZE);
RingBufInit(&rxRingBuf, &rxBuf[0], RING_BUF_SIZE);
// Enable the NVIC interrupt, clear the UART individual interrupts and then enable.
IntEnable(INT_UART1);
UARTIntClear(UART1_BASE, UARTIntStatus(UART1_BASE, false));
UARTIntEnable(UART1_BASE, (UART_INT_TX | UART_INT_RX | UART_INT_RT));
// Setup the timer to go off at 1Hz (periodically). This will be used to generate a message
// for transmission.
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);
TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet());
IntEnable(INT_TIMER0A);
TimerIntClear(TIMER0_BASE, TimerIntStatus(TIMER0_BASE, false));
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
TimerEnable(TIMER0_BASE, TIMER_A);
//Enable the analog to digital converter. ADC0
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0);
//use ADC0, sample sequencer 1, use the highest priority
ADCSequenceConfigure(ADC0_BASE, 1, ADC_TRIGGER_PROCESSOR, 0);
//Configure all four steps in the ADC sequencer
ADCSequenceStepConfigure(ADC0_BASE, 1, 0, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 1, 1, ADC_CTL_TS);
ADCSequenceStepConfigure(ADC0_BASE, 1, 2, ADC_CTL_TS);
//Sample the temperaturesensor(ADC_CTL_TS)and configure the interrupt flag(ADC_CTL_IE)
//to be set when the sample is done. Tell the ADC logic that this is the last conversion on sequencer(ADC_CTL_END)
ADCSequenceStepConfigure(ADC0_BASE,1,3,ADC_CTL_TS|ADC_CTL_IE|ADC_CTL_END);
//Enable ADC sequencer 1.
ADCSequenceEnable(ADC0_BASE, 1);
ADCIntClear(ADC0_BASE, 1);
ADCProcessorTrigger(ADC0_BASE, 1);
while(!ADCIntStatus(ADC0_BASE, 1, false)) //Unknown time in this loop.
{
}
ADCSequenceDataGet(ADC0_BASE, 1, ui32ADC0Value);
ui32TempAvg = (ui32ADC0Value[0] + ui32ADC0Value[1] + ui32ADC0Value[2] + ui32ADC0Value[3] + 2)/4;
//Allows for whole number temperature. Does not allow for floating point values. Look at data sheet
ui32TempValueC = (1475 - ((2475 * ui32TempAvg)) / 4096)/10;
//ui32TempValueF = ((ui32TempValueC * 9) + 160) / 5;
ui32TempValueF = (ui32TempValueC * 9/5) + 32;
while(true)
{
//Check if we have received some bytes.
if(RingBufEmpty(&rxRingBuf) == false)
{
// Did we receive enough bytes that it could be a message? In the real world you
// need more complicated logic for multiple messages but this works for now.
if(RingBufUsed(&rxRingBuf) == sizeof(message_t))
{
message_t receivedMsg;
// Read the bytes into our received message structure.
RingBufRead(&rxRingBuf, (uint8_t *)&receivedMsg, sizeof(message_t));
// At this point we have a complete message. Note that bytes would have
// to be endian swapped if it werent for the fact this is Little-Endian host
// to little-endian host.
bool stop;
stop = true;
UARTprintf("Temperature received is %d\n",receivedMsg.tempF);
}
}
}
}