学习板:STM32F103ZET6
USART为通用同步异步收发器,支持同步单向通信和半双工单线通信,也支持LIN(局部互连网),智能卡协议和IrDA(红外数据组织)SIR ENDEC规范,以及调制解调器(CTS/RTS)操作。
USART双向通信至少需要两个引脚:收数据输入(RX)和发送数据输出(TX)。此外,CK引脚用于同步通信;CTS、RTS用于调制解调。
USART传输数据时,有一个起始位、数据位可通信双方软件约定8位或9位、停止位可设置为0.5或1位或1.5位或2位。
在传输线空闲状态下,TX线为高电平,起始位时,TX为低电平,故可以通过下降沿检测是否开始数据传输(后边还会详述);停止位时TX处于高电平。
(1)接收数据
外部设备——>串行输入移位寄存器——>输入数据缓冲寄存器——>MCU
(2)发送数据
MCU——>输出数据缓冲器——>串行输出移位寄存器——>外部设备
该位设置为1后,完成当前数据传输,会在TX线上发送一个断开符号,发送断开符号完成后该位被硬件置1 。空闲帧为高电平,断开帧为低电平,而起始位也为低电平。所以插入断开帧后,TX线会传送一个“1”,为了准确检测下一帧的起始位(低电平)。当然如果没有下一帧数据,会处于空闲状态,TX线保持高电平。
位1: 接收唤醒
该位设置USART是否为静默模式,该位不设置时,为默认正常工作状态。设置为唤醒模式时,需要先接收一个数据字节才能设置,否则无法唤醒。唤醒序列到来时,该位硬件清零。
静默模式:
①任何接收状态都不会被设置
②所有接收中断被禁止
③CR1寄存器位11 WAKE设置为0,可以被空闲总线唤醒;WAKE设置为1,可以被地址标记唤醒。
位2: 接收使能
该位使能接收,置1后开始搜索RX引脚上的起始位。
起始位:1110x0x0x0000(x为0或1,x为1时,认为有噪音)
在搜索起始位时,先检测低电平(因为空闲状态传输线上为高电平),检测到下降沿后,会通过两次采样。上图中的第3、5、7为第一次采样;第8、9、10为第二次采样。
如果俩次采样的每一次都全为0。(即图中红框中“每三位至少有2个0”,为全0),此时确定为起始位。
如果俩次采样中,有一次仅仅有2个0、或者两次采样每次都只有2个0,也会判断为起始位,此时表示有噪音,噪声标志位会被置1.
位3: 发送使能
位4: IDLE中断使能
该位使能空闲总线中断,该位置1后,使能中断。当检测到总线空闲时,SR寄存器的位4:IDLE硬件置1,发生中断。
位5~位8: :各中断使能
分别为接收缓冲区非空中断、发送完成中断、发送缓冲区空中断、帧错误中断。由SR寄存器可以得到状态。
位9~位10: :校验选择与校验控制
位10使能校验控制,根据帧长度的不同,校验码插入到不同的位置。校验码在数据位后面,停止位之前
位9选择奇偶校验。奇偶校验的目的是判断数据传输是否出错。偶校验通过校验位配置,使数据位与校验位的所有“1”加起来为偶数。奇校验通过校验位配置,使数据位与校验位的所有“1”加起来为奇数。
偶校验:如果数据位中1的个数为奇数,则校验位为1;如果数据位中1的个数为偶数,则校验位为0。
奇校验:如果数据位中1的个数为奇数,则校验位为0;如果数据位中1的个数为偶数,则校验位为1。
位11: :唤醒方法设置
该位设置唤醒静默状态下的唤醒方式。之前的位1设置什么工作状态,位1置1后进入静默状态。
WAKE=0时:
此时静默状态由空闲总线唤醒,即当检测到空闲帧后被唤醒;此时CR1寄存器位1(RWU)被硬件清零,USART进入正常工作状态。
WAKE=1时:
此时静默状态由地址标记唤醒,当传输的数据高位为1时,认为是地址,否则认为是数据。目标接收器的地址在第四位中存储低四位在CR2寄存器的位0:3设置。
当接收到的字节与接收器编程地址匹配时,被唤醒;否则还会处于静默状态;之前也总结到过,处于静默状态不会产生中断。
当接收到的字节与接收器编程地址匹配时,被唤醒,退出静默状态。CR1寄存器的位1被硬件清零,即USART处于正常工作状态。
位12: :设置字长
该位设置字长,停止位由CR2寄存器设置,之后与字长一起总结。
位3:0: :设备地址设置
这4位来设置目标设备的地址,用于静默状态下由地址标记唤醒USART设备。
位5: :断开符长度检测
该位用来设置断开符的长度。之前CR1寄存器位0置1后发送断开符。本寄存器位5设置断开符长度为10位或11位。
位6: :断开符中断使能
该位使能检测断开符中断。
如果检测到断开符,则状态寄存器SR的位8 LBD被硬件置1 ,当CR2寄存器位6 LBDIE设置为1,则会产生中断。
注意是在同步模式下设置极性和相位。与SPI时钟的极性和相位一致。
附SPI数据时钟序图:
位11: 时钟使能
位12~13: 设置停止位
①不设置时,默认1位停止位。
②2个停止位:用于常规USART模式、单线模式、调制解调器模式
③0.5个停止位:智能卡模式下接收数据
④1.5个停止位:智能卡模式下发送和接收数据
前面CR1寄存器设置过字长、奇偶校验、起始位,本寄存器位13:12设置停止位。其数据传输如下:
之前总结的CR1寄存器位0发送断开符。需要通过CR2寄存器位14使能断开符发送,使能后才能通过CR1寄存器位0发送断开符。
该中断用于DMA传送时的错误中断,所以必须先使能DMA,即需将本寄存器的位6置1 。
分别为红外模式使能、红外功耗、半双工选择、智能卡不应答设置、只能卡使能
位6~7:使能DMA发送和接收
位8~10:
分别为RTS硬件流使能、CTS硬件流使能、CTS中断使能。
位0:校验错误状态
该位在接收模式出现奇偶校验错误时,硬件置1 。注意清除方式:先读SR寄存器、再读DR寄存器。读DR寄存器,要保证DR寄存器非空,所以读之前一定要保证SR寄存器位5 RXNE位为1 。
位1:帧错误状态
当检测到同步错位,过多的噪声或者检测到断开符,该位被硬件置位。注意清除方式:先读SR寄存器、再读DR寄存器。
位2:噪声错误标志
在接收到的帧检测到噪音时,由硬件对该位置位。注意清除方式:先读SR寄存器、再读DR寄存器。
当DR寄存器非空,但是又有数据传到RDR移位寄存器时,出现过载错误。即当SR寄存器位5 RXNE位为1时,有数据传进RDR移位寄存器,出现过载错误。注意清除方式:先读SR寄存器、再读DR寄存器。
当总线处于空闲状态时,该位被硬件置1。注意清除方式:先读SR寄存器、再读DR寄存器。
位5:读数据寄存器非空
当数据从RDR移位寄存器转移到DR数据寄存器是,该位硬件置1 。注意清除方式:读DR寄存器或直接写入0。
位6:发送完成状态
当发送完一帧的数据,且TD寄存器数据被移入移位寄存器时,该位硬件置1 。注意清除方式:先读SR寄存器、再读DR寄存器。
数据寄存器分为发送数据寄存器TDR和接收数据寄存器RDR。
该寄存器的的位15:4储存分频器除法因子的整数部分,位3:0储存分频除法因子的小数部分。
波特率计算:
STM32F1中USART1搭载在APB2线上,时钟最大为72MHZ;USART2、USART3、USART4、USART5搭载在APB1上,时钟最大为36MHZ。
以USART1、72MHZ时钟、115200波特率为例:
根据上述公式:
USARTDIV=72000000/(16*115200)=39.0625
十进制39=100111B=0x27,故将0x27存入BRR寄存器位15:4
小数部分转二进制:
故十进制0.0625=0.0001B=0x 0.1
故BRR寄存器位3:0存储 1
参数2:
结构体
成员:
uint32_t USART_BaudRate; //波特率
uint16_t USART_WordLength; //字长
uint16_t USART_StopBits; //停止位
uint16_t USART_Parity; //校验码
uint16_t USART_Mode; //发送或接收模式
uint16_t USART_HardwareFlowControl; //硬件控制流
参数:
USART_Init()函数传递的那个结构体
操作:
波特率:9600
字长:8位
停止位:1位
校验位:无
模式:接收或发送
硬件控制流:无
参数1:
USART1~USART5
参数2:
结构体,成员:
typedef struct
{
uint16_t USART_Clock; //时钟使能
uint16_t USART_CPOL; //时钟极性
uint16_t USART_CPHA; //时钟相位
uint16_t USART_LastBit; //时钟最后一个脉冲是否从ck输出
} USART_ClockInitTypeDef;
设置为:
①不使能同步时钟
②极性位0
③相位为0
④时钟脉冲最后一个周期不从Ck输出
参数2:
ENABLE 或DISABLE
参数2:
USART_IT_PE //奇偶校验错误中断
USART_IT_TXE //发送数据寄存器空中断
USART_IT_TC //发送完成中断
USART_IT_RXNE //接收数据就绪中断
USART_IT_IDLE //空闲总线中断
USART_IT_LBD //断开标志中断
USART_IT_CTS //CTS中断
USART_IT_ERR //传输出错中断
参数3:
EnABLE 或DISABLE
参数1:
USART1~USART5
参数3:
ENABLE 或DISABLE
该函数在多处理器通信下的静默模式中使用的,使用地址标记来唤醒某个USART设备。
参数1:
USART1~USART5
参数2:
小于0xf的一个数
参数2:
参数1:
USART1~USART5
参数2:
ENABLE 或DISABLE
ENABLE 时将USARTx设置为静默模式,否则为正常工作模式。
参数2:
ENABLE 或DISABLE
参数2:
一个小于等于0x1ff的数,DR寄存器为低9位有效寄存器
DR寄存器可读、写
参数2:
USART_IT_PE //奇偶校验错误中断
USART_IT_TXE //发送数据寄存器空中断
USART_IT_TC //发送完成中断
USART_IT_RXNE //接收数据就绪中断
USART_IT_IDLE //空闲总线中断
USART_IT_LBD //断开标志中断
USART_IT_CTS //CTS中断
USART_FLAG_ORE //数据溢出中断
USART_FLAG_NE //噪声标记中断
USART_FLAG_FE //帧错误中断
返回:
返回SET表示发生中断
返回RESET表示未发生中断
参数2:
USART_IT_PE //奇偶校验错误中断
USART_IT_TXE //发送数据寄存器空中断
USART_IT_TC //发送完成中断
USART_IT_RXNE //接收数据就绪中断
USART_IT_IDLE //空闲总线中断
USART_IT_LBD //断开标志中断
USART_IT_CTS //CTS中断
USART_FLAG_ORE //数据溢出中断
USART_FLAG_NE //噪声标记中断
USART_FLAG_FE //帧错误中断
其中:
USART1使用RCC_APB2PeriphClockCmd()
使能
USART2~5使用RCC_APB1PeriphClockCmd
使能
使用USART_DeInit()
函数复位串口
使用GPIO_Init()
函数初始化GPIO
在《中文参考手册》GPIO章节的“外设的GPIO配置”中得到以下信息:
所以一定要注意在什么模式下,GPIO应该配置为什么模式。
最长用的是TX、RX全双工模式,所以TX引脚配置为推挽复用输出、RX引脚配置为浮空输入或上拉输入(空闲状态下,总线为高电平,所以可以为上拉输入)
使用 USART_Init()
函数初始化串口
使用USART_Cmd()
函数使能串口
使用USART_ITConfig()
配置中断
如果配置了中断,需要设置NVIC中断分组,使用NVIC_Init()
函数
使用USART_SendData()
函数发送数据
使用USART_ReceiveData()
函数接收数据
使用USART_GetFlagStatus()
函数获取USART状态
使用USART_GetITStatus()
函数获取中断状态
利用按键,通过串口1发送一个字符:按一下KEY_UP按键发送一个字符a;按一下KEY1发送一个字符b;按一下KEY2,发送一个字符c;按一下KEY3,发送字符d;通过串口监视器察看串口发送情况。
按键实验STM32F103五分钟入门系列(五)按键实验(库函数+寄存器)总结过,不再赘述。
在串口设置中,PA9为USART1的TX引脚,设置为复用推挽输出。PA10为USART1的RX引脚,设置为浮空输入(本实验没用到输入,所以可以不设置)。没有接收,所以接收中断、中断优先级分组都不用设置。
需要用到串口监视器,所以需要通过跳线帽,将PA9、PA10与RS232的引脚连接,通过RS232与电脑USB连接在一起
key.h代码:
#ifndef KEY_H
#define KEY_H
void KEY_Init(void);
#endif
key.c代码:
#include "stm32f10x.h"
#include "sys.h"
#include "key.h"
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct_A;
GPIO_InitTypeDef GPIO_InitStruct_E;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE , ENABLE);//使能GPIOA和GPIOE(PA0 PE2、3、4)
GPIO_InitStruct_A.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStruct_A.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct_A.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct_A);//PA0 key_up 下拉输入
GPIO_InitStruct_E.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct_E.GPIO_Pin=GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;
GPIO_InitStruct_E.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStruct_E);//PE2、3、4 key0、key1、key2 上拉输入
}
myusart.h代码:
#ifndef _USART_
#define _USART_
#include "stm32f10x.h"
void Myusart_Init(u32);
#endif
myusart.c代码:
#include "myusart.h"
#include "stm32f10x.h"
void Myusart_Init(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //TX
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //RX
USART_InitStructure.USART_BaudRate=baud;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
USART_Cmd(USART1, ENABLE);
}
#include "stm32f10x.h"
#include "myusart.h"
#include "delay.h"
#include "key.h"
char temp=0;
int main(void)
{
Myusart_Init(115200);
KEY_Init();
delay_init();
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//检测key_up按下
{
delay_ms(10); //消抖
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0))//检测key_up按下
{
temp='a';
}//取消按下key_up
if(temp=='a')
{
USART_SendData(USART1,temp);
temp=0;
}
}
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)//key0按下
{
delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)==0)//key0按下
{
temp='b';
}//取消按下key0
if(temp=='b')
{
USART_SendData(USART1,temp);
temp=0;
}
}
else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)//key1按下
{
delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)==0)//key1按下
{
temp='c';
}
//取消按下key1
if(temp=='c')
{
USART_SendData(USART1,temp);
temp=0;
}
}
else if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)//key2按下
{
delay_ms(10);
while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)//key2按下
{
temp='d';
}//取消按下key2
if(temp=='d')
{
USART_SendData(USART1,temp);
temp=0;
}
}
}
}
注意:两块板子必须共地!虽然不同的板子布线不一样,但是如果是四层以上板子,只需要将两块板子的任意GND引脚连起来就行,这样的话两片板子的负片GND就连在一起了。
F1板子发送字符给F4板子,F4板子做以下操作:
①如果F4接收到字符a,LED0亮、LED1灭;延迟200ms,LED0灭、LED1亮,延迟200ms。两个灯只呼吸一次。
②如果F4接收到字符b,LED0、LED1同时亮、延时200ms后,同时灭。两个灯只呼吸一次。
③如果F4接收到字符c,LED0亮一次,延迟200ms后灭,LED1保持熄灭。LED0呼吸一次。
④如果F4接收到字符d,LED1亮一次,延迟200ms后灭,LED0保持熄灭。LED1呼吸一次。
如果没有接收到字符或接收到其它字符,则LED0、LED1保持熄灭状态。
F4板子中,需要接收数据,所以需要配置接收完成中断,告诉单片机什么时候去读数。此外。由于呼吸灯只呼吸一次,所以每次执行完LED闪烁代码后,数据需要清零。
另外F1与F4的波特率要保持相同!
F1代码与例1代码完全相同。
led.h代码
#ifndef LED_H
#define LED_H
void LED_Init(void);
#endif
led.c代码
#include "sys.h"
#include "stm32f4xx.h"
#include "led.h"
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed=GPIO_High_Speed;
GPIO_Init(GPIOF,&GPIO_InitStructure);
GPIO_SetBits(GPIOF, GPIO_Pin_9|GPIO_Pin_10);//PB5置高电平
}
myusart.h代码
#ifndef _USART_
#define _USART_
#include "stm32f4xx.h"
void Myusart_Init(u32);
#endif
myusart.c代码
#include "myusart.h"
#include "stm32f4xx.h"
#include "sys.h"
void Myusart_Init(u32 baud)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //速度100MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化PA9,PA10
USART_InitStructure.USART_BaudRate=baud;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ; //抢占优先级 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级 3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
USART_Cmd(USART1, ENABLE);
}
extern u16 temp;
void USART1_IRQHandler()
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
temp=USART_ReceiveData(USART1);
}
}
#include "stm32f4xx.h"
#include "myusart.h"
#include "delay.h"
#include "led.h"
u16 temp=0;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Myusart_Init(115200);
LED_Init();
delay_init(168);
while(1)
{
if(temp=='a')
{
PFout(9)=0;//点亮LED0
PFout(10)=1;//熄灭LED1
delay_ms(200);
PFout(9)=1; //熄灭LED0
PFout(10)=0;//点亮LED1
delay_ms(200);
temp=0;
}
else if(temp=='b')
{
PFout(9)=0;
PFout(10)=0;
delay_ms(200);
PFout(9)=1;
PFout(10)=1;
delay_ms(200);
temp=0;
}
else if(temp=='c')
{
PFout(9)=0;
PFout(10)=1;
delay_ms(200);
PFout(9)=1;
delay_ms(200);
temp=0;
}
else if(temp=='d')
{
PFout(9)=1;
PFout(10)=0;
delay_ms(200);
PFout(10)=1;
delay_ms(200);
temp=0;
}
else
{
PFout(9)=1;
PFout(10)=1;
}
}
}
1、F1的TX(PA9)连接F4的RX(PA10)
2、F1与F4的任意GND引脚连接
按下KEY_UP键后:
按下KEY0后:
按下KEY1后:
按下KEY2键后: