问题确定串口接收数据丢失,用串口工具发送一串字符数据时,有时会出现会丢失一两个数据。
可能原因1:cpu处理速度过慢,在下一次中断来临时,此次中断还未处理完。
解决方法:使用ringbuffer
void USART0_IRQHandler( void ) interrupt 4 using 2
{
uint8_t count;
if(SFR_RI)
{
// char temp_space = ' ';
buffer_push(SFR_SBUF0);
SFR_RI = 0;
rev_flag = 0;
// RxTimeout = 50;
}
if(SFR_TI)
{
SFR_TI = 0;
gucUart0BusyFlg = 0;
}
}
#define BUFFER_MAX 64
typedef struct _circle_buffer{
unsigned char head_pos;
unsigned char tail_pos;
unsigned char circle_buffer[BUFFER_MAX];
}circle_buffer;
circle_buffer buffer;
void buffer_init(void)
{
buffer.head_pos = buffer.tail_pos = 0;
}
uint8_t buffer_pop(unsigned char *_buf)
{
if(buffer.head_pos == buffer.tail_pos)
{
*_buf = 0xFF;
return 0;
}
else
{
*_buf = buffer.circle_buffer[buffer.head_pos];
if(++buffer.head_pos>=BUFFER_MAX)
buffer.head_pos = 0;
}
return 1;
}
void buffer_push(unsigned char _buf)
{
buffer.circle_buffer[buffer.tail_pos] = _buf;
if(++buffer.tail_pos >= BUFFER_MAX)
buffer.tail_pos = 0;
if(buffer.tail_pos == buffer.head_pos)
if(++buffer.head_pos >= BUFFER_MAX)
buffer.head_pos = 0;
}
主程序负责将数据拿出来进行处理
while(1)
{
while(!rev_flag)
{
rev_flag = 1; 这是在中断中设置了一个标志位,当在20ms内有数据发送过来,则继续接收数据。当20ms没数据进来,则对数据进行处理
delay(20);
}
if(buffer_pop(&tmp))
{
putchar(tmp);
}
}
可能原因2:优先级过低,同时几个中断到来时,处理别的中断
SFR_IP = 0x10; 本次使用串口0,将串口0对应优先级位设置为1.
可能原因3:一次中断同时传进来几个数,而只进行了一次接收工作,这个应该是在有fifo时才会有的情况。
如同:串口用的方式不对,你每接收一个数据需要进一次中断,实际上应该是进一次中断把所有数据接收完毕。通过一个超时判断判断是否后续还有数据,没有数据了才退出中断函数。
解决办法3:可以适当增大fifo阈值,循环判断fifo中是否还有数据待取出。直到空为止。
用一个while(timeout<=TIMEOUT)的循环做超时判断。接到数据则清零timeout,具体的TIMEOUT时间需要查看时序表,应该在1、2ms左右。
知识点四:串口有缓冲区的,你只要发送之前判断一下发送缓冲区是否空,如果未空则将数据发送即可。(注意不是发送寄存器的状态位)。
硬件上会帮你做掉后面的事情:它会将缓冲区的值先交给发送寄存器,然后才发出去。
而你在查询的标志位如果是发送寄存器的标志位,你的时间就浪费在要等它硬件“把缓冲区的值放到发送寄存器+发送出去”这两步上面。
1)0.2ms发送一个数据就是1S发送5000,如果你的波特率是9600,那你的0.2ms根本起不到任何测试作用。应该用一连串的数据通过串口工具进行发送测试。
2)数据需要加校验,“接收数据+判断数据正确性+处理数据”应该在下一次数据来之前处理完毕。
3)中断函数里一般只做接收,并不做数据处理。你把数据反着发送回去就是把处理放在了中断函数里。
知识点5:串行口中有两个缓冲寄存器SBUF,一个是发送寄存器,一个是接收寄存器,在物理结构上是完全独立的。它们都是字节寻址的寄存器,字节地址均为99H。这个重叠的地址靠
读/写指令区分:串行发送时,CPU向SBUF写入数据,此时99H表示发送SBUF;串行接收时,CPU从SBUF读出数据,此时99H表示接收SBUF。51单片机的缓冲区只有八位。
https://wenku.baidu.com/view/062f262acfc789eb172dc835.html
说最基本的,老的51单片机(80C51系列)有5个中断源,2个优先级,可以实现二级中断服务嵌套。现在很多扩展的51单片机已经有4个优先级(或更多)和更多的中断源了。
在说到中断之前,我先来定义一下优先级,明白了什么是优先级,后面的阐述就容易明白了。实际上很多人都是混淆了优先级的含义,所以才觉得糊里糊涂。
中断的优先级有两个:查询优先级和执行优先级。
什么是查询优级呢?我们从datasheet或书上看到的默认(IP寄存器不做设置,上电复位后为00H)的优先级:
外部中断0 > 定时/计数器0 > 外部中断1 > 定时/计数器1 > 串行中断
或 int0,timer0,int1,timer1,serial port 或 INT0、T0、INT1、T1、UART
或 PX0>PT0>PX1>PT1>PS>……
其实都是查询优级。首先查询优先级是不可以更改和设置的。这是一个中断优先权排队的问题。是指多个中断源同时产生中断信号时,中断仲裁器选择对哪个中断源优先处理的顺序。而这与是否发生中断服务程序的嵌套毫不相干。当CPU查询各个中断标志位的时候,会依照上述5个查询优先级顺序依次查询,当数个中断同时请求的时候,会优先查询到高优查询先级的中断标志位,但并不代表高查询优先级的中断可以打断已经并且正在执行的低查询优先级的中断服务。
例如:当计数器0中断和外部中断1(按查询优先级,计数器0中断>外部中断1)同时到达时,会进入计时器0的中断服务函数;但是在外部中断1的中断服务函数正在服务的情况下,这时候任何中断都是打断不了它的,包括逻辑优先级比它高的外部中断0计数器0中断。
而中断的执行优先级就是你对IP寄存器的设置了。在2个优先级的情况下,某位为1,则相应的中断源为高优先级;为0,则为低优先级。
关于中断的优先级有三条原则:
1、CPU同时接收到几个中断时,首先响应优先级最高的中断请求;
2、正在进行的中断过程不能被新的同级或低行优优先级的中断请求所中断;
3、正在进行的低行优优先级中断服务,能被高行优优先级中断请求中断;
若:同一执行优先级中的中断申请不止一个时,则有一个中断优先权排队问题。同一执行优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,优先权自高到低的顺序即: 外部中断0>定时/计数0>外部中断1>定时/计数1>串行接口
例如:设置IP = 0x10,即设置串口中断为最高优先级,则串口中断可以打断任何其他的中断服务函数实现嵌套,且只有串口中断能打断其他中断的服务函数。若串口中断没有触发,则其他几个中断之间还是保持逻辑优先级,相互之间无法嵌套。
关于中断嵌套。可以这样说,当一个中断正在执行的时候,如果事先设置了中断优先级寄存器IP,那么当一个更高优先级的中断到来的时候会发生中断嵌套,如果没有设置则不会发生任何嵌套;如果有同一个优先级的中断触发,它并不是在“不断的申请”,而是将它相应的中断标志位置即IE寄存器的某位置位,当CPU执行完当前中断之后,按照查询优先级重新去查询各个中断标志位,进入相应中断。
要记住,没有设置IP时,单片机会按照查询优先级(或都说逻辑优先级)来排队进入服务。如果要想让某个中断优先响应, 则要设置IP,更改执行优先级(或者说物理优先级)。要注意的是,当设置了IP后,当低执行优先级中断在运行时,如果有高执行优先级的中断产生,则会嵌套调用进入高执行优先级的中断。如果你是用C语言写的程序,并在中断服务时 using 了寄存组,要注意,两个不同执行优先级的中断服务程序不要 using 同一组寄存器。
看两个问题,如下:
1 在各个中断都是低优先级的时候,如果定时器0的溢出进入中断。在这个中断处理的过程中,外部中断0也被触发了,那么是不是要发生中断嵌套?
2 如果定时器0发生中断的时候,进入中断处理程序,这个时候外部中断1条件触发条件满足了。因为定时器0自然优先级比外部中断1高,那么定时器0的中断处理程序继续执行。假设定时器中断处理程序执行的过程中,外部中断1的触发。条件消失了,那么等定时器0的中断处理完后,程序还是会进入外部中断1处理程序吗?
答案1:在IP事先设置了外部中断0的优先级的情况下,CUP会中止定时器0的中断服务,进入外部中断0服务程序,执行完以后再回到定时器0中断服务程序。否则不会。
答案2:肯定会进入中断的;外部中断1的触发条件满足后会置位外部1的中断标志,即使后来外部中断1的触发条件消失了,也不会清除已置位的中断标志,所以等定时器0的中断处理完后,程序判断外部中断的中断标志为1后依然会进入外部中断1处理程序的,只有在外部中断1处理程序中执行reti指令才会硬件清除外部中断1的中断标志(这也正是为什么中断返回使用reti指令而不可以用ret替换的原因)…
知识点6:待学习硬件调试
知识点7:自定义协议进行串口通信传输数据
DWORD wr_addr = 0x1F0000;
void read_user_data_from_flash(void)
{
uint8_t rd_data[32] = {0};
SpiFlashDmaRead2XMem(rd_data, wr_addr,32);
memcpy((BYTE *)user_data,rd_data,sizeof(rd_data));
report_rsp(rd_data,32);
}
void write_user_data_to_flash(void)
{
BYTE wr_data[32] = {0};
memcpy(wr_data,(BYTE *)user_data,sizeof(wr_data));
report_rsp(wr_data,32);
SpiFlashDmaWrite4XMem(wr_data, wr_addr, sizeof(wr_data));
}
void send_telegram(void)
{
report_rsp((BYTE *)&tx_telegram, sizeof(t_telegram));
}
extern uint8_t flag_rev;
#define MEM_REQ_MSK 0xd8
#define MEM_REQ 0x80
#define MEM_RSP 0x90
#define MEM_RD 0x20
#define MEM_ADD 0x07
void configure_input(void)
{
uint8_t i, ret, tmp;
static uint8_t my[10] = {0};
ret = get_command(); //??è??üá?
if(ret)
{
rx_telegram.CTRL = cmd[0];
tmp = rx_telegram.CTRL & MEM_REQ_MSK;
if(MEM_REQ == tmp) //it's a memory access request telegram to the display
{
if (rx_telegram.CTRL & MEM_RD) // read access
{
// report_rsp(cmd,5);
tx_telegram.point = user_data[rx_telegram.CTRL & MEM_ADD];
tx_telegram.CTRL = (MEM_RSP | MEM_RD) + (rx_telegram.CTRL & MEM_ADD);
send_telegram();
}
else
{
memcpy((BYTE *)&rx_telegram,cmd,sizeof(t_telegram));
tx_telegram.point = user_data[rx_telegram.CTRL & MEM_ADD] = rx_telegram.point;
tx_telegram.CTRL = MEM_RSP + (rx_telegram.CTRL & MEM_ADD);
if((rx_telegram.CTRL & MEM_ADD) == 7)
write_user_data_to_flash();
send_telegram();
}
}
}
}