单片机与电脑串口通信,无论发啥信息都收到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;

当然老是接收到00了,我给你解释一下:你的串口中断函数不止是在接收数据的时候执行,在执行while(!Ti);时等待发送完成时也会触发中断函数,此时sbuf里面为空,数据之前被取走了。解决办法:把串口中断函数改为:void ser()interrupt 4
{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,求问是怎么回事?代码见下面。的主要内容,如果未能解决你的问题,请参考以下文章

5V单片机与3.3V单片机串口通信问题

单片机与DWIN屏串口通信时,单片机能与电脑通信,屏能与电脑通信,而单片机与屏不能通信。不能通信是为什么

借助虚拟串口驱动软件模拟Proteus中51单片机与PC端串口调试助手软件相互通信

5V单片机与3.3V单片机串口通信问题

单片机串口通信过程中电脑死机

单片机: EEPROM和串口通信