异常二:程序卡在中断函数里面无法跳出执行主函数的逻辑
中断标志位没有被清除,在这里要注意一点,串口中断标志位自动清空的前提是软件需要先读USART_SR寄存器,然后读USART_DR寄存器来自动清除。即串口中断事件发生后,如果使能的接收中断,而中断函数里面什么都不执行的话,接收中断标志位是无法自动清空的,故而,函数会一直卡在中断函数里面。
比如一下这个函数,该函数没有逻辑问题,但会引发以上问题,代码如下
extern unsigned char star_time_led ; //计时开始变量
unsigned char recv_flag = 0;//定义接受标志位
unsigned long recv_cnt = 0;//串口1接收数据缓存
unsigned char recv_buf[MAX_REV_NUM];//串口1接收数据缓存
extern unsigned char star_time;
extern unsigned char recv_time_cnt;
/
以下写法有严重问题
如果没有这句函数→USART_ClearFlag(USART1,USART_FLAG_RXNE); //清空中断标志位
串口接收中断标志位将文法被清空,会导致函数卡在中断函数里面一直循环,无法正常运行主函数
原因分析:
中断条件成立后,中断标志位将会标记,程序将会进入中断函数运行,软件自动轻触中断标志位的条件是
先读USART_SR寄存器,再读USART_DR寄存器。
void USART1_IRQHandler(void) //串口1中断服务程序
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //是否发送中断事件
{
star_time = 1; //接受到一帧数据的时候,打开软件定时器,去计数
if(recv_cnt < MAX_REV_NUM)//数组长度是否超过缓存区
{
recv_buf[recv_cnt] =USART_ReceiveData(USART1);//将接收到的数据存在数组Usart1RecBuf[RxCounter]里
recv_cnt++;
}
else
{
recv_cnt = MAX_REV_NUM
;//限制数组长度,超过缓存区则不再接收
}
recv_time_cnt = 0; //每接收到一帧数据,把定时计数器清零,相当于喂狗
//但是在定时器中断里面会不断的累加
USART_ClearFlag(USART1,USART_FLAG_RXNE); //清空中断标志位
}
}
/
上述代码优化后如下
void USART1_IRQHandler(void) //串口1中断服务程序
{
static char ch;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //是否发送中断事件
{
ch = USART_ReceiveData(USART1);//将接收到的数据存在数组Usart1RecBuf[RxCounter]里
star_time = 1; //接受到一帧数据的时候,打开软件定时器,去计数
if(recv_cnt < MAX_REV_NUM)//数组长度是否超过缓存区
{
recv_buf[recv_cnt] =ch;//将接收到的数据存在数组Usart1RecBuf[RxCounter]里
recv_cnt++;
}
else
{
recv_cnt = MAX_REV_NUM
;//限制数组长度,超过缓存区则不再接收
}
recv_time_cnt = 0; //每接收到一帧数据,把定时计数器清零,相当于喂狗
//但是在定时器中断里面会不断的累加
USART_ClearFlag(USART1,USART_FLAG_RXNE); //清空中断标志位
}
}
异常三:数据发送中间歇性数据异常漏发乱发等
对于这些奇奇怪怪的问题,首先要了解一下发送函数是怎么发送的
USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送 *** 作时,往 USART_DR 写入数据会自动存储在 TDR 内;当进行读取 *** 作时,向 USART_DR读取数据会自动提取 RDR 数据。
TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
当 TDR 内容转移到发送移位寄存器,还没有发送出去的,就再次把TDR 内容转移到发送移位寄存器里,就会出现少发的现象。
什么时候会有这种情况呢?错误 *** 作代码如下:
void USART2_IRQHandler(void) //串口2中断服务程序
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //是否发送中断事件
{
Usart1RecBuf[RxCounter] =USART_ReceiveData(USART2);//将接收到的数据存在数组Usart1RecBuf[RxCounter]里
RxCounter++;//指向数组地址自加
if(RxCounter==2)
{
USART_SendData(USART1, Usart1RecBuf[0]);//发送Usart1RecBuf[0]
USART_SendData(USART1, Usart1RecBuf[1]);//发送Usart1RecBuf[1]
USART_SendData(USART1, Usart1RecBuf[2]);//发送Usart1RecBuf[2]
}
}
}
上述代码连续运行了3次USART_SendData(USART1, Usart1RecBuf);这个函数,这种情况一般都会出现只有最后一个数据发送成功出去。原因可能就是数据还没有发送出去,发送移位寄存器就更新了。
可能没有安装驱动。
驱动分为很多种,比如有FT232、PL2303、CP2102、CH340、CH341、DTECH_RS422等等,他们的驱动都是不一样的,即使名字看上去非常一样的,CH340与CH341就是这个样子。当然,选对了驱动未必就是可以安装成功的。
简介
先查看自己电脑的版本,安装对应的驱动,现在的笔记本基本上都是Window10 64位的,你在安装驱动的时候,要查看自己的电脑的版本,比如,Window 7 32位,那么你安装驱动的时候,建议安装对应的驱动,无论是Window的版本,还是电脑的位数(32位和64位),都要匹配。
但是,也不是一定的,有部分的驱动在不同的版本是可以用的,比如,Window10 64位的驱动,可以安装到Window 7 64位,那到底怎么查看自己的驱动是否可以符合呢?请安装之后,插上串口线,打开设备管理器,如果显示COM了,那么就安装成功了。
#include <reg52h>#include <intrinsh>
#define uchar unsigned char
#define uint unsigned int
sbit LCD_RS = P2^0;
sbit LCD_RW = P2^1;
sbit LCD_EN = P2^2;
#define delayNOP(); {_nop_();_nop_();_nop_();_nop_();};
uchar data RXDdata[ ] = {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20 };
uchar temp,buf,m,count;
bit playflag=0;
uchar code cdis1[ ] = {" SERILA TRANFER "};
uchar code cdis2[ ] = {" "};
/
延时子程序
/
char code SST516[3] _at_ 0x003b;
void delay1(uint ms)
{
uchar k;
while(ms--)
{
for(k = 0; k < 120; k++);
}
}
//
/ /
/检查LCD忙状态 /
/lcd_busy为1时,忙,等待。lcd-busy为0时,闲,可写指令与数据。 /
/ /
//
bit lcd_busy()
{
bit result;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
delayNOP();
result = (bit)(P0&0x80);
LCD_EN = 0;
return(result);
}
//
/ /
/写指令数据到LCD /
/RS=L,RW=L,E=高脉冲,D0-D7=指令码。 /
/ /
//
void lcd_wcmd(uchar cmd)
{
while(lcd_busy());
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 0;
_nop_();
_nop_();
P0 = cmd;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}
//
/ /
/写显示数据到LCD /
/RS=H,RW=L,E=高脉冲,D0-D7=数据。 /
/ /
//
void lcd_wdat(uchar dat)
{
while(lcd_busy());
LCD_RS = 1;
LCD_RW = 0;
LCD_EN = 0;
P0 = dat;
delayNOP();
LCD_EN = 1;
delayNOP();
LCD_EN = 0;
}
//
/ /
/ LCD初始化设定 /
/ /
//
void lcd_init()
{
delay1(15);
lcd_wcmd(0x01); //清除LCD的显示内容
lcd_wcmd(0x38); //162显示,57点阵,8位数据
delay1(5);
lcd_wcmd(0x38);
delay1(5);
lcd_wcmd(0x38);
delay1(5);
lcd_wcmd(0x0c); //开显示,显示光标,光标闪烁
delay1(5);
lcd_wcmd(0x01); //清除LCD的显示内容
delay1(5);
}
//
/ /
/ 设定显示位置 /
/ /
//
void lcd_pos(uchar pos)
{
lcd_wcmd(pos | 0x80); //数据指针=80+地址变量
}
/
发送数据函数
/
void senddata(uchar dat)
{
SBUF =dat;
while(!TI);
TI = 0;
}
/
串行中断服务函数
/
void serial() interrupt 4
{
ES = 0; //关闭串行中断
RI = 0; //清除串行接受标志位
buf = SBUF; //从串口缓冲区取得数据
playflag=1;
switch(buf)
{
case 0x31: senddata('X');break; //接受到1,发送字符'W'给计算机
case 0x32: senddata('L');break; //接受到2,发送字符'I'给计算机
case 0x33: senddata('1');break; //接受到3,发送字符'L'给计算机
case 0x34: senddata('0');break; //接受到4,发送字符'L'给计算机
case 0x35: senddata('0');break; //接受到5,发送字符'A'给计算机
case 0x36: senddata('0');break; //接受到5,发送字符'R'给计算机
default: senddata(buf);break; //接受到其它数据,将其发送给计算机
}
if(buf!=0x0D)
{
if(buf!=0x0A)
{
temp =buf;
if(count<16)
{
RXDdata[count]=temp;
count++;
}
}
}
ES = 1; //允许串口中断
}
/
数据显示函数
/
void play()
{
if(playflag)
{
lcd_pos(0x40); //设置位置为第二行
for(m=0;m<16;m++)
lcd_wdat(cdis2[m]); //清LCD1602第二行
for(m=0;m<16;m++)
{
lcd_pos(0x40+m); //设置显示位置为第二行
lcd_wdat(RXDdata[m]); //显示字符
}
playflag=0;
count=0x00;
for(m=0;m<16;m++)
RXDdata[m]=0x20; //清显存单元
}
}
/
主函数
/
void main(void)
{
P0 = 0xff;
P2 = 0xff;
SCON=0x50; //设定串口工作方式
PCON=0x00; //波特率不倍增
TMOD=0x20; //定时器1工作于8位自动重载模式, 用于产生波特率
EA=1;
ES = 1; //允许串口中断
TL1=0xf3;
TH1=0xf3; //波特率2400
TR1=1;
lcd_init();
lcd_pos(0x00); //设置显示位置为第一行
for(m=0;m<16;m++)
lcd_wdat(cdis1[m]); //显示字符
lcd_pos(0x40); //设置显示位置为第二行
for(m=0;m<16;m++)
lcd_wdat(cdis2[m]); //显示字符
while(1)
{
play();
}
}
这个串口通信程序在我的开发版上已经全部验证通过,你可以根据实际,攸 改里面某些参数,满足你自己的需要就行。
比如你你点亮小灯,这部分你只可在相应位置添加代码即可。单片机都是51的。
其中1602的显示程序是用来验证是否通信成功,这部分你可作为参考。这部分你大可用小灯显示程序来替代。串口模式修改esp32的网络设置:这里使用Goouuu-ESP32开发板来制作这个蓝牙串口调试工具,市面上基于ESP32的开发板不少,大都是和Goouuu-ESP32一样采用ESP-WROOM-32模组,具有WIFI和BLE BT功能,使用上也是没有太大差别。从外形上看,Goouuu-ESP32和LoLin NodeMcu V3有些相似,不过稍加辨认还是能看出端倪,后者使用的是ESP8266MOD模组,只有WIFI功能,下载芯片用的是CH340G,而Goouuu-ESP32用的是CP2102。这个蓝牙串口调试工具的制作过程可以说是相当简单,用一句话来描述就是,将支持蓝牙串口协议的代码烧写到这块ESP32开发板上就可以了。具体步骤可以参考下文:
首先,需要安装ESP32的编译环境。这里推荐使用Arduino IDE,安装文件可以到Arduino官网进行下载,由于安装过程比较简单,就不再这里赘述。编程环境安装好后,就需要添加相应的开发板。依次打开“文件”->“首选项”,在“设置”页面的“附加开发板管理器网址”中添加“>我们设置RF参数,只需要在上面的灰色格子里输入参数就可以,再根据产生的数据进行配置,个人建议使用英文版的吧
上图从左往右依次是让你选择调试类型,是否禁用曼切斯特模式,晶体误差(TX和RX两个),频率设置,发射速率设置,AFC是否开启,(中间的可以不理),频差
选择好以上的配置后,相应的在下面会有几个表,分别往所对应的寄存器写入相应的值就可以
频率设置
发射速率设置
频率偏差
其他设置(我也不知道是干嘛的,具体看数据手册吧)
把以上配置好后,SI4432的初始化基本也就配置好了。
接下来,应该对相应的spi口配置,写SI4432读寄存器函数和写寄存器函数,注意,在写的时候最高位应为1代表写!
配置大致就是这样了~~
接下来列出一些主要的寄存器吧~
地址03h中断状态 1
地址04h 中断/中断2
地址 05h中断使能 1
地址 06h 中断使能 2
地址 07h 运行模式和功能控制1
地址 08h 工作模式和功能控制
地址 09h 30 MHz晶体振荡器负载电容
地址 0Bh GPIO配置 0
地址 0Ch GPIO配置 1
地址 0Dh GPIO配置2
地址 30h 数据存取控制
地址4Bh 接收到的数据包长度
地址 69h AGC 过载
地址71h 调制模式控制 2
地址 7Fh FIFO存取当用单片机和PC机通过串口进行通信,尽管单片机有串行通信的功能,但单片机提供的信号电平和RS232的标准不一样,因此要通过max232这种类似的芯片进行电平转换。
MAX232芯片是美信(MAXIM)公司专为RS-232标准串口设计的单电源电平转换芯片,使用+5v单电源供电。
简言之,你的电脑已经有RS232接口,使用MAX232芯片可以实现单片机和PC机通过串口进行通信,当然也能下载程序。
问题是现在的笔记本已经没有了RS232接口,肿么办?ch340是USB转串口芯片,有了它,可以模拟出来一个串口,实现单片机和PC机通过串口进行通信,当然也能下载程序。RS-485用于多点互连时非常方便,可以省掉许多信号线。应用RS-485 可以联网构成分布式系统。RS-485的”节点数”主要是依”接收器输入阻抗”而定;根据规定,标准RS-485接口的输入阻抗为≥12kΩ,相应的标准驱动节点数为32个。为适应更多节点的通信场合,有些芯片的输入阻抗设计成1/2负载(≥24kΩ)、1/4负载(≥48kΩ)甚至1/8负载(≥96kΩ),相应的节点数可增加到64个、128个和256个。以泓格的I/O模块为例,每个485网络最多的节点为256个,加中继I-7510后,每个485网只要工作在不同的波特率:1200,2400,4800,9600,19200,38400,57600,115200之下,就可以允许相同的地址号。所以中继模块不但可以使通讯距离增加,还可以增加节点数。泓格模块的485网络中节点数最大为:2568=2048个。首先,串口调试软件的波特率最好设置成9600,这种波特率也支持显示中文的,
USB转串口看你用的什么芯片,经过实验采用CH340或是341的是没有问题的,其他还有待验证。
串口调试软件也很重要,有些软件会奇怪出现乱码,但并不是程序错误,推荐你用小丁的sscom32,个人感觉很棒! ;
; 程序名称:单片机串口发送中文字符实验,232通信程序
; 硬件说明:打开《串口调试助手》,波特率设为9600速率,110592晶体
; 注 意:《串口调试助手》要设置好COM号(请核对是否和设备管理器的COM编号一致)
; *** 作说明:下载程序后打开串口调试助手sscom32exe即可显示,或按一下复位开关
;/
ORG 0000H
AJMP main
ORG 0030H
main: MOV 20H,#00H
MOV TMOD,#22H ;定时器工作方式
MOV SCON,#40H
MOV TH1,#0FAH ;波特率9600
MOV PCON,#80H
SETB TR1
SETB REN ;允许接收
ajmp start
start: mov dptr,#num ;查表
send : clr a ;
movc a,@a+dptr
cjne a,#'$',send_2
ajmp $ ;遇到$停止。
send_2:mov sbuf,a
JNB TI,$ ;串行发送一帧结束了吗?
CLR TI ;
inc dptr ;指针加一
Ljmp send
num:
db 0dh,0ah,0dh,0ah,"XY900_USB型单片机学习板USB-232串口中文发送试验 >
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)