USART串口协议

 TX(Transmit Exchange)是数据发送脚,RX(Receive Exchange)是数据接收脚

SCL(Serial Clock)是时钟,SDA(Serial Data)是数据

SCLK(Serial Clock)是时钟,MOSI(Master Output Slave Input)是主机输出数据脚,MISO(Master Input Slave Output)是主机输入数据脚,CS(Chip Select)是片选,用于指定通信的对象

CAN_H和CAN_L是两个差分数据脚,用两个引脚表示一个差分数据

DP(Data Positive)和DM(Data Minus)或者叫D+或D-,也是一对差分数据脚

异步的通信协议需要双方约定一个采样频率,并且需要加一些帧头帧尾等,进行采样位置的对齐。

单端信号:引脚的高低电平都是对GND的电压差,单端信号通信的双方必须要共地,就是把GND接在一起

使用差分信号可以极大地提高抗干扰特性,所以差分信号一般传输速度和距离都会非常高

多设备通信需要有一个寻址的过程

串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力

 

USB转串口模块:上面的芯片可以把串口协议转换为USB协议,一边是USB口可以插在电脑上,一边是串口的引脚,可以和支持串口的芯片接在一起

陀螺仪传感器模块:一边是串口的引脚,一边是I2C的引脚

蓝牙模块:下面四个脚是串口通信的引脚,上面的芯片可以和手机互联,实现手机遥控单片机的功能

直接从控制器出来的信号一般都是TTL电平,相同的电平才能互相通信,不同的电平信号需要加一个电平转换芯片,转接一下

在串口中,每个字节都装到一个数据帧里

串口的空闲状态是高电平,也就是没有数据传输的时候,传输的时候必须要先发送一个起始位,这个起始位必须是低电平,来打破空闲状态的高电平,产生一个下降沿,这个下降沿就告诉接收设备这一帧数据要开始了,数据发送完要有一个停止位,用于数据帧间隔,固定为高电平,为下一个起始位做准备。这里串口使用的是奇偶校验的数据验证方法,校验可以选择3种方式:无校验、奇校验和偶校验

串口的停止位是可以配置的,可以选择1位、1.5位、2位等

USART比UART多了个时钟输出,它只支持时钟输出,不支持时钟输入,USART模式更多是为了兼容别的协议或者特殊用途而设计的,并不支持两个USART之间进行同步通信

波特率发生器就是用来配置波特率的,其实就是一个分频器,波特率发生器对总线时钟进行分频,得到波特率时钟。

硬件流控制:比如A设备有个TX向B设备的RX发送数据,A设备一直在发,发的太快了,B处理不过来,如果没有硬件流控制,那B只能抛弃新数据或者覆盖原数据了,如果有硬件流控制,在硬件电路上,会多出一根线,如果B没准备好接收,就置高电平,如果准备好了,就置低电平,A接收到了B反馈的准备信号,就只会在B准备好的时候发数据,如果B没准备好,那数据就不会发送出去。用来防止因为B处理慢而导致数据丢失的问题

DMA:串口支持DMA进行数据转运,如果有大量的数据进行收发,可以使用DMA转运数据,减轻CPU的负担

IrDA:用于红外通信,一个红外发光管,一个红外接收管,然后靠闪烁红外灯光通信

LIN:局域网的通信协议

USART1是APB2上的设备,USART2、USART3是APB1上的设备

SW_RX、IRDA_OUT/IN是智能卡和IrDA通信的引脚,可以不管

发送数据寄存器和接收数据寄存器占用同一个地址,在程序上只表现为一个寄存器,但在硬件中分成两个寄存器,TDR是只写的,RDR是只读的

发送移位寄存器的作用:把一个字节的数据一位一位地移出去,比如你在某个时候给TDR写入了0X55这个数据,在寄存器里就是二进制存储,0101 0101,那么此时硬件检测到你写入数据了,它就会检查当前移位寄存器是不是有数据正在移位,如果没有,0101 0101就会全部移动到发送移位寄存器,准备发送。当数据从TDR移动到移位寄存器时,会置一个标志位,叫TXE(TX Empty),发送寄存器空,我们检查这个标志位,如果置1了,我们就可以在TDR写入下一个数据了。注意一下,当TXE标志位置1时,数据其实还没有发送出去。发送移位寄存器在下面地发送器控制的驱动下,向右移位,然后一位一位地把数据输出到TX引脚,这里是向右移位的,所以正好和串口协议规定的低位先行是一致的。

接收移位寄存器在接收器控制的驱动下一位一位地读取RX电平,先放在最高位,然后向右移,移位8次之后,就能接收一个字节了。当一个字节接收完成后,就会转移到接收数据寄存器RDR里,在转移的过程中,也会置一个标志位,叫RXNE(RX Not Empty),接收数据寄存器非空,当我们检测到RXNE置1之后,就可以把数据读走了。当接收移位寄存器中的数据转移到RDR,就可以直接移位接收下一帧数据了。发送需要添加帧头帧尾,接收需要剔除帧头帧尾,这些操作,它内部有电路会自动执行。

硬件数据流控,也就是硬件流控制,简称流控。流控有两个引脚,一个是nRTS,一个是nCTS,nRTS(Request To Send)是请求发送,是输出脚,也就是告诉别人,我当前能不能收。nCTS(Clear To Send)是清除发送,是输入脚,也就是用于接收别人nRTS的信号的。这里加个n意思是低电平有效。

首先,得找另一个支持流控的串口,它的TX接我的RX,然后我的RTS要输出一个能不能接收的反馈信号,接到对方的CTS,当我能接收的时候,RTS就置低电平,请求对方发送,对方的CTS接收到之后,就可以一直发,当我处理不过来时,比如接收数据寄存器我一直没有读,又有新的数据过来了,现在就代表我没有及时处理,那RTS就会置高电平,对方CTS接收到之后,就会暂停发送,直到接收数据寄存器被移走,RTS置低电平,新的数据才会继续发送。那反过来,当我的TX给对方发送数据时,我们的CTS就要接到对方的RTS,用于判断对方能不能接收。

右边的SCLK控制到SCLK部分的电路用于产生同步的时钟信号,它是配合发送移位输出的,发送寄存器每移位一次,同步时钟电平就跳变一个周期,时钟告诉对方,我移出去一位数据了,你看要不要让我这个时钟信号来指导你接收一下。当然这个时钟只支持输出,不支持输入。所以两个USART之间,不能实现同步的串口通信。这个时钟信号的用途:第一个用途就是兼容别的协议,比如串口加上时钟之后,就跟SPI协议特别像,所以有了时钟输出的串口,就可以兼容SPI。另外这个时钟也可以做自适应波特率,比如接收设备不确定发送设备给的什么波特率,那就可以测量一下这个时钟的周期,然后再计算得到波特率。

中间的唤醒单元,这部分的作用是实现串口挂载多设备,多设备是在一条总线上可以接多个从设备,每个设备分配一个地址,我想跟某个设备通信,就先进行寻址,确定通信对象,再进行数据收发。那这个唤醒单元就可以用来实现多设备的功能,在这里(USART地址)可以给串口分配一个地址,当你发送指定地址时,此设备唤醒开始工作,当你发送别的设备地址时,别的设备就唤醒工作。这个设备没收到地址,就会保持沉默。

USART中断控制,中断申请位,就是状态寄存器的各种标志位(CTS到PE),有两个标志位比较重要,一个是TXE发送寄存器空,另一个是RXNE接收寄存器非空,这两个是判断发送状态和接收状态的必要标志位。中断控制就是配置中断是不是通向NVIC。

再下面是波特率发生器部分,波特率发生器其实就是分频器,APB时钟进行分频,得到发送和接收移位的时钟,再除以USARTDIV进行分频,是一个数值,有整数部分和小数部分,支持小数点后四位,分频完后还要再除个16,得到发送器时钟和接收器时钟,通向控制部分,然后右边这里如果TE(TX Enable)为1,就是发送器使能了,发送部分的波特率就有效,如果RE(RX Enable)为1,就是接收器使能了,接收部分的波特率就有效。

时钟的最后一位,可以通过这个LBCL位控制要不要输出,另外这个时钟的极性、相位什么的也可以通过配置寄存器配置。空闲帧和断开帧是局域网协议用的,我们串口用不着,不用管。

STM32可以配置停止位长度为0.5、1、1.5、2这四种,这四种参数的区别就是停止位的时长不一样,倍数跟长度一样。

输入的时候要保证每次输入采样的位置要正好处于每一位的正中间,只有在每一位的正中间采样,这样高低电平读进来才是最可靠的。如果采样点过于靠前或靠后,那有可能高低电平还正在翻转,电平不稳定,或者稍有误差,数据就采样错了。另外输入最好还要对噪声有一定的判断能力,如果是噪声最好能置个标志位提醒我一下。

STM32如何设计输入电路

首先是起始位侦测,当输入电路侦测到一个数据帧的起始位后,就会以波特率的频率连续采样一帧数据,同时,从起始位开始,采样位置就要对齐到位的正中间,只要第一位对齐了,后面肯定也是对齐的。为了实现这些功能,首先输入的这部分电路对采样时钟进行了细分,它会以波特率的16倍频率进行采样,也就是在一位的时间里,可以进行16次采样,然后它的策略是,最开始空闲状态高电平,那采样就一直是1。在某个位置突然采到一个0,那么就说明在这两次采样之间出现了下降沿,如果没有任何噪声,那之后就应该是起始位了。在起始位,会进行连续16次采样,没有噪声的话,这16次采样,肯定就都是0。但是,实际电路还是会存在一些噪声的,所以即使出现下降沿了,也要在后续多采样几次,以防万一。根据手册描述这个接收电路还会在下降沿之后的第3次、5次、7次进行一批采样,在第8次、9次、10次再进行一批采样,且这两批采样,都要要求每3位里面至少应有2个0。如果有轻微的噪声,导致这里3位里面只有两个0,另一个是1,那也算是检测到了起始位。但是在状态寄存器里会置一个NE(Noise Error),噪声标志位,就是提醒你一下,数据我收到了,但是有噪声,你悠着点用。如果这里3位里面,只有1个0,那就不算检测到了起始位,可能前面那个下降沿是噪声导致的。这时电路就忽略前面的数据,重新开始捕捉下降沿。这就是STM32的串口,在接收过程中,对噪声的处理。如果通过了这个起始位侦测,那接收状态就由空闲,变为接收起始位,同时第8、9、10次采样的位置,就正好是起始位的正中间,之后,接收数据位时,就都在第8、9、10次,进行采样。这样就能保证采样位置在位的正中间了。

从1到16是一个数据位的时间长度,在一个数据位,有16个采样时钟,由于起始位侦测已经对齐了采样时钟,所以这里就直接在第8、9、位次采样数据位,为了保证数据的可靠性,这里是连续采样3次,没有噪声的理想情况下,这3次肯定全为1或者全为0,全为1就认为收到了1,全为0就认为收到了0。如果有噪声导致3次采样不是全为1或者全为0,那它就按照2:1的规则来,2次为1就认为是1,2次为0就认为是0。在这种情况噪声标志位也会置为1。

USB有4根线,GND、D+、D-、VCC,USB标准供电是5V,然后中间D+和D-是通信线,走的也是USB协议,所以这里需要一个CH340芯片转换一下,转换之后输出的就是TXD和RXD,是串口协议,最后通过排针引出来,需要注意的就是这边的供电策略,首先所有的电都是从VCC+5V来的,然后VCC+5V通过稳压管电路进行降压,得到VCC+3.3V,之后VCC+5V和VCC+3.3V都通过排针引出来了,所以这个第6脚和第4脚,是分别有5V和3.3V输出的。第5脚CH340G VCC实际上是CH340的电源输入脚,一般我们这个模块的排针会有一个跳线帽,这个跳线帽需要插在4、5脚,或者5、6脚上,右边有文字说明,所以这个跳线帽是用来选择通信电平的,也是给CH340芯片供电的。

 SR(状态寄存器)、DR(数据寄存器)、CR(配置寄存器)

------------------------------------------------------------------------------------

第一步,开启时钟,把需要用的USART和GPIO的时钟打开

第二步,GPIO初始化,把TX配置成复用模式,RX配置成输入

第三步,配置USART,直接使用一个结构体就可以把所有参数配置好

第四步,如果你只需要发送的功能,就直接开启USART,初始化就结束了。如果你需要接收的功能,可能还需要配置中断,那就在开启USART之前,再加上ITConfig和NVIC的代码就行了

void USART_DeInit(USART_TypeDef* USARTx);
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);

//配置同步时钟输出,包括时钟是不是要输出,时钟的极性相位等参数,也是用结构体配置
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

//开启USART到DMA的触发通道
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);

//设置地址、唤醒、LIN
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//发送数据,写DR寄存器
//DR寄存器内部有4个寄存器,控制发送与接收
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//接收数据,读DR寄存器

//智能卡、IrDA
void USART_SendBreak(USART_TypeDef* USARTx);
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

一根线只能有一个输出,但可以有多个输入。

输出是复用推挽输出,输入是浮空输入或者上拉输入,因为串口空闲的时候是高电平,所以不使用下拉输入

Ctrl+Alt+空格可以联想代码

TXE:发送数据寄存器空(Transmit data register empty)当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。即下一次SendData时,这个标志位会自动清零。

MicroLIB是Keil为嵌入式平台优化的一个精简库,printf函数就可以用MicroLIB

printf重定向,将printf函数打印的东西输出到串口,因为printf函数默认是输出到屏幕,我们单片机没有屏幕,所以要进行重定向。步骤就是,在串口模块里,最开始加上#include<stdio.h>,之后重写fputc函数

int fputc(int ch, FILE *f)
{
    Serial_SendByte(ch);
    return ch;
}

 fputc是printf函数的底层,printf函数在打印的时候,就是不断调用fputc函数一个一个打印的。我们把fputc函数重定向到了串口,那printf自然就输出到串口了。

sprintf可以把格式化字符输出到一个字符串里,先定义一个字符串

//第一个参数是打印输出的位置(字符串),之后就跟printf一样
sprintf(String,"Num=%d\r\n",666);
Serial_SendString(String);

封装sprintf

#include<stdarg.h>
void Serial_Printf(char *format, ...)//参数用来接收格式化字符串
{
    char String[100];
    va_list arg;//定义一个参数列表变量
    va_start(arg, format);//从format位置开始接收参数表,放在arg里面
    vsprintf(String, format, arg);//sprintf改写成vsprintf,因为sprintf只能接收直接写的参数
    va_end(arg);//释放参数列表
    Serial_SendString(String);
}

C/C++---Mac Controls(杂项控制栏)---写上--no-nultibyte-chars---编译

RXNE:该数据寄存器非空(Read data register not empty)当RDR移位寄存器中的数据被转移到USART_DR寄存器中,该位被硬件置位。如果USART_CR1寄存器中的RXNEIE为1,则产生中断。对USART_DR的读操作可以将该位清0。RXNE位也可以通过写入0来清除,只有在多缓存通讯中才推荐这种清除程序。

 

 

 

头文件的extern声明数组可以不表明元素个数,比如extern abc[]; 

FlyMcu是串口下载,STLINK Utility是STLINK下载

BootLoader在复位之前切换,USART1接收的数据将会放到主闪存

小机器人BootLoader,是ST公司写好的一段程序代码,这段程序的存储位置,就是ROM区的最后,1FFFF000,这段区域叫做系统存储器,存储的是BootLoader程序,或者叫自举程序,用途是程序自我更新,串口下载。在更新过程中,BootLoader接收USART1数据,刷新到程序存储器,这时主程序就处于瘫痪状态,更新好之后,再启动主程序,执行新程序,这就是串口下载的流程。

切换BOOT引脚的麻烦解决:因为我们不使用流控,使用USB转串口的RTS、DTR(都是作为输出的引脚)引脚当作普通的GPIO引脚控制BOOT引脚和RST复位引脚,当然外围还需要设计控制电路,一般可以用两三个三极管开关来控制,这样就不需要频繁切换跳线帽和按复位键了。一般最常用的配置是DTR的低电平复位,RTS高电平进BootLoader。

.bin格式是没有地址信息的原始数据文件,.hex文件是有地址信息的。

FLASH擦除之后所有的数据都是FF。

选项字节,存储一些独立于程序代码的配置参数。为了保护程序安全,选项字节里有一个参数可以配置读保护,如果阻止读出了,再回到Keil下载程序就会失败,需要取消读保护才能下载程序。在取消读保护时,会同时清空芯片的程序。还有一些硬件选项字节,其中包括看门狗,停机和待机模式不产生复位。选项字节存储不随程序变化而变化的参数,无论程序怎么更新,选项字节的数据都可以不变。还可以写保护,写保护开启后如果下载时写入保护区的话就会出错。 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值