当前位置:   article > 正文

STM32学习笔记(基于B站江科大标准库教程)_江科大stm32笔记

江科大stm32笔记

 USART串口通信

通信接口:
1.通信的⽬的:将⼀个设备的数据传送到另⼀个设备,扩展硬件系统。
2.通信协议:指定通信的规则,通信双⽅按照协议规则进⾏数据收发
3.全双⼯:通信双⽅能够同时进⾏双向通信,输入输出可以同时进行,⼀般有两根通信线(类似打电话)
   单⼯:只能输出或输入
   半双工:既可以输入也可以输出,但不能同时进行。
4.TX RX 是单端信号,它们的⾼低信号都是相对于 GND 的,严格上来说 GND 也算是
5.通信线,串⼜通信的 TX RX GND 是必须要接的。
6.串⼜通信有两根通信线(发送端 TX 和接收端 RX
7.TX RX 要交叉连接
8.当只需单向的数据传输时,可以只接⼀根通信线
9.当电平标准不⼀致时,需要加电平转换芯⽚
10.复杂⼀点的串⼜通信还有时钟引脚、硬件流控制的引脚

串口参数及时序:

 串口数据帧的整体结构:串口中,每⼀个字节都装载在⼀个数据帧(10或者11位)⾥⾯,每个数据帧都由起始位、数据位和停⽌位组成。数据位有8个,代表⼀个字节的8位;还可以在数据位的最后加⼀个奇偶校验位,这样数据位总共就是9位;其中有效载荷时前8位,代表1个字节,校验位跟在有效载荷后⾯,占1位。

波特率:规定串⼜通信的速率(串⼜⼀般使⽤异步通信,需要双⽅约定⼀个通信速率),例
如每隔 1s 发送⼀位,接收⽅也要每隔 1s 接收⼀位,接收快了,就会重复接收某些位,如果接
收慢了,就会漏掉某些位,发送和接收必须约定好速率,波特率本义是每秒传输码元的个
数,单位是码元 /s ,或者直接叫波特 (Baud) ,⽐特率是每秒传输的⽐特数,单位是 bit/s ,或
者叫 bps ,在⼆进制调制的情况下,⼀个码元就是⼀个 bit ,此时波特率就等于⽐特率,单⽚
机的串⼜通信,基本都是⼆进制调制,也就是⾼电平表⽰ 1 ,低电平表⽰ 0 ,⼀位就是 1bit
规定波特率为 1000bps ,表⽰ 1s 要发 1000 位,每⼀位的时间就是 1ms ,发送⽅每隔 1ms 发送
⼀位,接收⽅每隔 1ms 接收⼀位。
起始位:标志⼀个数据帧的开始,固定为低电平(串口的空闲状态是⾼电平,没有数据传输
的时候引脚必须置⾼电平,作为空闲状态)需要传输的时候先发送⼀个起始位 起始位必须
是低电平,来打破空闲状态的⾼电平,产⽣⼀个下降沿 (告诉接受设备,这⼀帧数据要开始
了),如果没有起始位,当发送 8 1 的时候,数据线⼀直都是⾼电平,没有任何波动,这
样接收⽅就不知道我是否发送数据,所以必须要有⼀个固定为低电平的起始位,产⽣下降
沿,来告诉接受设备,为要发送数据了 ----- 起始位固定为0,产⽣下降沿,表⽰传输开始.
停⽌位;在⼀个字节数据发送完成后,必须要有⼀个停⽌位,这个停⽌位的作⽤是, ⽤于数
据帧间隔,固定为⾼电平 ,同时这个停⽌位也是为下⼀个起始位做准备的,如果没有停⽌
位,那当为数据最后⼀位是 0 的时候,下次再发送新的⼀帧,就没法产⽣下降沿了 ----- 停⽌
位固定为 1 ,把引脚恢复成⾼电平,⽅便下⼀次的下降沿,如果没有数据了,引脚也为⾼电
平,代表空闲状态
数据位:表⽰数据帧的有效载荷, 1 为⾼电平, 0 位低电平, 低位先⾏。
校验位:⽤于数据验证,是根据数据位计算得来的,串口使⽤奇偶校验位⽅法,奇偶校验可
以⽤来判断数据传输是不是出错了,如果数据出错了可以选择丢弃或者要求重传,校验可以
选择三种⽅式,⽆校验、奇校验和偶校验。 ⽆校验就是不需要校验位,波形就是上图左边
的,起始位、数据位,停⽌位⼀共 3个部分。 奇校验和偶校验的波形就是上图右边的,起始
位、数据位、校验位、停⽌位,总共 4个部分。奇校验要求有效数据和校验位中1的个数位奇数。
定偶校验,则保证1 的个数是偶数。

 功能引脚:

TX:发送数据输出引脚   RX:接收数据输入引脚

SW_RX:用于智能卡通信的引脚

硬件数据流控:

nRTS(Request To Send) 是请求发送,是输出脚,n 表示低电平有效.使能RTS流控制,当USART接收器准备接收新数据时,nRST就置低电平,请求对方发送;接收寄存器已满时,nRST将设置为高电平。
nCTS(Clear To Send) 是清除发送,是输入脚, n表示 低电平有效.使能CTS流控制,发送器在发送下一帧之前会检测nCTS引脚,如果为低电平,表示可以发送数据;如果为高电平则在发送完当前数据帧后停止发送。
中断输出控制:中断申请位就是状态寄存器的各种标志位,状态寄存器这⾥有两个标志位⽐
较重要,⼀个是 TXE 发送寄存器空,另⼀个是 RXNE 接收寄存器空,这两个是判断发送状态
和接收状态的必要标志位,中断输出控制就是用来配置中断能否通向 NVIC。
波特率发⽣器:其实就是分频器, APB 时钟进⾏分频,得到发送和接收移位的时钟,时钟输
⼊是发 PCLKx(x=1 2) ,因为 USART1 挂载在 APB2 ,所以就是 PCLK2 的时钟,⼀般是 72M
其他的 USART 都挂载在 APB1 ,所以是 PCLK1 的时钟,⼀般是 36M ,之后时钟在进⾏⼀个分
频,除以⼀个 USARTDIV 的分频系数, USARTDIV 是⼀个数值,分为整数部分和⼩数部分,
因为有些波特率,⽤ 72M 除于⼀个整数的话,可能除不尽,会有误差,所以这⾥的分频系数
是⽀持⼩数点后 4 位的,分频就更加精准,之后分频完还要再除个 16 ,得到发送时钟和接收
器时钟,通向控制部分,然后右边,如果 TE TX Enable )为 1 ,就是发送器使能,发送部
分的波特率就有效,如果 RE RX Enable )为 1 ,就是接收器使能了,接收部分的波特率就
有效.

最左边的是波特率发⽣器,⽤于产⽣约定的通信速率,时钟来源是 PCLK2 1 ,经过波特率
发⽣器分频后,产⽣的时钟通向发送控制器和接收控制器,发送控制器和接收控制器⽤来控
制发送移位和接收移位,之后由发送数据寄存器和发送移位寄存器这两个寄存器的配合,将
数据⼀位⼀位的移出去,通过 GPIO ⼜的复⽤输出,输出到 TX 引脚,产⽣串⼜协议规定的波
形,这个移位寄存器是向右移的,是低位先⾏,当数据由数据寄存器转移到移位寄存器时,
会置⼀个 TXE 的标志位,通过判断这个标志位,就可以知道是不是可以写⼊下⼀个数据了,
接收部分也是类似的, RX 引脚的波形,通过 GPIO 输⼊,在接收控制器的控制下,⼀位⼀位
地移⼊接收移位寄存器,移完⼀帧数据后,数据就会统⼀转运到接收数据寄存器,在转移的
同时,置⼀个 RXNE 标志位,检查这个标志位,就可以知道是不是收到数据了,同时这个标
志位也可以去申请中断,这样就可以在收到数据时,直接进⼊中断函数,快速的读取和保存
数据,虽然有四个寄存器但是在软件层⾯上,只有⼀个 DR 寄存器可以供我们读写,写⼊ DR
时,数据⾛上⾯这条路,进⾏发送,读取 DR 时,数据⾛下⾯这条路,进⾏接收,这就是
USART 进⾏串⼜数据收发的过程,右下⾓是个开关控制.

数据帧下的时钟波就是之前说的同步时钟输出功能,在每一个数据位的中间都有一个时钟上升沿,时钟频率和数据速率一样,接收端可以在时钟上升沿处进行采集。 

一般选择9位字长有校验或者8位字长无校验,保证每一帧的有效载荷都是1字节.

 STM32的串口可以配置停⽌位为0.511.52,一般选择1位停⽌位。

串口的输出TX比输入RX简单得多,输出就定时翻转TX引脚高低电平;但是要保证采样频率和波特率一致,还要保证每次输入采样的位置正好处于每一位中间。

串口初始化:
第⼀步,开启时钟,把需要⽤的 USART GPIO 的时钟打开
第⼆步, GPIO 初始化,把 TX 配置成复⽤输出, RX 配置成输⼊
第三步,配置 USART ,直接⽤⼀个结构体,就可以配置好所有参数
第四步,如果只需要发送的功能就直接开启 USART ,如果需要接收的功能,还需要再配置
中断,在开启 USART 之前,加上 ITConfig NVIC 的代码就可以了
常用函数
  1. void USART_DeInit(USART_TypeDef* USARTx);//回复缺省值函数
  2. void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);//开启串口函数
  3. void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState);//开启串口中断函数
  4. void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq,FunctionalState NewState);//开启USART到DMA的触发通道函数
  5. void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//发送数据函数
  6. uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//接收数据
  7. FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_tUSART_FLAG);//在中断函数外获取标志位函数
  8. void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);//在中断函数外清除标志位函数
  9. ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);//在中断函数内获取标志位函数
  10. void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_tUSART_IT);//在中断函数内清除标志位函数

代码示例
  1. #include "stm32f10x.h" // Device header
  2. #include <stdio.h>
  3. #include <stdarg.h>
  4. void Serial_Init(void)
  5. {
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  10. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
  11. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  12. GPIO_Init(GPIOA, &GPIO_InitStructure);
  13. USART_InitTypeDef USART_InitStructure;
  14. USART_InitStructure.USART_BaudRate = 9600;//波特率
  15. USART_InitStructure.USART_HardwareFlowControl =
  16. USART_HardwareFlowControl_None;//⽆流控
  17. USART_InitStructure.USART_Mode = USART_Mode_Tx;//发送模式
  18. USART_InitStructure.USART_Parity = USART_Parity_No;//⽆校验位
  19. USART_InitStructure.USART_StopBits = USART_StopBits_1;//停⽌位⼀位
  20. USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位1字
  21. USART_Init(USART1, &USART_InitStructure);
  22. USART_Cmd(USART1, ENABLE);//开启串⼝
  23. }
  24. void Serial_SendByte(uint8_t Byte)//发送字符
  25. {
  26. USART_SendData(USART1, Byte);
  27. while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
  28. }
  29. void Serial_SendArray(uint8_t *Array, uint16_t Length)//发送数组
  30. {
  31. uint16_t i;
  32. for (i = 0; i < Length; i ++)
  33. {
  34. Serial_SendByte(Array[i]);
  35. }
  36. }
  37. void Serial_SendString(char *String)//发送字符串
  38. {
  39. uint8_t i;
  40. for (i = 0; String[i] != '\0'; i ++)
  41. {
  42. Serial_SendByte(String[i]);
  43. }
  44. }
  45. uint32_t Serial_Pow(uint32_t X, uint32_t Y)
  46. {
  47. uint32_t Result = 1;
  48. while (Y --)
  49. {
  50. Result *= X;
  51. }
  52. return Result;
  53. }
  54. void Serial_SendNumber(uint32_t Number, uint8_t Length)//发送数字
  55. {
  56. uint8_t i;
  57. for (i = 0; i < Length; i ++)
  58. {
  59. Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 +'0');
  60. }
  61. }
  62. int fputc(int ch, FILE *f)//重定向printf
  63. {
  64. Serial_SendByte(ch);
  65. return ch;
  66. }
  67. void Serial_Printf(char *format, ...)//重定向printf多串⼝使⽤
  68. {
  69. char String[100];
  70. va_list arg;
  71. va_start(arg, format);
  72. vsprintf(String, format, arg);
  73. va_end(arg);
  74. Serial_SendString(String);
  75. }
串口收发数据包
数据包的作⽤是:把⼀个个单独的数据给打包起来,⽅便进⾏多字节的数据通信.因为接收⽅可能从任意的位置接收,所以可能出现数据错位的现象,我们需要⼀种⽅式把数据进⾏分割,数据分割开来,分割成⼀批批数据包,接收的时候就知道了对应的数据. 数据包的任务,就 是把同⼀批的数据进⾏打包和分割。
串口数据包,通常使⽤的是额外添加包头包尾的这种⽅式
防⽌数据包包头包尾和数据重复的⽅法,第⼀种,限制载荷数据的范围,在发送的时候对数
据进⾏限幅,第⼆种,尽量使⽤固定长度的数据包,第三种,增加包头包尾的数量,并且让
它尽量呈现出载荷数据出现不了的状态
串⼜收发Hex数据包
串口收发Hex数据包

文本数据包:

串口收发⽂本数据包

 数据包的收发流程:

 

接收固定包长的数据包,设计⼀种能够记住不同状态的机制,在不同状态执⾏不同的操作,
同时还要进⾏状态的合理转移,这种程序设计思维叫做 状态机
第⼀个状态是等待包头,第⼆个状态是接收数据,第三个状态是等待包尾,每个状态需要⼀
个变量来标志⼀下,类似于置置标志位,标志位只有 0 1 ,状态机是多标志位的⼀种⽅式 执⾏流程是最开始 S = 0 ,收到⼀个数据,进中断,根据 S = 0 ,进⼊第⼀个状态的程序,判断数据是不是包头FF ,如果是 FF ,则代表收到包头,之后置 S = 1 ,退出中断,结束,这样下次再进中断,根据S = 1 ,就可以进⾏接收数据的程序了,在第⼀个状态,如果收到的不是FF 就说明数据包没有对齐,应该等待数据包包头的出现,这时状态仍然是 0 ,下次进中断,就还是判断包头的逻辑,直到出现FF ,才能转到下⼀个状态,之后出现了 FF ,就可以转移到接收数据的状态了,这时再收到数据,就可以直接把它存在数据中,另外再⽤⼀个变量,记录收了多少个数据,如果没收够4 个,就⼀直是接收状态,如果收够了,就置 S = 2, 下次进中断时,就可以进⼊下⼀个状态了,最后⼀个状态就是等待包尾,判断数据是不是FE,这样就可以置 S = 0 ,回到最初的状态,开始下⼀个轮回。
状态机使⽤的基本步骤:先根据项⽬要求,画⼏个圈,考虑好各个状态在什么情况下会进⾏
转移,如何转移,画好线和转移条件,最后根据图来进⾏编程,例如,做个菜单,按什么
键,切换什么菜单,执⾏什么程序。
发送数据包
  1. void Serial_SendPacket(void)
  2. {
  3. Serial_SendByte(0xFF);
  4. Serial_SendArray(Serial_TxPacket, 4);
  5. Serial_SendByte(0xFE);
  6. }

  1. void USART1_IRQHandler(void)
  2. {
  3. static uint8_t RxState = 0;//状态机变量
  4. static uint8_t pRxPacket = 0;//指示接收个数
  5. if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  6. {
  7. uint8_t RxData = USART_ReceiveData(USART1);
  8. if (RxState == 0)
  9. {
  10. if (RxData == 0xFF)
  11. {
  12. RxState = 1;
  13. pRxPacket = 0;
  14. }
  15. }
  16. else if (RxState == 1)
  17. {
  18. Serial_RxPacket[pRxPacket] = RxData;
  19. pRxPacket ++;
  20. if (pRxPacket >= 4)
  21. {
  22. RxState = 2;
  23. }
  24. }
  25. else if (RxState == 2)
  26. {
  27. if (RxData == 0xFE)
  28. {
  29. RxState = 0;
  30. Serial_RxFlag = 1;
  31. }
  32. }
  33. USART_ClearITPendingBit(USART1, USART_IT_RXNE);
  34. }
  35. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/457409
推荐阅读
相关标签
  

闽ICP备14008679号