1.说点啥
🍌
🍌🍌
作者简介:大家好啊,我叫DW,小白一个,每天分享一些我新学到的知识,期待和大家一起进步
🍋
🍋🍋
系列专栏:STM32
🍎
🍎🍎
🍎🍎🍎
🌞小实验目标:实现串口打印数据🌞
🍊如有写得不好的地方欢迎大家指正🍊
创作时间:🍊🍊🍊2022年4月20日🍊🍊🍊
2 USART简介
STM32 的串口资源相当丰富的,功能也相当强劲。STM32F103 系列最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA等。
2.1 端口复用功能
一个GPIO如果可以复用为内置外设的功能引脚,那么当这个GPIO作为内置外设使用的时候,就叫做复用。例如串口1 的发送接收引脚是PA9,PA10,当我们把PA9,PA10不用作GPIO,而用做复用功能串口1的发送接收引脚的时候,叫端口复用。
串口一复用管脚
2.2 复用端口配置步骤
(1)GPIO 端口时钟使能。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
(2)复用的外设时钟使能。 PA9,PA10 复用为串口,故需要使能串口时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);
(3)端口模式配置,本实验使用全双工模式,我们要配置全双工的串口 1,那么 TX 管脚需要配置为推挽复用输出,RX 管脚配置为浮空输入或者带上拉输入都可以。
GPIO_InitTypeDef GPIO_InitStructure;//GPIOA 结构体定义
//TX 发送 PA9
GPIO_InitStructure.GPIO_Pin = TX;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(USART_PROT,&GPIO_InitStructure);//初始化
//RX 接收 PA10
GPIO_InitStructure.GPIO_Pin = RX;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(USART_PROT,&GPIO_InitStructure);//初始化
2.3 串口配置步骤
(1) 串口时钟使能,GPIO 时钟使能。(2.2已经配置过)
(2) 串口复位
USART_DeInit(USART1); //复位串口 1
(3) 串口参数初始化,查看串口助手,依次配置波特率、停止位、数据位、校验位。
//串口
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = BaudRate;//波特率 115200
USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位为1
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长 8
USART_InitStructure.USART_Parity = USART_Parity_No;//无校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流 使用软件触发
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//收发模式
USART_Init(USART1,&USART_InitStructure);//初始化串口1
波特率通过形参传输
void USART_UserConfig(uint32_t BaudRate)
(4) 开启中断并且初始化 NVIC。
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启中断
USART_Cmd(USART1,ENABLE);//开启全局中断
//中断配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);//中断分组选择
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断入口 选择串口一
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//子占优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);//初始化中断
(6) 编写中断处理函数。
//中断函数配置
void USART1_IRQHandler(void){
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET){
USART_SendData(USART1,USART_ReceiveData(USART1));
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//用TXE换成TC会造成数据缺失
}
}
2.4 重映射打印输出printf
fputc函数是printf函数内部的一个函数,功能是将字符dat写入文件指针BUF所指向文件的当前写指针位置,我们使用USART函数重新修改fputc函数内容,达到类似写入的功能。
int fputc(int dat,FILE *BUF){
USART_SendData(USART1,dat);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
//while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//数据丢失
return dat;
}
🎵🎵 小提示
(1)使用前需要在文件中包含 stdio.h头文件。
(2)使用 fput和 fgetc函数达到重定向 C语言标准库输入输出函数必须在 MDK的工程选项把“Use MicroLIB”勾选上
2.5 注意要点
🎵🎵 小提示
(1) 在接收到数据的时候(RXNE 读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
(2)判断是否产生中断事件,如果其其值为‘1’则产生中断:
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
🎵🎵 小提示
(3)获取中断标志位,判断中断是否完成;只要其值不等于0,退出这条语句,即可判别当前中断是否完成,以便进入下一个中断
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);//第一句
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);//第二句
如果第一句的TXE换成第二句的TC会造成数据缺失
3.结语
今天的分享就到这里,从头到尾一个一个字码下来,重新回顾了今天的学习内容,收货满满,希望这个习惯能一直坚持下去,用博客记录自己学习的过程,谢谢大家的耐心阅读,如果觉得有用的话给个👍👍👍
🌜🌜🌜本章结束,我们下一章见🌜🌜🌜
源码已上传,需要自取
这是我今天学习链接:学习链接
附录
//main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
int main(void)
{
delay_init();
USART_UserConfig(115200);
while(1){
printf("adcdefg520520给个点赞吧\r\n");
delay_ms(200);
}
}
//usart.c
#include "usart.h"
void USART_UserConfig(uint32_t BaudRate){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO,ENABLE);//串口需要开启复用功能
//TX 发送 PA9
GPIO_InitStructure.GPIO_Pin = TX;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(USART_PROT,&GPIO_InitStructure);//初始化
//RX 接收 PA10
GPIO_InitStructure.GPIO_Pin = RX;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(USART_PROT,&GPIO_InitStructure);//初始化
//串口
USART_DeInit(USART1);
USART_InitStructure.USART_BaudRate = BaudRate;//波特率
USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位为一
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长 8
USART_InitStructure.USART_Parity = USART_Parity_No;//无校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流 使用软件触发
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;//收发模式
USART_Init(USART1,&USART_InitStructure);//初始化串口1
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//开启中断
USART_Cmd(USART1,ENABLE);//开启全局中断
//中断配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);//中断分组选择
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断入口 选择串口一
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//子占优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断
NVIC_Init(&NVIC_InitStructure);//初始化中断
}
//中断函数配置
void USART1_IRQHandler(void){
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET){
USART_SendData(USART1,USART_ReceiveData(USART1));
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
}
int fputc(int dat,FILE *BUF){
USART_SendData(USART1,dat);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);
return dat;
}
//usart.h
#ifndef __USART_H
#define __USART_H
#include "sys.h"
#include "stdio.h"
#define TX GPIO_Pin_9 //PA9
#define RX GPIO_Pin_10 //PA10
#define USART_PROT GPIOA
void USART_UserConfig(uint32_t BaudRate);
#endif