关注同名微信公众号“混沌无形”,阅读更多有趣好文!
一、串口原理
1.处理器与外部设备通信的两种方式:并行通信(速度快,占用资源多)+串行通信(反之)
2.串行通信(按照数据传送方向分类):
(1)单工:数据传输只支持数据在一个方向上传输
(2)半双工:允许数据在两个方向上传输,但在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
(3)全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立 的接收和发送能力。
4.STM32的串口通信接口
(1)UART:通用异步收发器
(2)USART:通用同步异步收发器
分数波特率发生器系统,提供精确的波特率( 波特率决定数据传输速度)。
-发送和接受共用的可编程波特率,最高可达4.5Mbits/s
可编程的数据字长度(8位或者9位);
可配置的停止位(支持1或者2位停止位);
可配置的使用DMA多缓冲器通信。
单独的发送器和接收器使能位。
检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
多个带标志的中断源。触发中断。
其他:校验控制,四个错误检测标志。
数据位(8位或者9位)
奇偶校验位(第9位)--->偶校验:如果数据位8位中,1的个数为偶数,则校验位为0,反之为1;奇校验相反
停止位(1,15,2位)
波特率设置---->点击打开链接
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
SR寄存器操作:
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
二、实际串口寄存器操作
1.miniSTM32板载2个串口:
(1)USB串口(CH340:USB转串口芯片):与电脑通信
(2)RS232串口:异步传输标准接口---STM32内带的USB接口
(3)术语区分:1)串口、COM口是指的物理接口形式(硬件)【COM口即串行通讯端口,简称串口】,而TTL、RS-232、RS-485是指的电平标准(电信号--通信协议)
2)TTL标准是低电平为0,高电平为1(+5V电平)。RS-232标准是正电平为0,负电平为1(±15V电平) 详见点击打开链接
2.串口设置步骤:
(1)串口时钟,GPIO时钟使能-->RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
(2)串口复位--->void USART_DeInit(USART_TypeDef* USARTx);--USART_DeInit(USART1); //复位串口 1
(3)GPIO端口模式设置-->GPIO_Init(); 模式设置为GPIO_Mode_AF_PP----【端口复用和重映射很不一样-点击打开链接--------区别:点击打开链接】
(4)串口参数初始化--->void USART_Init(USART_TypeDef* USARTx,USART_InitTypeDef*USART_InitStruct);
1)USART_InitTypeDef 类型的结构体指针:
USART_InitStructure.USART_BaudRate = bound; //波特率;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl= USART_HardwareFlowControl_None; //无硬件数据流控制,用于两个uart模块之间传输数据
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口
2)串口数据收发:
USART_DR寄存器发送数据函数:void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
第二个入口参数很重要:RXNE(读数据寄存器非空)以及TC(发送完成)
判断发送是否完成(TC),操作库函数:USART_GetFlagStatus(USART1, USART_FLAG_TC);
(5)开启中断并且初始化NVIC-->NVIC_Init();(如果需要开启中断才需要这个步骤)
1)使能串口中断函数:void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)
*****接收数据的时候产生中断:USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断【RXNE位被置位。它表明移位寄存器的内容被转移到RDR。换句话说,数据已经被接收并且可以被读出(包括与之有关的错误标志)------>*如果RXNE=1,上一个有效数据还在接收寄存器RDR上,可以被读出.;;如果RXNE=0,这意味着上一个有效数据已经被读走, RDR已经没有东西可读。当上一个有效数据在RDR中被读取的同时又接收到新的(也就是丢失的)数据时,此种情况可能发生。 】
发送数据结束时(TC,发送完成)产生中断:USART_ITConfig(USART1, USART_IT_TC, ENABLE);
******USART中断请求--标志说明
**TXE---->发送数据寄存器空
**CTS--->CTS标志
**TC--->发送完成
**TXNE--->接收数据就绪可读
**ORE--->检测到数据溢出
**IDLE--->检测到空闲线路
**PE--->奇偶检验错
**LBD--->断开标志
**NE或ORT或FE--->噪声标志,多缓冲通信中的溢出错误和帧错误
2)获取相应的中断状态(判断是哪个中断产生的):ITStatusUSART_GetITStatus(USART_TypeDef*USARTx, uint16_t USART_IT)
例:判断到底是否是串口发送完成中断 --->USART_GetITStatus(USART1, USART_IT_TC)
(6)编写中断处理函数--->USARTx_IRQHandler();
(7)串口数据收发:void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
(8)串口传输状态获取:FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
在主函数中编写UART1串口通信:
#include "stm32f10x.h"//必须加上,不然不能识别时钟函数等宏定义
#include "LED.h"
void USART1_IRQHandler(void)//USART_IT_RXNE:接收到数据,就执行中断服务函数
{
//在stm32f10x_usart.c文件中查找功能函数
if(USART_GetITStatus(USART1,USART_IT_RXNE/*中断标志是什么,此处就应该写什么*/))
{
uint16_t res = USART_ReceiveData(USART1); //从串口接收到数据
USART_SendData(USART1,res);//将数据从串口发出去
LED0 = ~LED0;
}
}
void my_uart_init(void)
{
//参数结构定义--->应该写在函数开头处
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART1_para;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);//PA9,PA10
//端口重映射
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
USART_DeInit(USART1);//串口复位
//GPIO端口模式设置
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//推免输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//USART1_TX
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//USART1_RX
//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//串口参数设置
USART1_para.USART_BaudRate = 9600;
USART1_para.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART1_para.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART1_para.USART_Parity = USART_Parity_No;
USART1_para.USART_StopBits = USART_StopBits_1;
USART1_para.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART1_para);
//使能串口
USART_Cmd(USART1,ENABLE);
//中断配置
USART_ITConfig(USART1,USART_IT_RXNE/*接收到数据 就出发中断*/,ENABLE);//触发中断标志
//开启中断并初始化
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;//串口中断函数
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;//子优先级
NVIC_Init(&NVIC_InitStruct);
}
int main(void)
{
LED_init();
LED0 = 1;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组
my_uart_init();
while(1);
}
//在stm32f10x_usart.c文件中查找功能函数
if(USART_GetITStatus(USART1,USART_IT_RXNE/*中断标志是什么,此处就应该写什么*/))
{
uint16_t res = USART_ReceiveData(USART1); //从串口接收到数据
USART_SendData(USART1,res);//将数据从串口发出去
LED0 = ~LED0;
}
}
void my_uart_init(void)
{
//参数结构定义--->应该写在函数开头处
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART1_para;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);//PA9,PA10
//端口重映射
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
USART_DeInit(USART1);//串口复位
//GPIO端口模式设置
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;//推免输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//USART1_TX
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//USART1_RX
//GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//串口参数设置
USART1_para.USART_BaudRate = 9600;
USART1_para.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART1_para.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART1_para.USART_Parity = USART_Parity_No;
USART1_para.USART_StopBits = USART_StopBits_1;
USART1_para.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART1_para);
//使能串口
USART_Cmd(USART1,ENABLE);
//中断配置
USART_ITConfig(USART1,USART_IT_RXNE/*接收到数据 就出发中断*/,ENABLE);//触发中断标志
//开启中断并初始化
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;//串口中断函数
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;//优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;//子优先级
NVIC_Init(&NVIC_InitStruct);
}
int main(void)
{
LED_init();
LED0 = 1;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组
my_uart_init();
while(1);
}
回车:0x0d;换行:0x0a
使用串口2源代码:点击打开链接
printf("\r\n\r\n");---->将内容发送到主串口-【可在usart.c中修改】
如果喜欢的话,可以关注同名微信公众号“混沌无形”,阅读更多有趣好文!