单片机与电脑串口通信,无论发啥信息都收到00,求问是怎么回事?代码见下面。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单片机与电脑串口通信,无论发啥信息都收到00,求问是怎么回事?代码见下面。相关的知识,希望对你有一定的参考价值。
#include<reg52.h>
unsigned char flag,a; // flag为标志位
void main(void)
/*串口初始化*/
TMOD=0x20; //设置定时器1为工作方式2
TH1=0xd9; //装初值,设定波特率为9600
TL1=0xd9;
TR1=1; //启动T1定时器
REN=1; //允许串口接收数据,REN、SM0、SM1这三句亦可以只写一个SCON=0x50
SM0=0; //串口工作方式设置为方式1
SM1=1;
PCON =0X00;
EA=1; //开总中断
ES=1; //开串口中断
while(1)
if ( flag == 1 ) //当标志位为1时,进行下列的操作
ES=0; //关串口中断,以保证一个数据一个数据的传输
flag=0; //标志位清零
SBUF=a; //如果收到计算机发送的数据,则将该数据再发回给计算机
while(!TI); //这句很重要,等待发送数据的完成
TI=0; // 串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请。在中断服务程序中,必须用软件将其清0,取消此中断申请。
ES=1; //开串口中断,用来接收下一个数据
void ser() interrupt 4 //串口中断函数
RI=0; //接收中断标志位,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请。则必须在中断服务程序中,用软件将其清0,取消此中断申请。
a=SBUF; //收到的数据存入缓冲器,然后赋给a
flag=1;
{if(Ri)
{Ri=0;
a=sbuf;
}
if (Ti)
{
}}
当然老是接收到00了,我给你解释一下:你的串口中断函数不止是在接收数据的时候执行,在执行while(!Ti);时等待发送完成时也会触发中断函数,此时sbuf里面为空,数据之前被取走了。解决办法:把串口中断函数改为:void ser()interrupt 4{if(Ri){Ri=0;a=sbuf;
flag=1;
}if (Ti){}}
再说一句,sbuf里面的数之前被取走的话就为0了,速度给分啊,用手机打了老半天 参考技术A void ser() interrupt 4 //串口中断函数
if(RI)
RI=0;
a=SBUF; //收到的数据存入缓冲器,然后赋给a
flag=1;
另外看看你的波特率,应该有问题,9600的话初值应该是0xfd,初始化时也没有设置1T模式,缺省状态应该12T模式的。 参考技术B TMOD = 0x20; //设置定时器1为工作方式2
TH1 = 0xd9; //装初值,设定波特率为9600
TL1 = 0xd9;
……
d9,这是设定 9600 吗?
隔壁老王看了都会的文章:STM32串口实验——单片机与上位机交互信息
今天介绍USART串口通信的使用,目的在于会用串口传送和接收数据。内容包含了NVIC中断的知识,下一篇着重讲NVIC中断。ADC在下下一篇。主要还是先看懂USART的相关代码。
什么是串行通信
我们对通信的字面意思理解就是信息的传输与交换。通信按照基本类型可以分为并行通信和串行通信。并行通信时数据的各个位同时传送,可以实现字节为单位通信,但是因为通信线多占用资源多,成本高。而串口通信传送数据是一帧一帧发送,传送距离相对较远,占用资源少,成本低,但是串行通信传输速度相对于并行通信传输速度较慢。单片机上大多使用串行通信。
串行通信是单片机最常用的一种通信技术,通常用于单片机和电脑之间以及单片机和单片机之间的通信。单片机的串口是全双工异步串口通信方式,通过TXD引脚发送,RXD引脚接收输入。
串行通信的实现方式--数据传输方式
同步通信:接收时钟与发送进钟严格同步,通常要有同步时钟,日常应用在大批量数据传输上。例(SPI、IIC通信接口)
异步通信:接收时钟与发送进钟不需要严格同步,但是必须设置相同的波特率,否则传输数据会产生异常。例(单总线,uart)
串行通信的实现方式--数据传输方向
根据串行通信的数据传输方向,串行通信方式分为三种:单工、单双工、全双工。
单工: 信息只可以从一方传向另一方。
半双工: 任意时刻信息都可以从一方传向另一方,但不能同时发生。即A-->B或B-->A。
全双工:任意时刻信息都可以双向的传输,即A-->B且B-->A。
串口通信的基础概念
串口通讯在协议层中规定了数据包的内容,具体包括起始位、主体数据(8位或9位)、校验位以及停止位,通讯的双方必须将数据包的格式约定一致才能正常收发数据。 1、波特率:由于异步通信中没有时钟信号,所以接收双方要约定好波特率,即每秒传输的码元个数,以便对信号进行解码,常见的波特率有4800、9600、115200等。 2、起始和停止信号:数据包的首尾分别是起始位和停止位,数据包的起始信号由一个逻辑0的数据位表示,停止位信号可由0.5、1、1.5、2个逻辑1的数据位表示,双方需约定一致。 3、有效数据:有效数据规定了主题数据的长度,一般为8或9位 4、数据校验;在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无(noparity)。
USART工作过程(usart比uart多了同步的功能)
UART传输数据依靠的是UART总线,数据总线用于通过微控制器等其他设备将数据发送到UART。数据以并行形式从数据总线传输发送到UART。UART从数据总线获得并行数据之后,它将添加起始位,奇偶校验位和停止位,从而创建数据包。接下来,数据包在Tx引脚上逐位串行输出。UART接收端则在其Rx引脚上逐位读取数据包。然后,接收UART将数据转换回并行形式,并删除起始位,奇偶校验位和停止位。最后,接收UART将数据包并行传输到接收端的数据总线。
介绍了其工作过程,我们现在看看其关键代码实现,下面是对USART串口配置的初始化代码,基本没有什么难的,请自动忽略NVIC的部分下一篇主要讲他:
1) 串口时钟使能,GPIO 时钟使能
2) 串口复位
3) GPIO 端口模式设置
4) 串口参数初始化
5) 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
6) 使能串口
void uart_init(u32 bound){
//定义需要用到的结构体
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能GPIOA与USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
//配置GPIO PA9为输出端 PA10为输入端
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//NVIC配置 下个主要讲
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//USART的初始化配置,根据上面USART的基础知识来配
USART_InitStructure.USART_BaudRate = bound; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //位长
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //是否设置奇偶校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//工作方式为收发
USART_Init(USART1, &USART_InitStructure); //串口1初始化
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启串口1中断
USART_Cmd(USART1, ENABLE); //串口1使能
}
加上重定义代码段就可以直接用printf()来给上位机传数据了
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);
USART1->DR = (u8) ch;
return ch;
}
那上位机如何给单片机传输数据呢?请看下面的代码:
void USART1_IRQHandler(void)
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
Res =USART_ReceiveData(USART1); //保存当前传送来的数据
if((USART_RX_STA&0x8000)==0) //如果接收未完成
{
if(USART_RX_STA&0x4000)//如果次高位为1
{
if(Res!=0x0a)USART_RX_STA=0;//次高位已经为1,所以本次接收结果一定为0x0a,不为0x0a的话就是接收过程错误,重新接受。
else USART_RX_STA|=0x8000;//此时的数据为0x0a,则将最高位改为1代表接受已经完成
}
else
{
if(Res==0x0d)USART_RX_STA|=0x4000; //次高位不为1,但是本次数据为0x0d,则将次高位设置为1
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//次高位不为1,本次数据也不是为0x0d,缓冲区加1
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//超出最大数据容量,接受错误,重新接收
}
}
}
}
}
想信许多没有基础的同学看这这些代码很蒙,没关系下来我给大家介绍一下代码的意思。
上位机给串口发送数据时,都会给数据末尾加上0x0d,0x0a。0x0d是回车的意思,0x0a是换行的意思。由此我们就可以判断数据何时发送结束了。
USART_RX_STA是接收状态标记。是16位的数据,他的最高位可以判断接受是否完成,次高位为判断数据是否为回车,回车的ASCLL是0X0d。其他14位用来存储数据的长度。USART_RX_BUF[]是接收缓冲区,他被下面代码定义为最大可以接收200个字节(这是usart.h里的代码)。
#define USART_REC_LEN 200
extern u8 USART_RX_BUF[USART_REC_LEN];
上面代码其实就是为了上位机给的数据被成功的接收,很难表达出来,详细注释都给了,基本没很大难度,有需要私聊。
主函数如下,目的是将上位机给单片机的数据又返回给上位机,便于观察是否传输正确:
while(1)
{
if(USART_RX_STA&0x8000) //最高位为1,接收完成
{
len=USART_RX_STA&0x3fff; //取出数据长度到len
//下面为函数重定义,将单片机收到的数据再返回给上位机,主要起了提示作用
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);
}
//传输完成清空标志位
USART_RX_STA=0;
}
//此时没有数据可以输出给上位机,请再发送数据
else
{
times++;
if(times%200==0)printf("请输入数据:\\r\\n");
delay_ms(10);
}
}
}
下篇和下下篇为NVIC中断管理和ADC模块。我会加急更新!!!!
以上是关于单片机与电脑串口通信,无论发啥信息都收到00,求问是怎么回事?代码见下面。的主要内容,如果未能解决你的问题,请参考以下文章
单片机与DWIN屏串口通信时,单片机能与电脑通信,屏能与电脑通信,而单片机与屏不能通信。不能通信是为什么