Hello Here is my USART's settings:
USART_InitTypeDef USAR; GPIO_InitTypeDef GPIOStruc; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE); GPIOStruc.GPIO_Mode=GPIO_Mode_AF_PP ; GPIOStruc.GPIO_Speed=GPIO_Speed_50MHz; GPIOStruc.GPIO_Pin=GPIO_Pin_6; GPIO_Init(GPIOB,&GPIOStruc); GPIOStruc.GPIO_Mode=GPIO_Mode_IN_FLOATING ; GPIOStruc.GPIO_Pin=GPIO_Pin_7; GPIO_Init(GPIOB,&GPIOStruc); USAR.USART_BaudRate=115200; USAR.USART_StopBits=USART_StopBits_1; USAR.USART_WordLength=USART_WordLength_8b; USAR.USART_Parity=USART_Parity_No ; USAR.USART_HardwareFlowControl=USART_HardwareFlowControl_None; USAR.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1,&USAR); USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); NVIC_EnableIRQ(USART1_IRQn); USART_Cmd(USART1,ENABLE);
and I send 200 characters with printf function each 20ms(that created by timer).
timer settings
TIM_TimeBaseInitTypeDef TimeStruct; NVIC_InitTypeDef nvicStructure; nvicStructure.NVIC_IRQChannel = TIM2_IRQn; nvicStructure.NVIC_IRQChannelPreemptionPriority = 0; nvicStructure.NVIC_IRQChannelSubPriority = 1; nvicStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TimeStruct.TIM_Prescaler=7200; TimeStruct.TIM_Period=200; TimeStruct.TIM_ClockDivision=TIM_CKD_DIV1; TimeStruct.TIM_CounterMode= TIM_CounterMode_Up ; TimeStruct.TIM_RepetitionCounter =0; TIM_TimeBaseInit(TIM2, &TimeStruct); TIM_Cmd(TIM2, ENABLE); TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
printf("LOng string......");
struct __FILE { int handle;} ; FILE __stdout; FILE __stdin; FILE __stderr; int fputc(int ch, FILE *f) { while(!USART_GetFlagStatus(USART1,USART_FLAG_TXE)); USART_SendData(USART1,ch); return ch;
but when I send a 120 characters string to my device it doesn't do anything
void USART1_IRQHandler(void){ if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { temp=USART_ReceiveData(USART1); } }
but when I increase timer step (for example 100ms) it works fine. what's wrong with it ? what is my mistake?
200 characters every 20 ms means 10k characters/second.
115200 baud and 1+8+1 bits means 11520 maximum characters/second.
But then you need to check the datasheet (or an oscilloscope) how soon after the end of a stop bit that the UART may send a start bit. Not all UART can throw in a new start bit directly after the stop bit ends, in which case the print data may take more than one timer clock period.
Thank you Per Westermark I limit my volume of data characters to 80bytes so it means 4Kbyte/second.However I have the same problem.(Actually I changed baud-rate to 460800 but it doesn't work again)
So it means I always miss some characters while my device is in sending mode with any baud rate?
My MCU is stm32f10x that support baud-rate up to 4500000 and unfortunately I can't find the time between start and stop bit.
Disconnected fragments of code. You should probably buffer the data, both in and out, and evaluate if your problem is with overwhelming the buffer, ie filling faster than it empties.
The input and output of the USART are independent, really not making a lot of sense of what the problem is here. I wouldn't printf() from an IRQ into a polling loop. Fill output buffer from TIM and leave.
Thank you Westonsupermare Pier Here is my main code
#include <stdio.h> #include <string.h> #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stm32f10x_usart.h" //############################## unsigned char flag; unsigned char stat; unsigned char pos; struct __FILE { int handle;} ; FILE __stdout; FILE __stdin; FILE __stderr; int fputc(int ch, FILE *f) { while(!USART_GetFlagStatus(USART1,USART_FLAG_TXE)); USART_SendData(USART1,ch); return ch; } //############################## int main(){ USARTConfig(); Tim(); flag=0; stat=0; while(1){ //if(flag==1){some code} } } void USARTConfig(void){ USART_InitTypeDef USAR; GPIO_InitTypeDef GPIOStruc; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE); GPIOStruc.GPIO_Mode=GPIO_Mode_AF_PP ; GPIOStruc.GPIO_Speed=GPIO_Speed_50MHz; GPIOStruc.GPIO_Pin=GPIO_Pin_6; GPIO_Init(GPIOB,&GPIOStruc); GPIOStruc.GPIO_Mode=GPIO_Mode_IN_FLOATING ; GPIOStruc.GPIO_Pin=GPIO_Pin_7; GPIO_Init(GPIOB,&GPIOStruc); USAR.USART_BaudRate=115200; USAR.USART_StopBits=USART_StopBits_1; USAR.USART_WordLength=USART_WordLength_8b; USAR.USART_Parity=USART_Parity_No ; USAR.USART_HardwareFlowControl=USART_HardwareFlowControl_None; USAR.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1,&USAR); USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); NVIC_EnableIRQ(USART1_IRQn); USART_Cmd(USART1,ENABLE); } void USART1_IRQHandler(void){ if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { temp=USART_ReceiveData(USART1); if(temp==65){ stat=1; pos=0; } else if(temp!=66 && stat==1){ bufferu[pos]=temp; pos+=1; } else if(temp==66 && stat==1) { stat=0; flag=1; } } } void Tim(void){ TIM_TimeBaseInitTypeDef TimeStruct; NVIC_InitTypeDef nvicStructure; nvicStructure.NVIC_IRQChannel = TIM2_IRQn; nvicStructure.NVIC_IRQChannelPreemptionPriority = 0; nvicStructure.NVIC_IRQChannelSubPriority = 1; nvicStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicStructure); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); TimeStruct.TIM_Prescaler=7200; TimeStruct.TIM_Period=200; TimeStruct.TIM_ClockDivision=TIM_CKD_DIV1; TimeStruct.TIM_CounterMode= TIM_CounterMode_Up ; TimeStruct.TIM_RepetitionCounter =0; TIM_TimeBaseInit(TIM2, &TimeStruct); TIM_Cmd(TIM2, ENABLE); TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); } void TIM2_IRQHandler(){ if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); } }
1)what changes make it correct? 2)Is not it related to NVIC Priority? 3)when input and output are independent why doesn't it do any reaction(doesn't call USART1_IRQHandler)?
Thanks
The receive code should use two buffers so the main loop can spend time on one message while the receive interrupt is already ready to receive the next message.
With a single receive buffer, there must be a pause after the last character is received until main has released the buffer so the ISR may start to fill it with the next message.
Just another note - your receive interrupt will overwrite memory if the end-of-message character is missed or if someone is evil. You need to always make sure there is still room for more data in the receive buffer before you insert more data.
Thank you Per Westermark I did what you said but I've lose some characters again. I'm searching and doing some correction and I will tell you more later. Thank you a zillion
So which characters do you lose? The first in a message? The last in a message? Some characters at random positions? Some other pattern?
Another thing: Any global variable that you update from an ISR and that you use outside of the ISR must (!) be declared volatile, to tell the compiler that they can be asynchronously updated.
Without marking them as volatile, the main loop may not know that the ISR has flagged the complete reception of a message, because the main loop code doesn't perform any new memory accesses to check the current value of the variables - the compiler assumes that it's enough to read the variable once since it doesn't see any assign statements that may change them.
Dear Per Westermark Thanks for your good explanation Actually I have been receiving characters random and I have been missing some of characters in any position.So here is my main code in my interrupt function
volatile unsigned char bitpos; volatile unsigned long int stat; volatile unsigned long int stat1; unsigned char bufferu[100]; unsigned char buffer1[100]; volatile unsigned char temp; volatile unsigned char flag; volatile unsigned char dat; void USART1_IRQHandler(void){ if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { dat=1; temp=USART_ReceiveData(USART1); if(temp==65){ stat=1; bitpos=0; memset(&bufferu[0], '\0', sizeof(bufferu)); } else if(temp!=66 && stat==1 && temp>47 && temp<58){ bufferu[bitpos]=temp; bitpos+=1; } else if(temp==66 && stat==1) { stat=0; memset(&buffer1[0], '\0', sizeof(buffer1)); memset(buffer1,bufferu ,sizeof(bufferu)); flag=1; }else if(stat==1 && bitpos>100){ bitpos=0; stat=0; memset(&bufferu[0], '\0', sizeof(bufferu)); } } }
As you see I can't use volatile for my bufferu and buffer1 variables because memset & memset don't work properly.It's good idea to write a new function that copy two arrays and erase array variables in interrupt function? or any other approach to make these variable volatile.
Thanks a Zillion for your contributions
Here is my code
volatile unsigned char bitpos; volatile unsigned long int stat; volatile unsigned long int stat1; volatile unsigned char bufferu[100]; volatile unsigned char buffer1[100]; volatile unsigned char temp; volatile unsigned char flag; volatile unsigned char dat; void USART1_IRQHandler(void){ if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { dat=1; temp=USART_ReceiveData(USART1); if(temp==65){ stat=1; bitpos=0; Erase(); } else if(temp!=66 && stat==1 && temp>47 && temp<58){ bufferu[bitpos]=temp; bitpos+=1; } else if(temp==66 && stat==1) { stat=0; Erase1(); COPY(); flag=1; }else if(stat==1 && bitpos>100){ bitpos=0; stat=0; Erase(); } } } void Erase(void){ char i; for(i=0;i<100;i++){bufferu[i]='\0';} } void Erase1(void){ char i; for(i=0;i<100;i++){buffer1[i]='\0';} } void COPY(void){ char i; for(i=0;i<100;i++){buffer1[i]=bufferu[i];} }
and my timer code:
void TIM2_IRQHandler(){ if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { if(dat==0){ //some calculations before send printf("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"); } else { dat=dat-1; } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } }
but in my main code
int main(void){ if(flag==1){ TIM_Cmd(TIM2, DISABLE); printf("H%sC\n\r",buffer1); delay(); flag=0; TIM_Cmd(TIM2, ENABLE); }
but still I have been missing a string or some characters.
Actually my main code contain a loop
int main(){ USARTConfig(); Tim(); flag=0; stat=0; dat=0; while(1){ if(flag==1){ TIM_Cmd(TIM2, DISABLE); printf("H%sC\n\r",buffer1); delay(); flag=0; TIM_Cmd(TIM2, ENABLE); } }
When using double-buffering, you shouldn't do any memset() or memcpy() in the ISR - you instead use two pointers to the buffers. It's enough to swap the value of the two pointers.
Next thing - why zero the buffer when starting to receive a new message? It's enough to keep track of the insert position until you have a complete message in which case you can hand over that message. You can even hand over the exact packet length if you want.
And why not use variable names that actually means anything? Why "stat" instead of something like "is_receiving_packet"? Why "flag" instead of something like "have_packet_ready"?
Note also that unless your code allows nested interrupts, a long function call in TIM2_IRQHandler() (such as the printf()) can create too much latency so USART1_IRQHandler() doesn't get called before you get an overrun in the UART and lose characters.
You still doesn't check the bitpos value to figure out if there is room for more data in the buffer.
Thanks dear Per Westermark
would it be possible to give me an example of "you instead use two pointers to the buffers. It's enough to swap the value of the two pointers."?
because I've wanted to set the first character's position at the first position of my array and prevent from overflow.(but it can be solved easily if I can save my characters)
Why "stat" instead of something like "is_receiving_packet"? Why "flag" instead of something like "have_packet_ready"?
Actually I've wanted to change their name for this forum and after my code works but I was too lazy and didn't have much more time such as before.(I will do)
would it possible to know your idea about "how can I send a string each 20ms without less latency" ? I've changed my code as shown below.but it doesn't work as the old one.
void TIM2_IRQHandler(){ if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { if(dat==0){ //some calculations before send send(); } else { dat=dat-1; } TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } void send(void){ char msg[200]; int i; sprintf(msg,"AAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBAAAAAAAAAAAAAAACCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDIIIIIIIIIIUUUUUUU"); for(i=0;i<150;i++){ if(msg[i]=='\0')break; while(!USART_GetFlagStatus(USART1,USART_FLAG_TXE)); if(dat!=0){break;} USART_SendData(USART1,msg[i]); } }
char buf1[BUFSIZE]; char buf2[BUFSIZE]; volatile unsigned delivered_msg_size = 0; volatile char *delivered_msg = NULL; volatile char *tmp_msg = buf1; void loop() { for (;;) { if (delivered_msg) { process_msg(delivered_msg,delivered_msg_size); delivered_msg = NULL; } ... } } void uart_irq() { static unsigned insert_pos = 0; static unsigned in_packet = FALSE; ... if (c == START_CHAR) { insert_pos = 0; in_packet = TRUE; } else if (!in_packet) { // ignore char } else if (c == END_CHAR) { if (delivered_msg) { // main loop hasn't processed data fast enough... // throw away this new packet! } else { delivered_msg_size = insert_pos; delivered_msg = tmp_msg; tmp_msg = (tmp_msg == buf1) ? buf2 : buf1; } in_packet = FALSE; } else if (!valid_char(c)) { // invalid data - fail packet in_packet = FALSE; } else if (insert_pos >= BUFSIZE) { // too much data - fail packet in_packet = FALSE; } else { tmp_msg[insert_pos++] = c; } ... }
Reserve one additional position in the buffer and zero-terminate just before hand-over, in case you want the final buffer to be zero-terminated so you can directly print it.
Then you can also decide to not hand over any message size.