最近在学习这系列视频,正好学了DMA读写串口的视频,发现DMA方式串口接收数据的时候,很容易丢失数据啊。。。
- #include "stc/STC32G.h"
- #include "stdio.h"
- #include "intrins.h"
-
- typedef unsigned char u8;
- typedef unsigned int u16;
- typedef unsigned long u32;
-
- #define MAIN_Fosc 22118400L //定义主时钟(精确计算115200波特率)
- #define Baudrate1 115200L
-
- bit DmaTxFlag;
- bit DmaRxFlag;
-
- u8 xdata DmaBuffer[21];
-
- void UART1_config(u8 brt); // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
- void DMA_Config(void);
- void Delay90us(void);
-
- void UartPutc(unsigned char dat)
- {
- SBUF = dat;
- while(TI == 0);
- TI = 0;
- }
-
- char putchar(char c)
- {
- UartPutc(c);
- return c;
- }
-
- //========================================================================
- // 函数: void main(void)
- // 描述: 主函数。
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2014-11-28
- // 备注:
- //========================================================================
- void main(void)
- {
- u16 i;
-
- WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
- EAXFR = 1; //扩展寄存器(XFR)访问使能
- CKCON = 0; //提高访问XRAM速度
-
- P0M1 = 0x00; P0M0 = 0x00; //设置P0.4、P0.5为漏极开路(实验箱加了上拉电阻到3.3V)
- P1M1 = 0x00; P1M0 = 0x00; //设置P1.1、P1.4、P1.5为漏极开路(实验箱加了上拉电阻到3.3V), P1.1在PWM当DAC电路通过电阻串联到P2.3
- P2M1 = 0x00; P2M0 = 0x00; //设置P2.2~P2.5为漏极开路(实验箱加了上拉电阻到3.3V)
- P3M1 = 0x00; P3M0 = 0x00; //设置P3.4、P3.6为漏极开路(实验箱加了上拉电阻到3.3V)
- P4M1 = 0x00; P4M0 = 0x00; //设置P4.2~P4.5为漏极开路(实验箱加了上拉电阻到3.3V)
- P5M1 = 0x00; P5M0 = 0x00; //设置P5.2、P5.3为漏极开路(实验箱加了上拉电阻到3.3V)
- P6M1 = 0x00; P6M0 = 0x00; //设置为漏极开路(实验箱加了上拉电阻到3.3V)
- P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
-
- for(i=0; i<256; i++)
- {
- DmaBuffer[i] = i;
- }
-
- UART1_config(1); // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
- printf("STC32G UART1 DMA Test Programme!\r\n"); //UART1发送一个字符串
-
- DMA_Config();
- EA = 1; //允许总中断
-
- DmaTxFlag = 0;
- DmaRxFlag = 0;
-
- while (1)
- {
- if(DmaRxFlag)
- {
- DmaRxFlag = 0;
-
- Delay90us();//模拟被其它中断 或 其它后台程序占用90us的时间,然后再开启DMA接收数据,则会丢失1个字节的数据
-
- DMA_UR1R_CR = 0xa1; //bit7 1:使能 UART1_DMA, bit5 1:开始 UART1_DMA 自动接收, bit0 1:清除 FIFO
-
- }
- }
- }
-
- void Delay90us(void) //STC32G @22.1184MHz
- {
- unsigned long edata i;
-
- _nop_();
- _nop_();
- i = 496UL;
- while (i) i--;
- }
-
- //========================================================================
- // 函数: void DMA_Config(void)
- // 描述: UART DMA 功能配置.
- // 参数: none.
- // 返回: none.
- // 版本: V1.0, 2021-5-6
- //========================================================================
- void DMA_Config(void)
- {
- DMA_UR1T_CFG = 0x80; //bit7 1:Enable Interrupt
- DMA_UR1T_STA = 0x00;
- DMA_UR1T_AMT = 20; //设置传输总字节数(低8位):n+1
- DMA_UR1T_AMTH = 0x00; //设置传输总字节数(高8位):n+1
- DMA_UR1T_TXAH = (u8)((u16)&DmaBuffer >> 8);
- DMA_UR1T_TXAL = (u8)((u16)&DmaBuffer);
- DMA_UR1T_CR = 0xc0; //bit7 1:使能 UART1_DMA, bit6 1:开始 UART1_DMA 自动发送
-
- DMA_UR1R_CFG = 0x80; //bit7 1:Enable Interrupt
- DMA_UR1R_STA = 0x00;
- DMA_UR1R_AMT = 20; //设置传输总字节数(低8位):n+1
- DMA_UR1R_AMTH = 0x00; //设置传输总字节数(高8位):n+1
- DMA_UR1R_RXAH = (u8)((u16)&DmaBuffer >> 8);
- DMA_UR1R_RXAL = (u8)((u16)&DmaBuffer);
- DMA_UR1R_CR = 0xa1; //bit7 1:使能 UART1_DMA, bit5 1:开始 UART1_DMA 自动接收, bit0 1:清除 FIFO
- }
-
- //========================================================================
- // 函数: void UART1_config(u8 brt)
- // 描述: UART1初始化函数。
- // 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2014-11-28
- // 备注:
- //========================================================================
- void UART1_config(u8 brt) // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
- {
- u16 dat = (u16)(65536UL - (MAIN_Fosc / 4) / Baudrate1);
-
- /*********** 波特率使用定时器2 *****************/
- if(brt == 2)
- {
- S1BRT = 1; //S1 BRT Use Timer2;
- T2R = 0; //Timer stop
- T2_CT = 0; //Timer2 set As Timer
- T2x12 = 1; //Timer2 set as 1T mode
- T2H = dat >> 8;
- T2L = dat;
- ET2 = 0; //禁止中断
- T2R = 1; //Timer run enable
- }
- /*********** 波特率使用定时器1 *****************/
- else
- {
- TR1 = 0;
- S1BRT = 0; //S1 BRT Use Timer1;
- T1_CT = 0; //Timer1 set As Timer
- T1x12 = 1; //Timer1 set as 1T mode
- TMOD &= ~0x30; //Timer1_16bitAutoReload;
- TH1 = dat >> 8;
- TL1 = dat;
- ET1 = 0; //禁止中断
- TR1 = 1;
- }
- /*************************************************/
-
- SCON = (SCON & 0x3f) | 0x40; //UART1模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
- // PS = 1; //高优先级中断
- // ES = 1; //允许中断
- REN = 1; //允许接收
- P_SW1 &= 0x3f;
- P_SW1 |= 0x00; //UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7, 0xC0: P4.3 P4.4
- }
-
- //========================================================================
- // 函数: void UART1_DMA_Interrupt (void) interrupt 50/51
- // 描述: UART1 DMA中断函数
- // 参数: none.
- // 返回: none.
- // 版本: VER1.0
- // 日期: 2021-5-8
- // 备注:
- //========================================================================
- void UART1T_DMA_Interrupt(void) interrupt DMA_UR1T_VECTOR
- {
- if (DMA_UR1T_STA & 0x01) //发送完成
- {
- DMA_UR1T_STA &= ~0x01;
- }
- if (DMA_UR1T_STA & 0x04) //数据覆盖
- {
- DMA_UR1T_STA &= ~0x04;
- }
- }
- void UART1R_DMA_Interrupt(void) interrupt DMA_UR1R_VECTOR
- {
- if (DMA_UR1R_STA & 0x01) //接收完成
- {
- DMA_UR1R_STA &= ~0x01;
- DmaRxFlag = 1;
-
- //开始发送数据
- DMA_UR1T_AMT = DMA_UR1R_AMT; //数据长度
- DMA_UR1T_CR |= 0x60; //开始发送数据
- }
- if (DMA_UR1R_STA & 0x02) //数据丢弃
- {
- DMA_UR1R_STA &= ~0x02;
- }
- }
复制代码
执行结果:
当以DMA方式接收串口数据时,能否增加配置,循环接收数据,比如我缓存区设100,接50就产生就接收完成,开始接收标志位不清零,模块还可以继续接收数据,把接收到的数据保存到缓存区后面50字节中,如此循环接收
还有,DMA本身是为了减少串口中断次数的,而示例中有个超时程序,5ms没有接收到数据则停止,这样的话反而得开3个中断,定时器中断,串口中断,DMA中断,比没有DMA方式所开的中断更多了
这时能不能内部做个超时处理呢?比如缓存区设为100,接收到第1个字符开始记时,自动按波特率算出接收1个字符需要的时间,中间有超过n个字符的时间没有收到数据则中断,让用户去处理数据,n可以配置0,1,2,3
|