嵌入式 单片机 串口通信
常见串行
通信接口有哪些?
串口配置的一般步骤是怎样的?
回帖(1)
2022-2-28 15:14:55
1 对应引脚
参考中文参考手册v10——8.3.8节
2 通信设置
1). 处理器与外部设备通信
并行通信:速度快 占用引脚多
串行通信:速度慢 占用引脚少
单工:单方向通信只能由A到B
半双工:可以双向传输,但同一时刻只能在一个方向
全双工:允许同时双向传输,两设备都具有独立的发送和接收能力
通信方式
同步通信:带时钟同步信号传输 SPI IIC通信接口
异步通信:不带时钟同步信号 UART 需要设置波特率
常见串行通信接口
2). 异步通信需要定义的参数
起始位 数据位 奇偶校验位 停止位 波特率设置
3). 波特率计算
tx/rx波特率=Fpclx/(16*USARTDIV)
上式中,F PCLKx是给串口的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1);USARTDIV是一个无符号定点数。我们只要得到USARTDIV的值,就可以得到串口波特率寄存器USARTI->BRR的值,反过来,我们得到USARTI->BRR的值,也可以推导出USARTDIV的值。但我们更关心的是如何从USARTDIV的值得到UUSARTI->BRR的值,因为一-般我们知道的是波特率,和PCLKx的时钟,要求的就是USART BRR的值。下面我们来介绍如何通过USARTDIV得到串口USART BRR寄存器的值。假设我们的串口1要设置为115200的波特率,而PCLK2的时钟为72M。这样,我们根据上面的公式有:
USARTDIV=72000000/(115200* 16)= 39.0625
那么得到:
DIV_ Fraction= 16*0.0625= 1=0X01;
DIV_ Mantissa= :39 =0X27;
这样,我们就得到了USARTI->BRR的值为0X0271。只要设置串口1的BRR寄存器值为0X0271就可以得到115200的波特率。
4). 用到的库函数函数
void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
3. 串口配置的一般步骤
①串口时钟使能
GPIO时钟使能:RCC_APB2PeriphClockCmd();
②串口复位
USART_Delnit(); (这一步不是必须的)
③GPIO端口模式设置
GPIO_Init(); 模式设置查表(后文附表,或v10——8.1.11)
④串口参数初始化:
USART_Init();
⑤开启中断并且初始化NVIC (如果需要开启中断才需要这个步骤)
USART_ITConfig(); 开启哪种中断
NVIC_Init(); 设置抢占/相应优先级
⑥使能串口
USART_Cmd();
⑦编写中断处理函数
USARTX_TRQHandler();
⑧串口数据收发
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
⑨串口传输状态获取:
FlagStatus USARTE_GetFlagStatus(USARTTypeDef*.USARTx, uint16_t USART_FLAG);
void USART_ClearlTPendingBit(USART_TypeDef USARTx, uint16_t USARTIT);
函数设置(串口1为例)
头文件程序
下面展示一些 内联代码片。
#ifndef __USART_H
#define __USART_H
#include "sys.h"
void usart_Init(void);
#endif
源文件
#include "sys.h"
#include "usart.h"
void usart_Init(void)
{
GPIO_InitTypeDef GPIO_InitTypestru;
USART_InitTypeDef USART_InitTypestru;
NVIC_InitTypeDef NVIC_InitTypestru;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
GPIO_InitTypestru.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitTypestru.GPIO_Pin=GPIO_Pin_9;
GPIO_InitTypestru.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitTypestru); //输出设置
GPIO_InitTypestru.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitTypestru.GPIO_Pin=GPIO_Pin_10;
GPIO_InitTypestru.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitTypestru); //输入设置
USART_InitTypestru.USART_BaudRate=115200; //波特率
USART_InitTypestru.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用硬件流
USART_InitTypestru.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //发送接收 都使能
USART_InitTypestru.USART_Parity=USART_Parity_No; //不使用奇偶校验
USART_InitTypestru.USART_StopBits=USART_StopBits_1; //一位停止位
USART_InitTypestru.USART_WordLength=USART_WordLength_8b; //字长8位
USART_Init(USART1,&USART_InitTypestru);
USART_Cmd(USART1,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启中断 接收中断
NVIC_InitTypestru.NVIC_IRQChannel=USART1_IRQn; //串口1中断
NVIC_InitTypestru.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitTypestru.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitTypestru.NVIC_IRQChannelSubPriority=1; //优先级都为1
NVIC_Init(&NVIC_InitTypestru);
}
void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE))
res=USART_ReceiveData(USART1);
USART_SendData(USART1,res);
}
主函数
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
int main(void)
{
usart_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
while(1);
}
效果
使用串口调试助手给mcu发送数据,单片机接收到数据,并将此数据在此发送给电脑。若需要其他指令,改变服务中断函数的内容即可。
接收数据的调用
例:串口1接收到一数据,如1,怎样在主函数中判断并调用此数据呢?
- 先在main函数之外定义一个全局变量,如 u8 res=0;
- 在usart.c文件中的串口中断函数中,我们将接收到的数据赋值给res, res=USART_ReceiveData(USART1);
- 但此时编译会提示未定义res,因此我们需要在usart.h文件中声明res已经在其他文件中已经定义过了。所以在usart.h文件中加入此行代码extern u8 res;
- 此时即可在main中使用此变量,如
if(res==0x31) //既res==1? 此为十六进制码
i=1;
else
i=2;
//PS:我是使用蓝牙通过串口发送数据给mcu,
//发送的数据为十六进制码,虽然在串口助手中发送的数字为1,
//但在代码中需要将**(res==1?)**写为**(res==0x31?)**,
//经实验(res==1)?不会有效果。
附表
1 对应引脚
参考中文参考手册v10——8.3.8节
2 通信设置
1). 处理器与外部设备通信
并行通信:速度快 占用引脚多
串行通信:速度慢 占用引脚少
单工:单方向通信只能由A到B
半双工:可以双向传输,但同一时刻只能在一个方向
全双工:允许同时双向传输,两设备都具有独立的发送和接收能力
通信方式
同步通信:带时钟同步信号传输 SPI IIC通信接口
异步通信:不带时钟同步信号 UART 需要设置波特率
常见串行通信接口
2). 异步通信需要定义的参数
起始位 数据位 奇偶校验位 停止位 波特率设置
3). 波特率计算
tx/rx波特率=Fpclx/(16*USARTDIV)
上式中,F PCLKx是给串口的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1);USARTDIV是一个无符号定点数。我们只要得到USARTDIV的值,就可以得到串口波特率寄存器USARTI->BRR的值,反过来,我们得到USARTI->BRR的值,也可以推导出USARTDIV的值。但我们更关心的是如何从USARTDIV的值得到UUSARTI->BRR的值,因为一-般我们知道的是波特率,和PCLKx的时钟,要求的就是USART BRR的值。下面我们来介绍如何通过USARTDIV得到串口USART BRR寄存器的值。假设我们的串口1要设置为115200的波特率,而PCLK2的时钟为72M。这样,我们根据上面的公式有:
USARTDIV=72000000/(115200* 16)= 39.0625
那么得到:
DIV_ Fraction= 16*0.0625= 1=0X01;
DIV_ Mantissa= :39 =0X27;
这样,我们就得到了USARTI->BRR的值为0X0271。只要设置串口1的BRR寄存器值为0X0271就可以得到115200的波特率。
4). 用到的库函数函数
void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
3. 串口配置的一般步骤
①串口时钟使能
GPIO时钟使能:RCC_APB2PeriphClockCmd();
②串口复位
USART_Delnit(); (这一步不是必须的)
③GPIO端口模式设置
GPIO_Init(); 模式设置查表(后文附表,或v10——8.1.11)
④串口参数初始化:
USART_Init();
⑤开启中断并且初始化NVIC (如果需要开启中断才需要这个步骤)
USART_ITConfig(); 开启哪种中断
NVIC_Init(); 设置抢占/相应优先级
⑥使能串口
USART_Cmd();
⑦编写中断处理函数
USARTX_TRQHandler();
⑧串口数据收发
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
⑨串口传输状态获取:
FlagStatus USARTE_GetFlagStatus(USARTTypeDef*.USARTx, uint16_t USART_FLAG);
void USART_ClearlTPendingBit(USART_TypeDef USARTx, uint16_t USARTIT);
函数设置(串口1为例)
头文件程序
下面展示一些 内联代码片。
#ifndef __USART_H
#define __USART_H
#include "sys.h"
void usart_Init(void);
#endif
源文件
#include "sys.h"
#include "usart.h"
void usart_Init(void)
{
GPIO_InitTypeDef GPIO_InitTypestru;
USART_InitTypeDef USART_InitTypestru;
NVIC_InitTypeDef NVIC_InitTypestru;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
GPIO_InitTypestru.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitTypestru.GPIO_Pin=GPIO_Pin_9;
GPIO_InitTypestru.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitTypestru); //输出设置
GPIO_InitTypestru.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitTypestru.GPIO_Pin=GPIO_Pin_10;
GPIO_InitTypestru.GPIO_Speed=GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&GPIO_InitTypestru); //输入设置
USART_InitTypestru.USART_BaudRate=115200; //波特率
USART_InitTypestru.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用硬件流
USART_InitTypestru.USART_Mode=USART_Mode_Rx|USART_Mode_Tx; //发送接收 都使能
USART_InitTypestru.USART_Parity=USART_Parity_No; //不使用奇偶校验
USART_InitTypestru.USART_StopBits=USART_StopBits_1; //一位停止位
USART_InitTypestru.USART_WordLength=USART_WordLength_8b; //字长8位
USART_Init(USART1,&USART_InitTypestru);
USART_Cmd(USART1,ENABLE);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启中断 接收中断
NVIC_InitTypestru.NVIC_IRQChannel=USART1_IRQn; //串口1中断
NVIC_InitTypestru.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitTypestru.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitTypestru.NVIC_IRQChannelSubPriority=1; //优先级都为1
NVIC_Init(&NVIC_InitTypestru);
}
void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(USART1,USART_IT_RXNE))
res=USART_ReceiveData(USART1);
USART_SendData(USART1,res);
}
主函数
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
int main(void)
{
usart_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
while(1);
}
效果
使用串口调试助手给mcu发送数据,单片机接收到数据,并将此数据在此发送给电脑。若需要其他指令,改变服务中断函数的内容即可。
接收数据的调用
例:串口1接收到一数据,如1,怎样在主函数中判断并调用此数据呢?
- 先在main函数之外定义一个全局变量,如 u8 res=0;
- 在usart.c文件中的串口中断函数中,我们将接收到的数据赋值给res, res=USART_ReceiveData(USART1);
- 但此时编译会提示未定义res,因此我们需要在usart.h文件中声明res已经在其他文件中已经定义过了。所以在usart.h文件中加入此行代码extern u8 res;
- 此时即可在main中使用此变量,如
if(res==0x31) //既res==1? 此为十六进制码
i=1;
else
i=2;
//PS:我是使用蓝牙通过串口发送数据给mcu,
//发送的数据为十六进制码,虽然在串口助手中发送的数字为1,
//但在代码中需要将**(res==1?)**写为**(res==0x31?)**,
//经实验(res==1)?不会有效果。
附表
举报
更多回帖