Interrupt / Stack Pointers / PIC Microcontroller - embedded

I am currently working with the PIC16F1829 micro controller. However, I am stuck on interrupt routine appropriate execution method. I want the interrupt routine to exit out of the infinite loop in all of the functions (LED animations), that are listed as different switch cases in the main() superloop. Should I use a global variable that the function would check and change it after interrupt flag has been detected or can I just use the stack pointers for this purpose? I have currently the ISR written using the stack pointer manipulation, but could you please let me know if this is the good approach?
void interrupt ISR(void){
if (TMR0IF && TMR0IE){ // (16MHz / (4 * 16 * 256)) [ 1.024 ms ]
TMR0IF = 0; // Clear Overflow Flag
if (++counter > 4531){ // 19531 * 1.024 ms [ ~ 20 sec ]
LED_Animation++; // Change the animation at a fixed time interval
counter = 0;
while (STKPTR > 1){ // STKPTR incremented due to an Interrupt
TOSH = 0; // TOSH:TOSL points to the Top of a Stack [ 0x00 ]
TOSL = 0;
STKPTR--; // Current value of the Stack Pointer
}
}
}
}
void main(){
STKPTR++; // Save this location in the stack
Sys_Init();
while(1){
switch(LED_Animation) {
case 0: LED_LizzardFeed(); break;
case 1: LED_Paparazzi(); break;
case 2: LED_FillnClear(); break;
case 3: LED_Slide(); break;
case 4: LED_MeetUp(); break;
case 5: LED_Chaser(); break;
default: LED_Animation = 0;
}
}

Your implementation using stack frame manipulation does not work on a PIC16F1829. These controllers use a separate memory space for the call stack.
To implement what you have suggested would require saving a copy of the 32-level call stack to the general RAM memory and restoring a copy of the 32-level call stack for the next routine in the animations list. This is possible be seems a bit too inefficient.
Please study the concept of protothreads, the are a useful method to achieve concurrent behavior in a resource limited controllers on a single execution thread.

Related

Best practice for buffering data to be sent on UART [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I'm working on an embedded project using an STM32F7 device, writing bare metal C.
I want to be able to send data to a UART at any point in the program for debugging purposes, without blocking while the data is sent. I'm using DMA to try to minimise the cpu time used for this.
Currently I'm filling the data into a FIFO queue, and then initiating a DMA request to send the data directly from the FIFO queue to the UART.
The issue with this is I can't set up the DMA to read from both the start and end of the FIFO buffer, in the case where the middle of the FIFO is unused and a message wraps from the end of the buffer to the start.
The two solutions to this would be to set up the first DMA request to read from the head of the FIFO to the end of the buffer, and then once that is complete, read from the start of the buffer to the tail of the FIFO.
The other way to do it would be to memcpy() out the bytes to be sent to another buffer, where they are all sequential, then initiate a single DMA request to send all the data at once.
Both of these would probably work but I'm looking for insight on what the best approach would be here.
The implementation I've usually chosen is similar to what you have proposed:
The logging functions creates a text and adds it to circular buffer.
DMA is used for the UART transmission. DMA is setup to send a contiguous chunk of data.
Whenever the DMA finishes, an interrupt is triggered. It first frees up the transmitted data in the circular buffer. Then it checks if more data needs to be transmitted. If so, it is immediately started again with new data.
Pseudo code:
tx_len = 0;
void log_message(const char* msg)
{
circ_buf_add(msg);
start_tx();
}
void start_tx()
{
if (tx_len > 0)
return; // already transmitting
const char* start;
int len;
circ_buf_get_chunk(&start, &tx_len);
if (tx_len == 0)
return;
uart_tx_dma(start, tx_len);
}
void dma_interrupt_handler()
{
circ_buf_remove(tx_len);
tx_len = 0;
start_tx();
}
It usually makes sense to limit the length of the transmitted chunk. The shorter it is, the sooner space is freed up in the circular buffer.
The examples proposed so far are fire and forget. In the case where your code needs to know if the data has been send. We have used the following structure in which the fifo holds structs pointing to the data.
This way your data is held by the code sending it. It is able to monitor the transmission but it is also responsible for not using the data until the transmission is complete.
A different advantage is that you do not have to allocate a buffer in advance. Only two pointers are required to point to the start en end of the linked list structure.
Some meta code:
enum transmission_state {
Unused,
WaitToBeSend,
Sending,
Done,
Error // Optional but handy
}
struct data_to_send
{
// Point to your data.
data* data_pointer;
// Set the length of your data.
int length;
// What is the current state of this transmission.
transmission_state state;
// Pointer to the next data to be send creating a linked list.
// Only have the send and dma functions use this.
data_to_send* next;
};
// Definition of the fifo.
data_to_send* fifo_first = null;
data_to_send* fifo_end = null;
// Use this function in your code to add data to be send.
void send(data_to_send* dts)
{
if(null == fifo_first) {
fifo_first = dts;
fifo_end = dts;
dts.next = null;
start_dma_transfer(fifo_first);
}
else {
fifo_end.next = dts;
fifo_end = dts;
dts.state = WaitToBeSend;
dts.next = null;
}
};
// Start a transfer.
void start_dma_transfer(data_to_send* dts)
{
dts->state = Sending;
// Do some DMA stuff to start the transmission.
dma_transfer(dts->data, dts->length)
}
// The interrupt handler called when the dma is done.
void dma_interrupt_handler()
{
fifo_first->state = Done;
if(null != fifo_first->next) {
// Send the next data.
fifo_first = fifo_first->next;
start_dma_transfer(fifo_first);
}
else {
// No new data to be send.
fifo_first = null;
fifo_end = null;
}
}
int main()
{
// Setup a transmission
byte data[3] = {x,y,z};
data_to_send transmission = default_dts; // Set to some default.
transmission.data = data;
transmission.length = 3;
send(&transmission);
// Do other important things.
// Later periodically check the transmission.
if(Done == transmission.status) {
// You could use the data for something else or send new data.
}
}
This structure can also be used for I2C and SPI in which case you can add responses to the data_to_send struct and check for a response and act upon it.

Transmit complete call back not getting called in UART DMA

What I'm trying to do is fairly simple. Transmit through DMA and wait till it gets transmitted. And then receive and wait till it is received.
When I comment out the receive part(including the call back), it is going into the transmit complete call back function. But when I un-comment the receive portion, it is not going into the tx cplt call back and it is directly going into the receive cplt callback. And when I check the receive buffer I'm not getting what I expected(obviously). What could have gone wrong?
I'm using Atollic True Studio V 9.0 , CubeMx v5.1.0, STM32F407VG-DISC1 board and enabled DMA for UART2.
I've tried sending char buffer through UART DMA and receive it. It seems it is not transmitting at all as it is not going into txCplt call back. And it is directly going into Rxcplt call back.
uint8_t tx_arr[10], rx_arr[10];
__IO ITStatus UartReady = RESET;
int main(void)
{
int i = 0;
for(i = 0; i<10; i++)
rx_arr[i] = 0;
for(i = 0; i<10; i++)
tx_arr[i] = i*2;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART6_UART_Init();
MX_USART2_UART_Init();
while (1)
{
if( HAL_UART_Transmit_DMA(&huart2, (uint8_t*)tx_arr, 10)!= HAL_OK )
{
Error_Handler();
}
while(UartReady != SET)
{
}
UartReady = RESET;
if( HAL_UART_Receive_DMA(&huart2, (uint8_t*)rx_arr, 10)!= HAL_OK )
{
Error_Handler();
}
while(UartReady != SET)
{
}
UartReady = RESET;
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
UartReady = SET;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
UartReady = SET;
}
I expect the rx_arr will get filled by 0,2,4,6,...18 but it is getting filled with junk
As this looks to me, the reason is that you are using the same flag variable from both ISRs, both times doing busy waiting in your main loop.
If you uncomment both handler actions, you will sooner or later end up with a race condition where both handlers put their "SET" value quickly one by one - before the main loop waits for it. Then, the main loop "consumes" this flag by setting the variable back to "RESET". A few lines later, the other waiting loop comes and isn't served (because both ISRs ran earlier and only left a single "SET" value, one overwriting the other). Then, your main loop is stuck.
In order to verify my assumption, you can activate a one-sided watchdog before entering into the main loop, and trigger it every main loop cycle. If the main loop gets stuck as I assume, you will detect that the reset cause points to the watchdog afterwards.

Write UART on PIC18

I need help with the uart communication I am trying to implement on my Proteus simulation. I use a PIC18f4520 and I want to display on the virtual terminal the values that have been calculated by the microcontroller.
Here a snap of my design on Proteus
Right now, this is how my UART code looks like :
#define _XTAL_FREQ 20000000
#define _BAUDRATE 9600
void Configuration_ISR(void) {
IPR1bits.TMR1IP = 1; // TMR1 Overflow Interrupt Priority - High
PIE1bits.TMR1IE = 1; // TMR1 Overflow Interrupt Enable
PIR1bits.TMR1IF = 0; // TMR1 Overflow Interrupt Flag
// 0 = TMR1 register did not overflow
// 1 = TMR1 register overflowed (must be cleared in software)
RCONbits.IPEN = 1; // Interrupt Priority High level
INTCONbits.PEIE = 1; // Enables all low-priority peripheral interrupts
//INTCONbits.GIE = 1; // Enables all high-priority interrupts
}
void Configuration_UART(void) {
TRISCbits.TRISC6 = 0;
TRISCbits.TRISC7 = 1;
SPBRG = ((_XTAL_FREQ/16)/_BAUDRATE)-1;
//RCSTA REG
RCSTAbits.SPEN = 1; // enable serial port pins
RCSTAbits.RX9 = 0;
//TXSTA REG
TXSTAbits.BRGH = 1; // fast baudrate
TXSTAbits.SYNC = 0; // asynchronous
TXSTAbits.TX9 = 0; // 8-bit transmission
TXSTAbits.TXEN = 1; // enble transmitter
}
void WriteByte_UART(unsigned char ch) {
while(!PIR1bits.TXIF); // Wait for TXIF flag Set which indicates
// TXREG register is empty
TXREG = ch; // Transmitt data to UART
}
void WriteString_UART(char *data) {
while(*data){
WriteByte_UART(*data++);
}
}
unsigned char ReceiveByte_UART(void) {
if(RCSTAbits.OERR) {
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
while(!PIR1bits.RCIF); //Wait for a byte
return RCREG;
}
And in the main loop :
while(1) {
WriteByte_UART('a'); // This works. I can see the As in the terminal
WriteString_UART("Hello World !"); //Nothing displayed :(
}//end while(1)
I have tried different solution for WriteString_UART but none has worked so far.
I don't want to use printf cause it impacts other operations I'm doing with the PIC by adding delay.
So I really want to make it work with WriteString_UART.
In the end I would like to have someting like "Error rate is : [a value]%" on the terminal.
Thanks for your help, and please tell me if something isn't clear.
In your WriteByte_UART() function, try polling the TRMT bit. In particular, change:
while(!PIR1bits.TXIF);
to
while(!TXSTA1bits.TRMT);
I don't know if this is your particular issue, but there exists a race-condition due to the fact that TXIF is not immediately cleared upon loading TXREG. Another option would be to try:
...
Nop();
while(!PIR1bits.TXIF);
...
EDIT BASED ON COMMENTS
The issue is due to the fact that the PIC18 utilizes two different pointer types based on data memory and program memory. Try changing your declaration to void WriteString_UART(const rom char * data) and see what happens. You will need to change your WriteByte_UART() declaration as well, to void WriteByte_UART(const unsigned char ch).
Add delay of few miliseconds after line
TXREG = ch;
verify that pointer *data of WriteString_UART(char *data) actually point to
string "Hello World !".
It seems you found a solution, but the reason why it wasn't working in the first place is still not clear. What compiler are you using?
I learned the hard way that C18 and XC8 are used differently regarding memory spaces. With both compilers, a string declared literally like char string[]="Hello!", will be stored in ROM (program memory). They differ in the way functions use strings.
C18 string functions will have variants to access strings either in RAM or ROM (for example strcpypgm2ram, strcpyram2pgm, etc.). XC8 on the other hand, does the job for you and you will not need to use specific functions to choose which memory you want to access.
If you are using C18, I would highly recommend you switch to XC8, which is more recent and easier to work with. If you still want to use C18 or another compiler which requires you to deal with program/data memory spaces, then here below are two solutions you may want to try. The C18 datasheet says that putsUSART prints a string from data memory to USART. The function putrsUSART will print a string from program memory. So you can simply use putrsUSART to print your string.
You may also want to try the following, which consists in copying your string from program memory to data memory (it may be a waste of memory if your application is tight on memory though) :
char pgmstring[] = "Hello";
char datstring[16];
strcpypgm2ram(datstring, pgmstring);
putsUSART(datstring);
In this example, the pointers pgmstring and datstring will be stored in data memory. The string "Hello" will be stored in program memory. So even if the pointer pgmstring itself is in data memory, it initially points to a memory address (the address of "Hello"). The only way to point to this same string in data memory is to create a copy of it in data memory. This is because a function accepting a string stored in data memory (such as putsUSART) can NOT be used directly with a string stored in program memory.
I hope this could help you understand a bit better how to work with Harvard microprocessors, where program and data memories are separated.

what does mean by __even_in_range(UCA0IV,0x08)

I found this part from the example code for UART for MSP430FR57xx from TI.
I don't understand what __even_in_range(UCA0IV,0x08) does mean?
#pragma vector=USCI_A0_VECTOR
__interrupt void USCI_A0_ISR(void)
{
switch(__even_in_range(UCA0IV,0x08))
{
case 0:break; // Vector 0 - no interrupt
case 2: // Vector 2 - RXIFG
while (!(UCA0IFG&UCTXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF = UCA0RXBUF; // TX -> RXed character
break;
case 4:break; // Vector 4 - TXIFG
default: break;
}
}
The interrupt vector register never has a value that is odd, or larger than eight. So if you tell the compiler about this, it is possible to generate slightly more efficient code by leaving out the checks for those values, and by implementing the cases with a simple jump table.

OSX Serial read freeze / hang

I'm writing a serial communication wrapper class in Objective-C. To list all serial available modems and setup the connection I'm using pretty much the same code as used in this example project by Apple.
I could read and write the ways apple does it. But I want to implement a loop on a second thread and write to the stream if a NSString *writeString longer 0 and read after write if bytes are available.
I got writing working quite straight forward. I just used the write function declared in unistd.h.
Reading will not work. Whenever I call read(), the function hangs and my loop does not proceed.
Here is the code used in my loop:
- (void)runInCOMLoop {
do {
// write
} while (bytesWritten < strlen([_writeString UTF8String]));
NSMutableString *readString = [NSMutableString string];
ssize_t bytesRead = 0;
ssize_t readB = 0;
char buffer[256];
do {
readB = read(_fileDescriptor, &buffer, sizeof(buffer));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this function hangs
bytesRead += readB;
if (readB == -1 {
// error
}
else if (readB > 0) {
if(buffer[bytesRead - 1] == '\r' ]] buffer[bytesRead - 1] == '\n') {
break;
}
[readString appendString:[NSString stringWithUTF8String:buffer]];
}
} while (readB > 0);
What am I doing wrong here?
read() will block if there is nothing to read. Apple probably has their own of doing things, but you can use select() to see if there is anything to read on _fileDescriptor. Google around for examples on how to use select.
Here's one link on StackOverflow:
Can someone give me an example of how select() is alerted to an fd becoming "ready"
This excerpt from the select man is pertains:
To effect a poll, the timeout argument should be
non-nil, pointing to a zero-valued timeval structure. Timeout is not
changed by select(), and may be reused on subsequent calls, however it is
good style to re-initialize it before each invocation of select().
You can set the non-blocking flag (O_NONBLOCK) on the file descriptor using fcntl() to keep read() from waiting for data, but if you do that, you have to continuously poll looking for data, which is obviously bad from a CPU usage standpoint. As Charlie Burns' answer explains, the best solution is to use select() which will allow your program to efficiently wait until there is some data to be read on the port's file descriptor. Here's some example code taken from my own Objective-C serial port class, ORSSerialPort (slightly modified):
fd_set localReadFDSet;
FD_ZERO(&localReadFDSet);
FD_SET(self.fileDescriptor, &localReadFDSet);
timeout.tv_sec = 0;
timeout.tv_usec = 100000; // Check to see if port closed every 100ms
result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
if (!self.isOpen) break; // Port closed while select call was waiting
if (result < 0) {
// Handle error
}
if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet)) continue;
// Data is available
char buf[1024];
long lengthRead = read(localPortFD, buf, sizeof(buf));
NSData *readData = nil;
if (lengthRead>0) readData = [NSData dataWithBytes:buf length:lengthRead];
Note that select() indicates that data is available by returning. So, your program will sit suspended at the select() call while no data is available. The program is not hung, that's how it's supposed to work. If you need to do other things while select() is waiting, you should put the select() call on a different queue/thread from the other work you need to do. ORSSerialPort does this.