1. 通信协议
• 假如,如果有两个芯片要通信该怎么办,这时候就需要用到串口了。
• 串口,其实就是一种通信接口,是可以用来传输数据的,如图:

• 这里的关键是两个引脚要交叉接线,从而可以实现双向通信。Tx --- Rx Rx --- Tx。
1.1 通信协议-》其实就是通信的格式
• 如图:

• 这是如果,串口不工作的时候(空闲状态下),总线上是表现出高电压状态。
• 如果突然出现了低电压,就说明 数据传输 的开始。
• 后8位或者9位表示的数据位。
• 如果启动了校验,数据位后面会紧接着校验位。
• 最后停止位,表示一帧数据传输的结束,停止位表现的是 高电压。
1.2 而这里有三处地方可以配置的
• 数据位的长度可以是8位,也可以是9位。
• 可以有校验位,也可以没有检验位。
• 停止位的长度也可以配置的。
• 因此,停止位是1位的情况下,可以得出四种不同的配置方法,如图:

1.3 例子
• 如图

• 串口传输的是二进制数据,这里会把27转换为二进制0001 1011,并且是低位先传输。
• 发送字符串,首先会把每个字符的ASCII码找出来,然后在转化对应的二进制,然后再传输。
1.3.1 检验位,也分为奇校验和偶校验
• 奇校验:要求数据位中有奇数个1。
• 偶校验:要求数据位中有偶数个1。
• 例子,这里发送这一帧数据用的是,奇校验,发送85,转换为二进制之后数为0101 0101,这里有4个1,所以为了达到奇校验,所以校验位写1。
• 在发送的时候把校验位发送给接收方,接收方得到校验位之后,会重新计算一帧数据有多少个1,看和校验位是不是相等,如果不相等,就说发送过程出现错误。
• 如图:

2. USART(其实就是串口)
2.1 基本用法
• 串口的框图:

• 假如需要发送数字100发出去,首先会把100变成二进制数,然后放到发送数据寄存器中,然后通过Tx引脚发送出去。
• 假如需要接收88,首先会从Rx引脚那里接收一组二进制数,然后把二进制数存放到接收数据寄存器中,然后转变为十进制88。
2.2 移位寄存器和串并转换
2.2.1 串行 和 并行 的概念
• 串行通信:数据按位顺序 一次传输一个bit,即一次只传输一个bit的数据。
• 并行通信:多个bit同时传输,即一次可以传输多个bit的数据。
• 如图,发送数据寄存器里面保存待发送的数据,然后发送数据寄存器通过 并行传输 给发送移位寄存器,之后发送数据寄存器会被清除。还有一个接收移位寄存器,接收传输进来的数据,然后再并行传输给接收数据寄存器。

2.2.1.1 发送移位寄存器并到串的意思
• 并:发送数据寄存器里面的数据 并行传输 给发送移位寄存器。
• 串:待发送的数据到了发送移位寄存器里面,之后会通过Tx引脚,一个数据一个数据进行发送。如图所示:
• 如上图例子:数据一位一位地向外发送,并且数据也会一位一位在移位寄存器里面向前移动,直到移动完为止。
2.2.1.2 接收移位寄存器串到并的意思
• 如图:

• 串:Rx引脚接收到的输入数据,需要一位一位向前移动,在接收移位寄存器中。直到接收完一帧数据之后,才停止移动。
• 并:在接收完一帧数据,并且数据停止移动的时候,接收移位寄存器的值 通过 并行传输 发送给接收数据寄存器。并且并行传输完之后,会清空接收移位寄存器,准备下次接收。
2.2.2 数据帧格式的转换方法
• 如图,是通过这一部分控制电路配置的。

2.2.3 波特率的设置方法
• 如图

• 波特率:每秒钟最多传输多少位数据,这其中发送位越多,传输速度就越快。
• 如何转换的,如图

• 这里的分频器是对时钟信号进行除法操作。
• 得出的结果就是寄存器里面保存的值。
• 上述是算出一个比特率的步骤。
2.3 函数接口
• 如图

2.4 USART模块的引脚
• 如图:

• 一共有5个引脚,但是主要是用TX和RX引脚。
2.4.1引脚分布表
• 可以通过查数据手册,如图:

2.4.2 重映射表
• 如图(部分):


• 重映射,可以理解为"备用"。
2.4.3 IO模式配置表
• 如图:

• 这里的RX全双工,建议使用的上拉输入:
• 如果使用的是浮空,在RX引脚断了的情况下,可能会遭到外界的电磁干扰。
• 为什么不用下拉,这是因为串口总线电平的空闲的时候是高电压,如果用低电压,这可能导致总线认为这是一个开始信号。
2.5 标志位
2.5.1 TxE标志位
• TxE标志位是发送数据寄存器空的意思。

• 判断发送数据寄存器是否有数据。
2.5.2 TC标志位
• TC标志位是发送完成,就是两个寄存器都为空了,发送数据寄存器和发送移位寄存器为空。

3. 编程接口
3.1 USART_Cmd:使能

3.2 USART_GetFlagStatus:获得标志位的状态,从而可以知道串口的状态

3.3 USART_SendData:发送数据

3.4 实践
• 示例代码
cpp
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h"
void My_Usart_SendDatas(USART_TypeDef* USARTx,uint8_t * data,uint16_t size);
void My_Usart_Init();
int main(void)
{
Delay_Init();
My_Usart_Init();
//uint8_t datas[5] = {1,2,3,4,5};
//My_Usart_SendDatas(USART1,datas,5);
//printf("hello world\r\n");
while(1)
{
uint32_t currentTime = GetTick();//获取单片机当前时间
uint32_t millSeconds = currentTime % 1000;//毫秒
currentTime = currentTime / 1000;
uint32_t seconds = currentTime % 60;//秒
currentTime = currentTime / 60;
uint32_t minutes = currentTime % 60;//分
currentTime = currentTime / 60;
uint32_t hours = currentTime;
printf("%02u:%02u:%02u.%03u",hours,minutes,seconds,millSeconds);
Delay(100);
}
}
int fputc(int ch,FILE* f){//重写fputc函数
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//一直等,等到数据寄存器为空
USART_SendData(USART1,(uint8_t)ch);//格式化字符串 重定向 到串口
return ch;
}
void My_Usart_SendDatas(USART_TypeDef* USARTx,uint8_t * data,uint16_t size){
for(uint32_t i = 0; i < size; i++){
while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE) == RESET);//一直等,等到数据寄存器为空
USART_SendData(USARTx,data[i]);
}
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC) == RESET);//等待移位寄存器发完
USART_ClearFlag(USARTx,USART_FLAG_TC);
}
//初始化USART1,并且波特率是115200,8位数据位,1位停止位,没有校验位
void My_Usart_Init(){
//配置GPIO
//这是使用重映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//重映射USART
GPIO_InitTypeDef gpio_initstruct = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//TX
gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_initstruct.GPIO_Pin = GPIO_Pin_6;
gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB,&gpio_initstruct);
//RX
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
gpio_initstruct.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOB,&gpio_initstruct);
//配置的是USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
USART_InitTypeDef usart_initstruct = {0};
usart_initstruct.USART_BaudRate = 115200;
usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart_initstruct.USART_Parity = USART_Parity_No;
usart_initstruct.USART_StopBits = USART_StopBits_1;
usart_initstruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&usart_initstruct);//初始化USART1
USART_Cmd(USART1,ENABLE);//使能USART1
//这是使用复用功能
//配置GPIO
//GPIO_InitTypeDef gpio_initstruct = {0};
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// //TX
// gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
// gpio_initstruct.GPIO_Pin = GPIO_Pin_9;
// gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
//
// GPIO_Init(GPIOA,&gpio_initstruct);
//
// //RX
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
// gpio_initstruct.GPIO_Pin = GPIO_Pin_10;
//
// GPIO_Init(GPIOA,&gpio_initstruct);
}
4. 接收数据
4.1 标志位
• RxNE:接收数据寄存器非空。如图:

4.2 错误的标志位
4.2.1 PE:奇偶校验错,如图:

4.2.2 FE:帧格式错误,如图:

4.2.3 NE:噪声错,这个对一个bit数据进行多次采样的,如果每次采样都不一样,就说明有噪声错。如图:

4.2.4 ORE:过载错,覆盖移位寄存器的值,来不及取走数据。如图:

5. 实践
• 用串口控制led,示例代码:
cpp
#include "stm32f10x.h"
#include "stdio.h"
#include "delay.h"
void My_Usart_Init();
void My_USART_Receive();
void My_LED_Init();
int main(void)
{
My_Usart_Init();
My_LED_Init();
while(1)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == RESET);//一直等接收数据寄存器有数据
uint16_t byteRecv = USART_ReceiveData(USART1);//接收数据
if(byteRecv == '0'){//灭
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}else if(byteRecv == '1'){//亮
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
USART_ClearFlag(USART1,USART_FLAG_RXNE);
}
}
void My_LED_Init(){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef gpio_initstruct = {0};
gpio_initstruct.GPIO_Mode = GPIO_Mode_Out_OD;
gpio_initstruct.GPIO_Pin = GPIO_Pin_13;
gpio_initstruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&gpio_initstruct);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//灭
}
//初始化USART1,并且波特率是115200,8位数据位,1位停止位,没有校验位
void My_Usart_Init(){
//配置GPIO
//这是使用重映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟
GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//重映射USART
GPIO_InitTypeDef gpio_initstruct = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
//TX
gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
gpio_initstruct.GPIO_Pin = GPIO_Pin_6;
gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB,&gpio_initstruct);
//RX
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
gpio_initstruct.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOB,&gpio_initstruct);
//配置的是USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//开启USART1的时钟
USART_InitTypeDef usart_initstruct = {0};
usart_initstruct.USART_BaudRate = 115200;
usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart_initstruct.USART_Parity = USART_Parity_No;
usart_initstruct.USART_StopBits = USART_StopBits_1;
usart_initstruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&usart_initstruct);//初始化USART1
USART_Cmd(USART1,ENABLE);//使能USART1
//这是使用复用功能
//配置GPIO
//GPIO_InitTypeDef gpio_initstruct = {0};
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// //TX
// gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;
// gpio_initstruct.GPIO_Pin = GPIO_Pin_9;
// gpio_initstruct.GPIO_Speed = GPIO_Speed_10MHz;
//
// GPIO_Init(GPIOA,&gpio_initstruct);
//
// //RX
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// gpio_initstruct.GPIO_Mode = GPIO_Mode_IPU;
// gpio_initstruct.GPIO_Pin = GPIO_Pin_10;
//
// GPIO_Init(GPIOA,&gpio_initstruct);
}