STM32F407系列文章目录
第一章 获取相关组件(注意:下载或安装不要有中文路径)
第二章 新建工程
第三章 GPIO相关配置实验
第四章 串口实验
前言
本文章采用Keil5及STM32F4(SDK-设计资源)进行STM32F407(ARM芯片)系列学习
- 萌新入坑,如有不对的地方请大佬多多指教,谢谢!->欢迎评论区留言
- 对于萌新来说,学习无非就是多看博文,但是…0.0大佬们的思维大多都有些跳跃,真的很难理解(当然大佬们估计都是为了记录或者提醒自己的工作及学习例程),所以想通过慢慢学习,把每一步都解释出来(保姆版)
第四章 串口实验
1.原理图
- MCU引脚
- 涉及外设
2.代码示例
- usart.c
- 库函数版本
#include "sys.h"
#include "usart.h"
//如果使用ucos,则包括下面的头文件即可
#if SYSTEM_SUPPORT_OS
#include "includes.h" // ucos 使用
#endif
//支持适应不同频率下的串口波特率设置
//加入了对printf的支持
//增加了串口接收命令功能
//修正了printf第一个字符丢失的bug
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0); // 循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX // 如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; // 接收缓冲,最大USART_REC_LEN个字节
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA = 0; // 接收状态标记
void uart_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); // 使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // 使能USART1时钟
//串口1对应引脚复用映射
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); // GPIOA9复用为USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); // GPIOA10复用为USART1
//USART1端口配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // GPIOA9与GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 速度50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉
GPIO_Init(GPIOA,&GPIO_InitStructure); // 初始化PA9,PA10
//USART1 初始化设置
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; // 无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收发模式
USART_Init(USART1, &USART_InitStructure); // 初始化串口1
USART_Cmd(USART1, ENABLE); // 使能串口1
//USART_ClearFlag(USART1, USART_FLAG_TC);
#if EN_USART1_RX
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启相关中断
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // 串口1中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure); // 根据指定的参数初始化VIC寄存器
#endif
}
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); // (USART1->DR); // 读取接收到的数据
if ((USART_RX_STA & 0x8000) == 0) // 接收未完成
{
if (USART_RX_STA & 0x4000) // 接收到了0x0d
{
if (Res != 0x0a) USART_RX_STA=0; // 接收错误,重新开始
else USART_RX_STA |= 0x8000; // 接收完成了
}
else // 还没收到0X0D
{
if (Res == 0x0d) USART_RX_STA |= 0x4000;
else
{
USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res;
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
}
#endif
- 寄存器版本
#include "sys.h"
#include "usart.h"
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" // ucos 使用
#endif
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
/* FILE is typedef’ d in stdio.h. */
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0); // 循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX // 如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; // 接收缓冲,最大USART_REC_LEN个字节
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA = 0; // 接收状态标记
void uart_init(u32 pclk2, u32 bound)
{
float temp;
u16 mantissa;
u16 fraction;
temp = (float)(pclk2 * 1000000) / (bound * 16); // 得到USARTDIV@OVER8=0
mantissa = temp; // 得到整数部分
fraction = (temp - mantissa) * 16; // 得到小数部分@OVER8=0
mantissa <<= 4;
mantissa += fraction;
RCC->AHB1ENR |= 1 << 0; // 使能PORTA口时钟
RCC->APB2ENR |= 1 << 4; // 使能串口1时钟
GPIO_Set(GPIOA, PIN9|PIN10, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_SPEED_50M, GPIO_PUPD_PU); // PA9,PA10,复用功能,上拉输出
GPIO_AF_Set(GPIOA, 9, 7); // PA9,AF7
GPIO_AF_Set(GPIOA, 10, 7); // PA10,AF7
// 波特率设置
USART1->BRR = mantissa; // 波特率设置
USART1->CR1 &= ~(1 << 15); // 设置OVER8=0
USART1->CR1 |= 1 << 3; // 串口发送使能
#if EN_USART1_RX // 如果使能了接收
// 使能接收中断
USART1->CR1 |= 1 << 2; // 串口接收使能
USART1->CR1 |= 1 << 5; // 接收缓冲区非空中断使能
MY_NVIC_Init(3, 3, USART1_IRQn, 2); // 组2,最低优先级
#endif
USART1->CR1 |= 1 << 13; // 串口使能
}
void USART1_IRQHandler(void)
{
u8 res;
#if SYSTEM_SUPPORT_OS // 如果SYSTEM_SUPPORT_OS为真,则需要支持OS
OSIntEnter();
#endif
if (USART1->SR & (1 << 5)) // 接收到数据
{
res = USART1->DR;
if ((USART_RX_STA & 0x8000) == 0) // 接收未完成
{
if (USART_RX_STA & 0x4000) // 接收到了0x0d
{
if (res != 0x0a) USART_RX_STA = 0; // 接收错误,重新开始
else USART_RX_STA |= 0x8000; // 接收完成了
}
else // 还没收到0X0D
{
if (res == 0x0d) USART_RX_STA |= 0x4000;
else
{
USART_RX_BUF[USART_RX_STA & 0X3FFF] = res;
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
}
#endif
- usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "stm32f4xx_conf.h"
#include "sys.h"
#define USART_REC_LEN 200 // 定义最大接收字节数 200
#define EN_USART1_RX 1 // 使能(1)/禁止(0)串口1接收
extern u8 USART_RX_BUF[USART_REC_LEN]; // 接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; // 接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif
- main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
int main(void)
{
u8 t;
u8 len;
u16 times=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置系统中断优先级分组2
//Stm32_Clock_Init(336, 8, 2, 7); // 设置时钟,168Mhz
delay_init(168); // 延时初始化
uart_init(115200); // 串口初始化波特率为115200
while(1)
{
if (USART_RX_STA & 0x8000)
{
len = USART_RX_STA & 0x3fff; // 得到此次接收到的数据长度
printf("\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); // 等待发送结束
/* 寄存器操作 */
USART1->DR = USART_RX_BUF[t];
while ((USART1->SR & 0X40) == 0); // 等待发送结束
}
printf("\r\n\r\n"); // 插入换行
USART_RX_STA = 0;
}
else
{
times++;
if (times % 5000 == 0)
{
printf("\r\n STM32F407串口实验 \r\n");
printf(" STM32F407 \r\n\r\n\r\n");
}
if (times % 200 == 0) printf("请输入数据,以回车键结束\r\n");
if (times % 30 == 0) LED0 = !LED0; // 闪烁LED,提示系统正在运行
delay_ms(10);
}
}
}
总结
本章为TMS320F280049学习系列文章 第四章:串口实验