STM32F4VGT6-DISCOVERY:uart1驱动

对于这款板子,官方并没有提供串口例程,只能自行添加。

一、PA9/PA10复用成串口1功能不可用

驱动测试代码如下:

main.c:

cpp 复制代码
#include "main.h"
#include <stdio.h>

void usart1_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    //(1)串口时钟和 GPIO 时钟使能
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//GPIO口时钟使能, 引脚PA9,PA10可以复用为串口功能
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口UART1时钟使能, 串口1是挂载在 APB2总线下的外设

    //(2)配置GPIO引脚为复用功能[PA9+PA10]
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//电平翻转速度100MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化 PA9,PA10

    //(3)配置串口1参数
    USART_InitStructure.USART_BaudRate = 115200;//波特率
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8个数据位
    USART_Init(USART1, &USART_InitStructure); //初始化串口1

    //(4) 开启中断并且初始化 NVIC,使能相应中断[接收一个byte触发中断一次]
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级 3
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应优先级 3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器

    //(5) 使能串口1
    USART_Cmd(USART1, ENABLE);
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
    char res;
    //检查标志位
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        res = USART_ReceiveData(USART1);//收到数据后读出来
        USART_SendData(USART1,res);//读到数据后立刻发送出去
        //清空标志位
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

int main(void)
{
    usart1_init();
    while(1)
    {
        ;
    }

    return 0;
}

固件烧录后,连接PC端串口工具,串口收发功能不可用。原因:PA9/PA10设计给板载USB使用,打开原理如下:

所以,只能换一组GPIO,换成PB6/PB7(原理图上可确认没有其他外设使用),测试代码如下:

cpp 复制代码
#if 0
void usart1_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	//(1)串口时钟和 GPIO 时钟使能
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//GPIO口时钟使能, 引脚PA9,PA10可以复用为串口功能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口UART1时钟使能, 串口1是挂载在 APB2总线下的外设

	//(2)配置GPIO引脚为复用功能[PA9+PA10]
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//电平翻转速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化 PA9,PA10

	//(3)配置串口1参数
	USART_InitStructure.USART_BaudRate = 115200;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8个数据位
	USART_Init(USART1, &USART_InitStructure); //初始化串口1

	//(4) 开启中断并且初始化 NVIC,使能相应中断[接收一个byte触发中断一次]
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应优先级 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器

	//(5) 使能串口1
	USART_Cmd(USART1, ENABLE);
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
	char res;
	//检查标志位
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		res = USART_ReceiveData(USART1);//收到数据后读出来
		USART_SendData(USART1,res);//读到数据后立刻发送出去

		//清空标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

#else
void usart1_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	//(1)串口时钟和 GPIO 时钟使能
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口UART1时钟使能, 串口1是挂载在 APB2总线下的外设

	//(2)配置GPIO引脚为复用功能[PB6+PB7]
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//电平翻转速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 PA9,PA10

	//(3)配置串口1参数
	USART_InitStructure.USART_BaudRate = 115200;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8个数据位
	USART_Init(USART1, &USART_InitStructure); //初始化串口1

	//(4) 开启中断并且初始化 NVIC,使能相应中断[接收一个byte触发中断一次]
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应优先级 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器

	//(5) 使能串口1
	USART_Cmd(USART1, ENABLE);
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
	char res;
	STM_EVAL_LEDOn(LED4);

	//检查标志位
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		res = USART_ReceiveData(USART1);//收到数据后读出来
		STM_EVAL_LEDOn(LED5);
		USART_SendData(USART1,res);//读到数据后立刻发送出去

		//清空标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
#endif

int main(void)
{
	usart1_init();
	while(1)
	{
		;
	}

	return 0;
}

板子实物图连接如下:

串口工具测试如下:

二、串口标准输入输出重定向打印实现

直接重写标准输入输出函数的底层接口即可,如下:

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| //重写printf底层函数接口 int fputc(int c, FILE *stream) { USART1->DR=c; //发送一个字符 while(!(USART1->SR&1<<7)){} return c; } //重写scanf底层函数接口 int fgetc(FILE *stream) { while(!(USART1->SR&1<<5)){} return USART1->DR; } |

烧录固件,并按reset键,观察串口调试助手上的打印:使用ST-LINK的方式进行烧录

串口助手:

三、完整代码

usart.c

cpp 复制代码
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"
#include <stdio.h>

void usart1_init(uint32_t BaudRate)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	//(1)串口时钟和 GPIO 时钟使能
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE); //使能GPIOB时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口UART1时钟使能, 串口1是挂载在 APB2总线下的外设

	//(2)配置GPIO引脚为复用功能[PB6+PB7]
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//电平翻转速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 PA9,PA10

	//(3)配置串口1参数
	USART_InitStructure.USART_BaudRate = BaudRate;//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8个数据位
	USART_Init(USART1, &USART_InitStructure); //初始化串口1

	//(4) 开启中断并且初始化 NVIC,使能相应中断[接收一个byte触发中断一次]
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;//抢占优先级 3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;//响应优先级 3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化NVIC寄存器

	//(5) 使能串口1
	USART_Cmd(USART1, ENABLE);
}

//串口1中断服务函数
void USART1_IRQHandler(void)
{
	char res;

	//检查标志位
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
	{
		res = USART_ReceiveData(USART1);//收到数据后读出来

		USART_SendData(USART1,res);//读到数据后立刻发送出去

		//清空标志位
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

//重写printf底层函数接口
int fputc(int c, FILE *stream)
{
	  USART1->DR=c; //发送一个字符
	  while(!(USART1->SR&1<<7)){}
		return c;
}

//重写scanf底层函数接口
int fgetc(FILE *stream)
{
	while(!(USART1->SR&1<<5)){}
    return USART1->DR;
}

/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/

usart.h

cpp 复制代码
#ifndef __USART__H__
#define __USART__H__

void usart1_init(uint32_t BaudRate);

#endif /* __USART__H__ */

main.c

cpp 复制代码
#include "usart.h"
#include <stdio.h>

int main(void)
{
	usart1_init(115200);
	printf("hello world!\n");
	while(1)
	{
		;
	}

	return 0;
}

注:这款Discovery开发板是基于标准外设库函数来开发的,因此工程中要把串口相关的库函数文件添加进来,如下:

相关推荐
bai54593626 分钟前
STM32 硬件I2C读写MPU6050
stm32·单片机·嵌入式硬件
LS_learner28 分钟前
6020角度双环控制一种用于电机控制的策略
嵌入式硬件
IT.小航1 小时前
STM32F103RC的USB上拉电阻1.5K
stm32·单片机·嵌入式硬件
m0_555762902 小时前
MCU 开发工具汇总
单片机·嵌入式硬件
伴杯猫2 小时前
【ESP32-IDF】高级外设开发3:I2S
c语言·单片机·嵌入式硬件·mcu·物联网·esp32·esp-idf
飞凌嵌入式4 小时前
高性能、高实时、高安全:如何在飞凌嵌入式i.MX95xx核心板上同时实现?
嵌入式硬件·安全·嵌入式·飞凌嵌入式
智能物联实验室6 小时前
如何低门槛自制Zigbee 3.0温湿度计?涂鸦上新开发包,开箱即用、完全开源
嵌入式硬件·开源·硬件工程
淘晶驰AK6 小时前
51单片机与stm32单片机,先学习哪一个?
stm32·单片机·51单片机
躺不平的小刘6 小时前
从YOLOv5到RKNN:零冲突转换YOLOv5模型至RK3588 NPU全指南
linux·python·嵌入式硬件·yolo·conda·pyqt·pip
Moonnnn.7 小时前
【51单片机学习】AT24C02(I2C)、DS18B20(单总线)、LCD1602(液晶显示屏)
笔记·单片机·学习·51单片机