一、代码例程
二、安装串口驱动
三、(建议安装)串口调试助手
四、验证代码能否实现相应功能
一、代码例程
main.c
#include "stm32f4xx.h"
//#include "led.h"
#include "uart.h"
u8 str[3] = "123";
void Delay(void);
int main()
{
UART1_Config();//使用相应功能前一定要加Config函数
while(1)
{
// Delay_ms(1000);
// UART1_Send_Byte('a');
// UART1_Send_Str((uint8_t *)"Hello\r\n"); //验证串口发送功能时使用这部分代码
// printf("123456");
}
}
uart.c
#include "uart.h"
//PA9 /USART1_TX PA9复用推挽输出
//PA10 /USART1_RX PA10浮空输入
uint8_t U1_R_Buff[100]; //串口1的接收缓冲区
uint8_t U1_R_Length=0; //串口1已经接收数据的长度
uint8_t U1_R_IDLE=0; //串口1空闲标志位
void UART1_Config(void)
{
//1.开GPIOA时钟
// void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState); stm32f4xx_rcc.h 976行
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
/*
GPIO_PinAFConfig函数的作用是配置指定引脚的复用功能。
在STM32系列的单片机中,每个引脚都有多个功能,可以通过配置引脚的复用功能来选择不同的功能。
通过GPIO_PinAFConfig函数,可以将指定引脚配置为特定的复用功能,例如串口通信、定时器功能等。
比如:
PA9/USART1_TX/TIM1_CH2 42
PA10/USART1_RX/TIM1_CH3
*/
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); //PA9 复用为 USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);//PA10 复用为 USART1
//2.定义结构体 xxx_init准备的
// void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
GPIO_InitTypeDef GPIO_InitStruct;
//3.给结构体赋值 PA9和PA10
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;
//4.调用xxx_init函数将参数写入到寄存器中 PA9
GPIO_Init(GPIOA,&GPIO_InitStruct);
/* //3.给结构体赋值 PA9 复用推挽输出
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;
//4.调用xxx_init函数将参数写入到寄存器中 PA10
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_UP;;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStruct);
*/
//5.开串口1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//6.定义结构体
//void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct); stm32f4xx_usart.h 367行
USART_InitTypeDef USART_InitStruct;
//7.给结构体赋值 关于波特率 数据位 校验方式 停止位 要和通信的对方保持一致
USART_InitStruct.USART_BaudRate = 115200;//波特率 115200
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控制
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx; //发送使能,接收使能
USART_InitStruct.USART_Parity = USART_Parity_No; //无校验
USART_InitStruct.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8个数据位
//8.调用xxx_init函数将参数写入到寄存器中
USART_Init(USART1,&USART_InitStruct);
//9.使能串口功能 xxx_cmd
// void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState); stm32f4xx_usart.h 371行
USART_Cmd(USART1,ENABLE);
//10.根据需求配置是否配置中断
UART1_NVIC_Config();
}
void UART1_NVIC_Config(void)
{
//1.定义结构体
NVIC_InitTypeDef NVIC_InitStruct={0};
//11.给结构体赋值
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //中断通道
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能对应的中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; //次级优先级 两个优先级的参数要和分组函数NVIC_PriorityGroupConfig保持一致
//12.调用XXX_Init函数将参数写入到寄存器中
NVIC_Init(&NVIC_InitStruct);
//13.开启对应的中断
//void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState); stm32f4xx_usart.h 371行
/* 单片机 接收到信息了/信息接收完成了 就会触发中断,执行下面的USART1_IRQHandler函数
USART_ITConfig 配置中断
USART1_IRQHandler 中断触发了才会调用该函数
*/
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启接收中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //开启空闲中断
}
//单字节发送
void UART1_Send_Byte(uint8_t date)
{
//FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); stm32f4xx_usart.h 390行
//void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); //参考手册 25.6.1的位6和位7介绍TC和TXE的区别
/*TXE 为1 表示数据从发送数据寄存器到发送移位寄存器 TC 为1是在前面基础上,发送移位寄存器,将数据发送完成*/
//void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); stm32f4xx_usart.h 378行
USART_SendData(USART1,date); //发送数据
/*TC标志位支持软件清除 参考手册 25.6.1位6 介绍中
软件清除:先读SR寄存器 USART_GetFlagStatus函数中有读取SR寄存器
再写DR寄存器 USART_SendData函数中有写DR寄存器
*/
}
//发送数组的函数
void UART1_Send_Buff(uint8_t *buf,uint16_t length)
{
uint16_t i=0;
for(i=0;i<length;i++)
{
// UART1_Send_Byte(*buf++);
UART1_Send_Byte(buf[i]);
}
}
//发送字符串的函数
void UART1_Send_Str(uint8_t *str)
{
while((*str)!='\0')
{
UART1_Send_Byte(*str++);
}
}
/*
printf fputc 的重写 能够使用printf函数
包含头文件: #include "stdio.h"
在魔法棒 勾选 Use MicroLIB
仿真:不进mian函数 因为为勾选 Use MicroLIB LDR R0, =SystemInit
*/
int fputc(int ch, FILE *f)
{
UART1_Send_Byte(ch);
return ch;
}
/*
//查询接收
uint8_t UART1_Rec_Byte(void)
{
uint8_t Date=0;
//FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); stm32f4xx_usart.h 390行
//void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)!=SET); //没有收到数据
//uint16_t USART_ReceiveData(USART_TypeDef* USARTx); stm32f4xx_usart.h 379行
Date=USART_ReceiveData(USART1); //参考手册 25.6.1的位5介绍,通过软件序列清除标志位
// UART1_Send_Byte(Date);
return Date;
}*/
/*
中断服务函数的编写要求:
1.无参数 无返回值
2.中断服务函数的名字,必须从启动文件startup_stm32f4xx_hd.s中复制
3.中断服务函数中,不要有大量延时操作,及时退出
4.置标志位
5.中断服务函数不需要声明
注意:
1.如果仿真,中断服务程序不能放断点,函数名字有错误
2.停止仿真,代码卡在启动文件 B .位置,没有编写中断服务程序,或者名字有误
*/
/*
接收中断:每接收1个字节,触发1次接收中断
空闲中断:如果检测到总线空闲了(连续10位总线都是高)
对方发过来的数据 0x01 0x02 0x03 0x04
总共触发5次中断:触发4次接收中断,触发1次空闲中断
接收中断中:放断点,会导致后面的数据无法接收
一般在空闲中断中放断点
*/
void USART1_IRQHandler(void)
{
uint8_t date=0;
//ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT); stm32f4xx_usart.h 392行
//void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
//1.判断中断是否发生
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{ //触发接收中断
//2.编写中断服务函数执行内容
date=USART_ReceiveData(USART1);
USART_SendData(USART1,date); //将从串口1接收的信息从串口1发送到电脑,在串口助手中显示
U1_R_Buff[U1_R_Length++]=date;
if(U1_R_Length>=100) //避免数组越界
U1_R_Length=0;
//3.清除中断标志位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
{//触发空闲中断
//uint16_t USART_ReceiveData(USART_TypeDef* USARTx); stm32f4xx_usart.h 379行
date=USART_ReceiveData(USART1);
//参考手册 25.6.4的位4的介绍 软件序列清除 先读USART_SR(USART_GetITStatus),然后读USART_DR(USART_ReceiveData)
U1_R_IDLE=1; //接收完成标志位置1
}
}
uart.h
#ifndef __UART_H__
#define __UART_H__
#include "stm32f4xx.h"
#include "stdio.h"
extern uint8_t U1_R_Buff[100]; //串口1的接收缓冲区
extern uint8_t U1_R_Length; //串口1已经接收数据的长度
extern uint8_t U1_R_IDLE; //串口1空闲标志位
void UART1_Config(void);
void UART1_Send_Byte(uint8_t date);
void UART1_Send_Buff(uint8_t *buf,uint16_t length);
void UART1_Send_Str(uint8_t *str);
void UART1_NVIC_Config(void);
uint8_t UART1_Rec_Byte(void);
#endif
二、安装串口驱动
2.1 双击执行串口驱动安装程序,点击下一页
2.2 选择我接受,点击下一页
2.3 安装完成,点击完成
三、(建议安装)串口调试助手
打开电脑自带的 Microsoft Store,搜索串口调试助手,安装它
四、验证代码能否实现相应功能
4.1 验证单字节发送发送、字符串发送、printf改写
main.c部分如下图所示,编译后,将代码下载进单片机
打开之前下载的串口调试助手,按下图进行操作,最终效果如下图
4.2验证串口接收功能
屏蔽掉main.c中发送的这部分代码,可以更好查看接收结果
这行代码负责将从串口1接收的信息从串口1发送到电脑,在串口助手中显示
编译代码,将代码下载进单片机,使用串口助手发送信息,串口助手会收到同样的信息