目录
串口通信基本原理
处理器与外部设备通信的两种方式
并行通信
- 传输原理:数据各个位同时传输。
- 优点:速度快
- 缺点:占用引脚资源多
串行通信
-
传输原理:数据按位顺序传输。
-
优点:占用引脚资源少
-
缺点:速度相对较慢
-
按照数据传送方向,分为:
- 单工:数据传输只支持数据在一个方向上传输
- 半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
- 全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
- 串行通信的通信方式
- 同步通信:带时钟同步信号传输。
-SPI,IIC通信接口 - 异步通信:不带时钟同步信号。
-UART(通用异步收发器),全双工,单总线,要约定速度(波特率)
常见的串行通信接口:
STM32的串口通信接口
UART: 通用异步收发器
USART: 通用同步异步收发器
大容量STM32F10x系列芯片,包含3个USART和2个UART
UART异步通信方式引脚连接方法:
RXD:数据输入引脚。数据接受。
TXD:数据发送引脚。数据发送。
UART异步通信方式特点:
- 全双工异步通信。
- 分数波特率发生器系统,提供精确的波特率。
-发送和接受共用的可编程波特率,最高可达4.5Mbits/s - 可编程的数据字长度(8位或者9位);
- 可配置的停止位(支持1或者2位停止位);
- 可配置的使用DMA多缓冲器通信。
- 单独的发送器和接收器使能位。
- 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
- 多个带标志的中断源。触发中断。
- 其他:校验控制,四个错误检测标志。
STM32串口异步通信需要定义的参数:
- 起始位
- 数据位(8位或者9位)
- 奇偶校验位(第9位)
- 停止位(1,15,2位)
- 波特率设置
发射器控制(发射器时钟)和接收器控制(接收器时钟)受到同一个单元控制。
串口寄存器配置
常用的串口相关寄存器
USART_SR状态寄存器
USART_DR数据寄存器(只用到了位0-8)
USART_BRR波特率寄存器
USART_CR1控制寄存器(使能)
波特率计算方法
串口操作相关库函数(省略入口参数):
void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断
//DR寄存器
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
//SR寄存器
FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
USART_Init();
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
typedef struct
{
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_InitTypeDef;
串口配置的一般步骤
- 串口时钟使能,GPIO时钟使能:
RCC_APB2PeriphClockCmd();
- 串口复位:这一步不是必须的
USART_DeInit();
- GPIO端口模式设置:要查表“STM32中文参考手册”8.1.11,模式设置为GPIO_Mode_AF_PP(复用为串口一设置为复用推挽)
GPIO_Init();
- 串口参数初始化:
USART_Init();
- 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_Init();//优先级设置
USART_ITConfig();//确定开启哪个中断
- 使能串口:
USART_Cmd();
- 编写中断处理函数
USARTx_IRQHandler();
- 串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
- 串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
程序实践
main.c
void My_USART_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//使能时钟
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA ,ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1 ,ENABLE);
//GPIOA9模式设置为GPIO_Mode_AF_PP
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//GPIOA10模式设置为GPIO_Mode_AF_PP
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//串口初始化
USART_InitStructure.USART_BaudRate=115200;
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);
//使能哪个串口中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//确定开启哪个中断,并开启接收中断(即接收到数据执行中断服务函数)
//设置响应中断优先级
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//入口参数,在stm32f10x.h头文件447行
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//是否开启中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void) //接收到数据执行这个中断
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE))//判断是否接收到数据
{
res=USART_ReceiveData(USART1);//接受到的值赋给变量
USART_SendData(USART1,res);//发送接收到的数据
}
}
int main(void)
{
//中断设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组选择为2,两位响应两位抢占
My_USART_Init();
while(1);//使用中断接收,使用死循环
}
串口实验讲解
#define USART_REC_LEN 200 //定义最大接收字节数 200
u8 USART_RX_BUF[USART_REC_LEN];
//接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
u16 USART_RX_STA; //接收状态标记
程序要求,发送的字符是以回车换行结束(0x0D,0x0A)
eg:ABCDEFGHI…….(0x0D),(0x0A)
bit15标记为1时候提取USART_RX_BUF中bit13~0个有效数据
串口中断服务函数:
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//最高位如果是0,接收未完成,继续下面的接收
{
if(USART_RX_STA&0x4000)//接收到了0x0d 即bit14是否为1
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了 ,最高位设为1
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;//只可以用bit13~0
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
main();
int main(void)
{
u16 t;
u16 len;
u16 times=0;
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
KEY_Init(); //初始化与按键连接的硬件接口
while(1)
{
if(USART_RX_STA&0x8000) //接收完成返回1
{
len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
printf("\r\n您发送的消息为:\r\n\r\n");
for(t=0;t<len;t++)
{
USART_SendData(USART1, USART_RX_BUF[t]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
printf("\r\n\r\n");//插入换行
USART_RX_STA=0;
}else
{
times++;
if(times%5000==0)
{
printf("\r\n精英STM32开发板 串口实验\r\n");
printf("正点原子@ALIENTEK\r\n\r\n");
}
if(times%200==0)printf("请输入数据,以回车键结束\n");
if(times%30==0)LED0=!LED0;//闪烁LED,提示系统正在运行.
delay_ms(10);
}
}
}