浅谈STM32串口通信(一)基本介绍和一个字节传输的实现

自古美人都是妖i 提交于 2020-04-09 11:04:54

0 传输引脚

串口收发共需要三根线
在这里插入图片描述
其中,TX脚为发送引脚, RX脚为发送引脚, GND为地, 作为电平的参考. 如果A发B收, 只需要连接两条线: A的TX连接B的RX, 以及GND相连即可
在这里插入图片描述


1 传输一个字节

1.1 发送一个字节

首先, 从传输一个字节开始说起. 抛开奇偶校验,多个停止位的情况, 假设, 我们设置一帧数据有十位, 里边包含一个起始位, 八个数据位, 还有一个停止位. 那么,我们发送一个字节, 只需要一次传输.
在这里插入图片描述
我们以传输0x01为例, 它的时序图如下
在这里插入图片描述


那怎么把这个字节变成时序呢? 发送器会帮我们完成. 只要配置好相应的波特率和停止位等即可. 发送器在起始位之后, 加入0x01对应的八个数据位, 最后加上停止位, 即可完成发送一个字节的时序. (0x01会通过发送数据寄存器(TDR), 存放到移位寄存器, 在每个时钟沿触发移位).

1.2 接收一个字节

我们以接收0x01为例(假设接收来自1.1的时序).首先我们必须设置波特率, 停止位位数, 跟接收到的时序保持一致.
那接收这边怎么知道对方是时序是什么意思呢? 波特率一致. 假设波特率为9600, 那么发送一个位需要的时间位1/9600 秒. 当RX脚检测到起始位(一个位的低电平, 持续1/9600秒), 就知道, 接下来的1/9600秒, 传输的是数据位的第一位, 再接下来的1/9600秒, 传输的是数据位的第二位… 这样即可通过移位寄存器在合适的时间去读取电平, 并保存0x01到接收数据寄存器(RDR). 我们通过程序去读取RDR即可得到0x01.

2 代码

以STM32F1的串口1为例

2.1 配置

void uart_init(u32 bound){
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
	 
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
  
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置(接受到数据时,会产生接收中断, 执行中断服务程序USART1_IRQHandler())
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
  NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

  USART_InitStructure.USART_BaudRate = bound;//串口波特率
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
  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);//开启串口接受中断  
  USART_Cmd(USART1, ENABLE);                    //使能串口1 
}

2.2 发送一个字节

main函数:
int main(void)
{	 
	delay_init();	    	 	//延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(9600);	 		//串口初始化为 115200

	while(1)//每个500ms向串口1发送0x01
	{
		delay_ms(500);
		USART_SendData(USART1,0x01);
	}		
  return 0;
}

在这里插入图片描述
连接好之后, 打开串口调试助手设置波特率等, 可以看到, PC端每隔500ms, 接收到一个数据(0x01)
在这里插入图片描述

2.3 接收一个字节

串口1每接受完一帧数据, 都会跳转到串口中断服务程序USART1_IRQHandler(). 我们这么做, 在接收到一个字节的数据后, 把该数据加一, 然后发送出去. 例如接收到0x01, 那就发送0x02.

void USART1_IRQHandler(void)                	//串口1中断服务程序
	{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断
	{
	  	Res =USART_ReceiveData(USART1);	//读取接收到的数据	 
		USART_SendData(USART1,Res+1); //把该数据加一, 从串口1发送出去
    } 
} 

main函数:

int main(void)
{	 
	delay_init();	    	 	//延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
	uart_init(9600);	 		//串口初始化为 115200

	while(1)//死循环,什么都不做
	{
		delay_ms(500);
	}		
  return 0;
}

在串口调试助手,向单片机发送0x01时,可以看到, 单片机接收到了, 并且给电脑发回来0x02.
在这里插入图片描述
对于串口的理解仅上, 想进一步理解配置过程的, 建议好好研究一下参考手册.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!