stm32和外設通信的時候,需要對外設發(fā)來的串行數據做同步。參考過下面這個鏈接的方法:串口通信幀的同步方法(識別一幀數據的起始結束)
- FIFO隊列的幀同步方法,比較簡單,準確度又高。
/**
* USART2_IRQHandler
*/
void USART2_IRQHandler(void)
{
uint8_t value = 0;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
value = USART_ReceiveData(USART2);
if(1 == syncFlag)
{
if(cnt <= 7)
{
pm_buf[cnt++] = value;
}
else
{
cnt = 0;
syncFlag = 0;
syncHead[1] = 0xFF;
syncHead[0] = 0xFF;
}
}
else
{
//Syncing......
syncHead[1] = syncHead[0];
syncHead[0] = value;
if(syncHead[1] == 0xAA && syncHead[0] == 0xC0)
syncFlag = 1;
}
}
}
然而這樣做有個問題。中斷服務程序需要做大量的工作,在這短暫的時間中,就可能會丟失串口發(fā)來的數據,因為串口的數據是源源不斷的。所以應該需要一個類似緩沖區(qū)的東西。這樣的話,IRQHandler只負責相應的數據,而中斷服務程序去完成真正的操作。
在查看很多文檔之后,發(fā)現這個就歸屬于經典的生產者和消費者問題。我認為FIFO隊列和循環(huán)緩沖區(qū)應該是一種互補的關系。
緩沖區(qū)的價值在于能使得生產者產生的數據不至于零散地只能接收到其中的一部分,比如AA C0 11 22 33 44 55 66,在AA C0同步幀同步完之后,不會說只收到了11 22 33 55 66,這就完全錯掉了。
-
如果有緩沖區(qū),像一個隊列一樣,先進先出,讀寫互相不干擾,寫優(yōu)先級高于讀,如果緩沖區(qū)滿了,就只好丟棄新產生的數據。
- 在我看來覆蓋老數據會有無法覆蓋同步的風險,比如,你不能保證剛好從同步幀頭那里覆蓋。因為要保證設備產生AA C0 的時候,剛好覆蓋到緩沖區(qū)的AA C0,這樣的工作量想必不小。
- 如果真的可能的話,從哪里覆蓋最老的數據是合適的呢?消費者正在讀數據,總不能把正要讀的數據給抹掉吧?
- 而且覆蓋了老數據,會產生時間線混亂的問題,本來隊列是從隊列頭到隊列尾是老數據>>>>新數據,這樣一來,完全被破壞了。
用了循環(huán)緩沖區(qū)之后,中斷里面就換成下面這樣的操作方式了,中斷只是簡單地向循環(huán)緩沖區(qū)寫入數據-如果可寫的話。
/**
* USART2_IRQHandler
*/
void USART2_IRQHandler(void)
{
uint8_t value = 0;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
value = USART_ReceiveData(USART2);
if(rb_can_write(&u_ring_buff) > 0)
{
rb_write(&u_ring_buff, &value, 1);
//printf("Interrupt\n\r");
}
}
}
- 參考的循環(huán)緩沖區(qū)實現的代碼:
#define min(a, b) (a)<(b)?(a):(b)
void rb_new(RingBuffer* rb)
{
//RingBuffer *rb = (RingBuffer *)buff;//malloc(sizeof(RingBuffer) + capacity);
//if (rb == NULL) return NULL;
rb->rb_capacity = MAX_RINGBUFFER_LEN;//-sizeof(RingBuffer);//capacity;
//rb->rb_buff = buff+sizeof(RingBuffer);//(char*)rb + sizeof(RingBuffer);
rb->rb_head = rb->rb_buff;
rb->rb_tail = rb->rb_buff;
//return rb;
};
void rb_free(RingBuffer *rb)
{
//free((char*)rb);
}
size_t rb_capacity(RingBuffer *rb)
{
//assert(rb != NULL);
return rb->rb_capacity;
}
size_t rb_can_read(RingBuffer *rb)
{
//assert(rb != NULL);
if (rb->rb_head == rb->rb_tail) return 0;
if (rb->rb_head < rb->rb_tail) return rb->rb_tail - rb->rb_head;
return rb_capacity(rb) - (rb->rb_head - rb->rb_tail);
}
size_t rb_can_write(RingBuffer *rb)
{
//assert(rb != NULL);
return rb_capacity(rb) - rb_can_read(rb);
}
size_t rb_read(RingBuffer *rb, void *data, size_t count)
{
//assert(rb != NULL);
//assert(data != NULL);
if (rb->rb_head < rb->rb_tail)
{
int copy_sz = min(count, rb_can_read(rb));
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head += copy_sz;
return copy_sz;
}
else
{
if (count < rb_capacity(rb)-(rb->rb_head - rb->rb_buff))
{
int copy_sz = count;
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head += copy_sz;
return copy_sz;
}
else
{
int copy_sz = rb_capacity(rb) - (rb->rb_head - rb->rb_buff);
memcpy(data, rb->rb_head, copy_sz);
rb->rb_head = rb->rb_buff;
copy_sz += rb_read(rb, (char*)data+copy_sz, count-copy_sz);
return copy_sz;
}
}
}
size_t rb_write(RingBuffer *rb, const void *data, size_t count)
{
//assert(rb != NULL);
//assert(data != NULL);
if (count >= rb_can_write(rb))
return -1;
if (rb->rb_head <= rb->rb_tail)
{
int tail_avail_sz = rb_capacity(rb) - (rb->rb_tail - rb->rb_buff);
if (count <= tail_avail_sz)
{
memcpy(rb->rb_tail, data, count);
rb->rb_tail += count;
if (rb->rb_tail == rb->rb_buff+rb_capacity(rb))
rb->rb_tail = rb->rb_buff;
return count;
}
else
{
memcpy(rb->rb_tail, data, tail_avail_sz);
rb->rb_tail = rb->rb_buff;
return tail_avail_sz + rb_write(rb, (char*)data+tail_avail_sz, count-tail_avail_sz);
}
}
else
{
memcpy(rb->rb_tail, data, count);
rb->rb_tail += count;
return count;
}
}
typedef struct {
size_t rb_capacity;
char *rb_head;
char *rb_tail;
char rb_buff[64];
}RingBuffer;
//struct RingBuffer;
// RingBuffer* rb_new(size_t capacity);
void rb_new(RingBuffer* rb);
void rb_free(RingBuffer *rb);
size_t rb_capacity(RingBuffer *rb);
size_t rb_can_read(RingBuffer *rb);
size_t rb_can_write(RingBuffer *rb);
size_t rb_read(RingBuffer *rb, void *data, size_t count);
size_t rb_write(RingBuffer *rb, const void *data, size_t count);
- 獲取新數據幀的代碼:
static void GetFrame()
{
if(rb_can_read(&u_ring_buff) >= 1)
{
if(1 == syncFlag)
{
if(cnt <= 7)
{
rb_read(&u_ring_buff, &curValue, 1);
//printf("cnt:%d\t",cnt);
pm_buf[cnt++] = curValue;
//printf("Interrupt_value:%2X\r\n\r\n",curValue);
}
else
{
//memset(pm_buf, 0, 8);
cnt = 0;
syncFlag = 0;
syncHead[1] = 0xFF;
syncHead[0] = 0xFF;
}
}
else
{
//printf("Syncing......\r\n");
rb_read(&u_ring_buff, &curValue, 1);
syncHead[1] = syncHead[0];
syncHead[0] = curValue;
if(syncHead[1] == 0xAA && syncHead[0] == 0xC0)
syncFlag = 1;
}
}
}
用了循環(huán)緩沖區(qū)后,發(fā)現用到一段時間后,之前還能正常提取數據的數據幀,過了一段時間數據就亂掉了,邏輯沒搞清楚嗎?