主程序计算量过大或者函数嵌套太多是否会影响UART中断(个人测试结论在13楼)
我这次搞个产品就碰到了这个问题,本来在上个帖子中问过了,但是没什么结果,所以重开个帖子大家一起讨论一下我觉得理论上不会有这样的问题出现,但是不排除我有没考虑到的地方。下面的程序的说明:
我的主程序算了很多东西(包括数码管显示,AD采样计算,还有大量的AD数据处理),同时我开了个UART终端,每次连续接收八个字节
当我主程序中的调用程序太多的时候,UART接收中断接收到的第一个字节就是错误的
当我把主程序中注释掉一部分的时候,UART接收的第一个字节就正确了
大家说有可能是主程序对中断的相应及时度有影响吗?
注:主程序中并没有操作其他中断 除非波特率特别高,一般都没碰到过。
主程序有没有关中断?
串口接受程序是不是太长? 回【1楼】 void_c 上官先生:波特率是19200,主程序中没有关中断,串口的中断程序中只有3 4句代码
我奇怪的事是1、主程序减少一些计算,就不会出错,或者说出错的几率就很小了
2、为什么总是第一个字节出错,而其他字节不会出错
我用的4M的晶振,1/2分频 ,接下来我把波特率调到9600试试,测试中.............. 在串口接收器没有FIFO的前提下,如果不能及时响应串行口中断,那么当下个字节收到后,就会缓冲区溢出,导致丢数据。。。
看看芯片有没有类似的标志位。 http://cache.amobbs.com/bbs_upload782111/files_14/ourdev_441591.JPG
(原文件名:11.JPG)
这是我减少了一些主程序中的计算结构测试的数据,只有部分不正确
如果我不改主程序的话,那么基本就是部分正确,大部分不正确了,所以我觉得还是跟主程序中的内容有关系
但是怎么样的关系,我又说不上来~~~ 第一个字节出错?
是不是485网络? 【5楼】 eiglxl 我是用的点对点的,没有涉及到网络,也不是每次都出错的 【3楼】 dr2001 能详细的帮忙讲解一下吗? 我就是不太明白为什么我这个后面的字节接收都没问题 大家一起帮我看一下,我测试了一下,即使我一位一位读,还是会出现这个问题,应该是跟主函数中的子程序有关系 用的什么片子?电路是什么样的?不好说! 没有人能给指个方向吗??? 用的是ICCAVR,代码压缩了吧?! 楼主, 你这么NB的葱也没找到方向啊? :) 感念我佛慈悲, 我来“指导”你一下:
把你的UART中断子程序贴出来看看。3楼已经说的很清楚了,你有没有FIFO? 你对接收到的数据是在中断中处理的还是在外面?从你的问题看, 应该是没有FIFO,在中断外面处理数据的。 有FIFO只会是最后几个字符丢掉。 【12楼】 theta呵呵 我的签名让你见笑了~~
我的处理是在外面的,不过我似乎是找到根本所在了
我用的是4M的晶振,1/2分频,开始采用的波特率是19200
昨天晚上(确切的说是今天凌晨2点),我把波特率调到了4800
终于没有收到错误字节了
我得到的结论是:波特率过高,而且主程序中函数嵌套太多,导致中断响不及时,而在波特率比较低的情况下,可以一定程序的容忍这个“中断不及时”
以上完全是自己的想法,到底是不是我说的这样,还不知道怎么证明呢~~~~~ 这样说来应该不是中断响应不及时,而是你处理程序响应不及时,因为你主程序并没有禁止中断, 而中断响应跟你主程序嵌套多少层关系不大,反正它就压几个寄存器入栈,时间是固定的。 【14楼】 theta 我的中断响应就2条语句,怎么能说响应不及时呢~~ 怎么?没人给点意见或者建议吗? 看不见程序,说什么也没用... 经常说没图没真相,不贴点相关代码大家坐着猜,猜中了就可以去买奖卷了。
即便中断处理函数只有一行,也不代表中断就能及时响应。
别处关中断,别的中断处理程序处理中断,诸如此类,都会导致响应延迟。。。 4M的晶振,1/2分频,开始采用的波特率是19200
4800就好了,波特率的计算后和标准的数据或许误差比较大 1、晶振不是用内部晶振,因为有误差,最好用11.0592的
2、定时中断中的代码一定要短
3、串口中断中的代码一定要短
4、串口分析程序在主程序中执行
5、采用“中断+缓冲区+超时”处理
即:接收时用中断方式+接收到字符后,直接装入接收缓冲区,。 我菜鸟,在中断中重复调用比较多,编译时候提示了重入问题,后来我就把需要重入的函数做成宏,但是还是没解决,偶尔出现死机 【19楼】 mikezfq: 这个波特率不是我计算的,是用软件算出来的~~
【18楼】 dr2001 :我把其他所有中断都关掉了,所以不会有中断间相互影响的问题
【20楼】 ba_wang_mao:我用的是外部晶振,基本符合你说的那几条标准
********************************************
我把程序注释整理一下发上来,大家看看~~~~~ 用CVAVR生成的代码
#define RXB8 1
#define TXB8 0
#define UPE 2
#define OVR 3
#define FE 4
#define UDRE 5
#define RXC 7
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<OVR)
#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
// USART Receiver buffer
#define RX_BUFFER_SIZE0 8
char rx_buffer0;
#if RX_BUFFER_SIZE0<256
unsigned char rx_wr_index0,rx_rd_index0,rx_counter0;
#else
unsigned int rx_wr_index0,rx_rd_index0,rx_counter0;
#endif
// This flag is set on USART Receiver buffer overflow
bit rx_buffer_overflow0;
// USART Receiver interrupt service routine
interrupt void usart_rx_isr(void)
{
char status,data;
status=UCSR0A;
data=UDR0;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
{
rx_buffer0=data;
if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0;
if (++rx_counter0 == RX_BUFFER_SIZE0)
{
rx_counter0=0;
rx_buffer_overflow0=1;
};
};
}
#ifndef _DEBUG_TERMINAL_IO_
// Get a character from the USART Receiver buffer
#define _ALTERNATE_GETCHAR_
#pragma used+
char getchar(void)
{
char data;
while (rx_counter0==0);
data=rx_buffer0;
if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0;
#asm("cli")
--rx_counter0;
#asm("sei")
return data;
}
#pragma used-
#endif
用时从堆栈取数据, 就怕你数据量大,你还没处理堆栈就溢出了! 一句话,程序结构不合理。应该处理的时候没有处理,因为有别的中断做其它事了。速度慢下来可以改善,但没有从源头解决。
不存在计算量过大的问题,MCU做什么不是一样的做?
你试试处理USART的时候禁所有中断? 【24楼】 fsclub 我处理USART的时候就是关闭所有中断了
不存在中断竞争的问题~ 一进中断你就CLI();试试。
再就是你用的是片内RC还是外部晶振?用片内RC有没有校正?片内RC存在时钟精度问题。 回:【26楼】 fsclub
我按照您的方法试了 CLI(),问题依然存在
我使用的4M晶振,是外部的。 没硬件不好说 搞单片机的,要有时间观念。一段程序最大时间多少,接收一个字节用多少时间等等。如果对这些都没有概念,还开发什么呢? 我不是说你中断来不及响应,是说你处理程序来不及响应!
假设你MCU正被N层嵌套缠住,这时候收到了第一个串口数据,发生中断,收完数据后没有处理(按你说的,中断里才2条语句),出中断,MCU回到原地挣扎,好不容易出来,这时第二个数据收到(如果没有FIFO,第一个数据就被冲掉了),再中断,出中断,你MCU终于有空处理接收到的数据了,刚好碰到第二个数据,然后处理。
如果是这个架构,出问题是肯定的,几率大小的问题。 【30楼】 theta :先不考虑堆栈深度问题,无论我嵌套几层,压入堆栈的字节数是一样的啊,毕竟在子程序调用的上一层,上一层的现场就已经保护了,不是吗? 所以我理解这个似乎跟嵌套关系不大,当然这是我个人的想法,您说呢?
****************************************************
【29楼】 zhiwei : 不好意思,小弟才疏学浅,无法理解您的意思,什么叫一段程序最长时间多少,接收一个字节用多少时间???
这个跟中断有关系吗?您能详细给说说吗? 4800bps,一个字节10bit,你可以算出来接受一个字节需要多少时间。
假设上位机连续发数据,如果接收端没有FIFO,那么,必须在第二字节接收完毕之前处理掉第一个数据,否则就会丢数。
无论你的程序怎么写,留给你的最坏情况处理时间就是1字节那么多时间。请注意,最坏情况的处理时间。 【32楼】 dr2001 : 我现在遇到的不是丢数的问题,而是接收到的字节不正确 真能扯,扯到现场保护了! 那你告诉我,最坏的情形,在响应完中断后, 你的MCU要花多长时间才有空处理接收到的数据? 到现在都没有见到程序,很难判断 一个有问题的程序还不肯发...
大家都在瞎猜-_- 【34楼】 theta :现场保护是指在调用子程序时候进行的压栈处理,这跟扯不扯没什么关系吧~ 还有我什么时候处理接收到的数据跟我问的问题有关系吗? 我只是接受8个字节,这个8个字节我在接收中断中已经存到数组中了。
【35楼】 bigworms 【36楼】 snoopyzz:不是我不肯发程序,是程序有点乱,要整理一下,要不发上来大家也看的晕头转向的~ #include"msp430x14x.h"
#include"SJD1C.h"
//显示数据定义
uchar table[]=
{
0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00, //0,1,2......9 +80H为带小数点
0x40,0x79,0x77//-ERR
}
;
/****************************************************
变量定义
****************************************************/
uchar Bstart=0; //起动标志位
uchar B2ms=0; //2ms延时标志位
uchar Bover=0; //30次采样标志位
uchar Bpf=0; //相故障标志位
uchar Bol=0; //过载故障标志位
uchar Btrip=0; //动作标志位
uchar Csend=0; //发送标志位
uchar actflag=0; //接收处理标志
uchar Csta=0; //Bstart削抖
uint CurrentA_add=0;
uint CurrentB_add=0;
uint CurrentC_add=0;
uint CurrentA=0;
uint CurrentB=0;
uint CurrentC=0;
uint CurrentMax=0; //最大相值
uint CurrentMin=0; //最小相值
uchar G_val=0; //个位显示值
uchar S_val=0; //十
uchar B_val=0; //百
uchar Q_val=0; //千
uchar CALcnt=0; //数码显示指针
uchar Ccnt =0 ;
uchar ADcnt=0;
uchar B2mscnt=0;
uintLedcnt=0;
uchar Selcnt=0;
uchar Showcnt=0;
uintLedshow=0;
uintStartGate=0;//起动门限值
uchar writebuff={0}; //用于存放被数组FLASH
uchar readbuff={0};//用于存放被数组FLASH
uchar usart_receive={0};//定义USART存放地址,USART发送数据;
uint num_receive=0; //总接收字节数
uchar num_byte; //变量字节数
uchar cad_flag=0; //ADC允许转换标志
uchar tsend=0; //USART发送延时标志位
uint CRC; //////////////////////////////////
double Ka=0; //A,B,C相的补偿系数
double Kb=0;
double Kc=0;
/****************************************************
应用函数定义
****************************************************/
void Init_device(void); //DEVICE初始化
void Dis_led(void); //数码管显示程序
void Start_Check(void);
void Chk_Set(void);
void Show_cal(void);
void Cal_current(void);
void Qz_cal(void);
void Qadd_cal(void);
void Right_show(void);
void delay_ms(int i);
void Ol_Check(void);
void Pf_Check(void);
void Trip_Check(void);
void Pf_delay();
void Adc_conver(void);
int Max(unsigned int i,unsigned int j,unsigned int k);
int Min(unsigned int i,unsigned int j,unsigned int k);
void Erase_Segb(void);
void Read_Segb(uchar *readbuff);
void Write_Segb(uchar *writebuff);
void crc_checking(volatile unsigned char trp);
void receive_act(void);
void modbus_act(void);
void Send_data(void);
void Change_k(void);
double var_change(uchar i,double ref_k);
/****************************************************
主函数main()
****************************************************/
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;
Init_device();
Chk_Set();
Read_Segb(readbuff);
Change_k();
IE1 |= 0x40; //打开UART接收中断
_EINT();
while (1)
{
Dis_led(); //显示程序 这段注释掉就可以正常收发
if (num_receive>=7) //判断是否接收够8字节
{
IE1 &= 0xBF;
num_receive=0;
Csend=1;
}
}
if(Csend)
{
Send_data();//这里是对接收到的数据进行处理
}
else
{
IE1 |= 0x40; //打开UART接收中断
}
if(B2ms) //这段注释掉也可以正常收发,这里是对AD采样进行数据处理
{
if(Bover==1) //采集30次结束,进入数据处理
{
Trip_Check();
switch(CALcnt)
{
case 0:
_NOP();
break;
case 1:
Start_Check();
break;
case 2:
Pf_Check();
break;
case 3:
Ol_Check();
break;
case 4:
Qz_cal();
break;
}
CALcnt++;
if(CALcnt>=5)
{
CALcnt=0;
Bover=0; //开始采集
}
}
}
}
}
/****************************************************
设备初始化
****************************************************/
void Init_device(void)
{
DCOCTL=0xE0;
BCSCTL1=0xD7;//LFXT1 ACLK=1/2
BCSCTL2=0xC0;
P1SEL=0x00;
P2SEL=0x00;
P1DIR=0xFF;
P2DIR=0xFF;
P1OUT=0x00;
P2OUT=0x00;
P3SEL=0x30;
P3DIR=0xFF;
P3OUT=0x00;
P4SEL=0x00;
P5SEL=0x00;
P4DIR=0xFC;
P5DIR=0xFF;
P4OUT=0x00;
P5OUT=0x00;
TACTL=0x144;
CCTL0=0x10;
// CCTL1=0x10;
// CCTL2=0x10;
CCR0=0x3E8;
// CCR1=0x3E8;
//CCR2=0x1F4;
TACTL|=0x10;
ADC12CTL0=0x230;
ADC12CTL1=0x210;
P6SEL=0xE0;
P6DIR=0x1F;
P6OUT=0x00;
FCTL2 = FWKEY + FSSEL0 + FN0; //Flash设置
ME1|=UTXE0+URXE0;
UCTL0=CHAR+SPB; //异步,8位数椐+2停止位
UTCTL0=SSEL0;
U0BR0=0xA0; //ACLK/2 2M 波特率4800**稳定
U0BR1=0x01;
U0MCTL=0xC0;
/*
U0BR0=0x68;
U0BR1=0x00;
U0MCTL=0x40;
*/
UCTL0&=0xFE;
IE1=0x00;
IE2=0x00; //禁止中断
}
/****************************************************
补偿系数更改程序
****************************************************/
void Change_k(void)
{
Ka=var_change(readbuff,1);
Kb=var_change(readbuff,1);
Kc=var_change(readbuff,1);
}
/****************************************************
定时器T0中断程序
****************************************************/
#pragma vector = TIMERA0_VECTOR
__interrupt voidT0_Interrupt(void)
{
cad_flag=1;
}
/****************************************************
UART0发送程序(采用查询方式)
****************************************************/
void Send_data(void)
{
static uchar i = 0;
static uchar sendcnt=0;
P5OUT |= BIT7;
P3OUT |= 0x08; //EN 485
delay_ms(100);
switch (i)
{
case 0:
U0TXBUF = usart_receive;
CRC = 0xffff;
i++;
break;
case 1:
U0TXBUF = usart_receive; //
i++;
break;
case 2:
num_byte=3;
U0TXBUF = usart_receive;
i++;
break;
case 3:
if (sendcnt<num_byte) //
{
U0TXBUF = readbuff;
sendcnt++;
}
else
{
i++;
sendcnt=0;
}
break;
case 4:
U0TXBUF = *(unsigned char*)(&CRC);
i++;
break;
case 5:
U0TXBUF = *((unsigned char*)(&CRC) + 1) ;
i = 0;
delay_ms(100);
P3OUT &=0xF7; //DISENBLE 485
actflag=0;
Csend=0;
IE1 |= 0x40;
break;
}
}
/****************************************************
UART0接收中断程序
****************************************************/
#pragma vector = USART0RX_VECTOR
__interrupt voidRecv_Interrupt(void)
{
usart_receive=U0RXBUF;
++num_receive;
} 果然和我想得一样,中断处理不够及时...
你建了缓冲区是没错,
但可以执行别的子程序时,时间够长的话,中断发生超过8次的话,
已经就缓冲区溢出了,你有没有考虑这个问题,居然没有缓冲溢出的判断...
__interrupt voidRecv_Interrupt(void)
{
if( num_receive>=sizeof(usart_receive) )
{
缓冲区溢出标志
}
else
{
usart_receive=U0RXBUF;
++num_receive;
}
}
然后主程序中加上相应的溢出处理,如果不能容忍数据丢失
简单则需要加大缓冲区。或者发送也做成中断式的,也没什么难度,
我写usart时,收发都中断...
缓冲区一满就通知发送方暂停发送,并回传收到的有效数据个数
等缓冲区处理结束,再通知它重新继续发送...
这样双方协调才能完成大量数据传输...当然我做的还有每块CRC16校验,出错块重传,超时处理等功能...
单纯的加大缓冲区不是最好的解决办法 你的主程序循环时间大于你 串口接受的满缓冲区的时间 就会出问题所以你改变了比特率或者减少主程序循环时间就会有这个现象
前后台的系统一定要注意这个问题 【39楼】 snoopyzz:您好,您说的是缓冲区是num_receive的个数吗?如果是,我的主程序里面加了判断了,我就是开始把判断加在中端里面不行,才把它改到主程序里的...
**********************************************
【40楼】 chip_good 一切都好:您说我的主程序循环时间大于串口接受的满缓冲区的时间,能否告知这个该如何计算呢,我以前没碰到过这个问题,所以没算过,还请指教... 在主程序中判断有什么用-_-
中断的产生又不是你说了算的,在执行到你的判断之前,如果传来的数据大于你的缓冲区
不就缓冲区溢出了吗....40L说的和我说的是同样的东东 【42楼】 snoopyzz :谢谢您的指导,您说的意思我理解,可我原来的时候就是把缓冲区判断放在中断程序中啊,是因为那样不行,我才把它放到主程序中的 我来挑挑刺:
(1)、用查询方式发送好象“不够专业”吧!
AVR单片机提供了“数据空中断”和“发送完成中断”为什么不用呢?
(a)、如果是RS232,则可以只使用“数据空中断”
(b)、如果是RS485,则必须“数据空中断”和“发送完成中断”配合使用
即:发送数据用“数据空中断”,只是最后一个字节发送完毕后,使能“发送完成中断”,
然后在“发送完成中断”服务程序中置:485_RECIVE()。
(2)接收中断服务程序中没有考虑接收缓冲区溢出。
(3)如果主程序执行时间大于接收时间,也会造成错误。
这种情况下可以用状态机来处理:
switch (时刻)
{
case 时刻1:
执行时刻1的代码
时刻=时刻2;
case 时刻2:
执行时刻2的代码
时刻=时刻3;
case 时刻3:
执行时刻3的代码
时刻=时刻1;
}
解决方案(分三种,下中上策):
1.保证处理速度比接收快(你注释掉语句或降低波特率都属于这个方法)
2.增大缓冲区,并保证不会溢出,一定要在中断检验是否有溢出
3.与发送方协调通讯,可以不影响单片机的其它任务的情况下高效的传输数据 查询方式发送时,由于需要等待发送中断标志,因此需要占用大量的CPU资源。
而中断方式发送,则可以避免上述现象。
而且可以立即将数据上传到上位机上。
中断方式发送要点:
void begin_send(void)
{
(1)、填写好发送缓冲区
(2)、填写好要发送的字节数send_count=100
(3)、初始化当前发送指针send_pos=0
(4)、置空中断允许
}
(4)、空中断服务程序
{
UDR0 = send_buffer;
if (send_pos>=send_count)
关闭空中断
} 【44楼】 ba_wang_mao :谢谢你的建议,其中3)如果主程序执行时间大于接收时间,也会造成错误? 这个我不太理解,接收应该是不受主程序执行时间影响的吧,能否说的详细一点....
【45楼】 snoopyzz :呵呵,我又单步调了一下程序,最后发现果然是缓冲区溢出了
谢谢大家的热心帮忙,谢谢~~~~~ 【44楼】 ba_wang_mao :谢谢你的建议,其中3)如果主程序执行时间大于接收时间,也会造成错误? 这个我不太理解,接收应该是不受主程序执行时间影响的吧,能否说的详细一点....
【45楼】 snoopyzz :呵呵,我又单步调了一下程序,最后发现果然是缓冲区溢出了
谢谢大家的热心帮忙,谢谢~~~~~ 【44楼】 ba_wang_mao :谢谢你的建议,其中3)如果主程序执行时间大于接收时间,也会造成错误? 这个我不太理解,接收应该是不受主程序执行时间影响的吧,能否说的详细一点....
【45楼】 snoopyzz :呵呵,我又单步调了一下程序,最后发现果然是缓冲区溢出了
谢谢大家的热心帮忙,谢谢~~~~~ mark mark {:shy:} mark chip_good 发表于 2009-5-5 19:23
你的主程序循环时间大于你 串口接受的满缓冲区的时间 就会出问题所以你改变了比特率或者减少主程序循环 ...
你的主程序循环时间大于你 串口接受的满缓冲区的时间 就会出问题所以你改变了比特率或者减少主程序循环 ...
如果主程序循环时间大于你串口接受的满缓冲区的时间 ,应该怎么解决呀?
页:
[1]