首页 > 其他分享 >4.APM32-USART-串口接发

4.APM32-USART-串口接发

时间:2024-11-16 10:15:04浏览次数:3  
标签:USART void 串口 发送 ConfigStruct USARTX GPIO APM32 接发

效果展示

<iframe allowfullscreen="true" data-mediaembed="csdn" frameborder="0" id="9bunCvkO-1731313725584" src="https://live.csdn.net/v/embed/432498"></iframe>

USART-串口接发

硬件原理图

串口接口
我们使用的开发板上没有USB转串口的芯片,如果要连接到电脑上还需要使用USB转串口的模块或者jlink自带的虚拟串口。开发板的PA9(TX)引脚接USB转串口模块的RX引脚,开发板的PA10(RX)引脚接USB转串口模块的TX引脚,同时双方的GND还要连起来。

源代码

串口部分

#ifndef __BSP_USARTX_H__
#define __BSP_USARTX_H__

/* 包含头文件 ----------------------------------------------------------------*/
#include "apm32f10x_usart.h"
#include "apm32f10x_gpio.h"
#include "apm32f10x_rcm.h"
#include "apm32f10x_misc.h"
/* 宏定义 --------------------------------------------------------------------*/

#define USARTX USART1

#define USARTX_BAUDRATE 115200
#define USARTX_CLOCK RCM_APB2_PERIPH_USART1

#define USARTX_TX_PIN GPIO_PIN_9
#define USARTX_TX_GPIO_PORT GPIOA
#define USARTX_TX_GPIO_CLOCK RCM_APB2_PERIPH_GPIOA

#define USARTX_RX_PIN GPIO_PIN_10
#define USARTX_RX_GPIO_PORT GPIOA
#define USARTX_RX_GPIO_CLOCK RCM_APB2_PERIPH_GPIOA

#define USARTX_IRQn USART1_IRQn

/* 函数声明 ------------------------------------------------------------------*/
void USARTx_Config(void);
void Usart_SendByte(uint8_t byte);
void Usart_SendStr_Length(uint8_t *str, uint32_t strlen);
void Usart_SendString(uint8_t *str);

#endif // __BSP_USARTX_H__

#include "bsp_usartx.h"

/**
 * @brief 配置串口,打开串口接收中断
 */
void USARTx_Config(void)
{
    // 定义IO硬件初始化结构体变量
    GPIO_Config_T GPIO_ConfigStruct;
    // 定义USART初始化结构体变量
    USART_Config_T USART_ConfigStruct;
    // 使能USART时钟
    RCM_EnableAPB2PeriphClock(USARTX_CLOCK);
    // 使能USART功能GPIO时钟
    RCM_EnableAPB2PeriphClock(USARTX_RX_GPIO_CLOCK | USARTX_TX_GPIO_CLOCK);
    // 嵌套向量中断控制器组选择
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_4);

    // 设定USART发送对应IO模式:浮空输入
    GPIO_ConfigStruct.mode = GPIO_MODE_IN_FLOATING;
    GPIO_ConfigStruct.pin = USARTX_RX_PIN;
    // 串口2M,I2C 10M,SPI 50M
    GPIO_ConfigStruct.speed = GPIO_SPEED_2MHz;
    GPIO_Config(USARTX_RX_GPIO_PORT, &GPIO_ConfigStruct);
    // 设定USART发送对应IO模式:复用推挽输出
    GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
    GPIO_ConfigStruct.pin = USARTX_TX_PIN;
    GPIO_ConfigStruct.speed = GPIO_SPEED_2MHz;
    // 初始化USART发送对应IO
    GPIO_Config(USARTX_TX_GPIO_PORT, &GPIO_ConfigStruct);
    // USART波特率:115200
    USART_ConfigStruct.baudRate = USARTX_BAUDRATE;
    // USART硬件数据流控制(硬件信号控制传输停止):无
    USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
    // USART工作模式使能:允许接收和发送
    USART_ConfigStruct.mode = USART_MODE_TX_RX;
    // USART校验位:无
    USART_ConfigStruct.parity = USART_PARITY_NONE;
    // USART停止位:1位
    USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
    //   USART字长(有效位):8位
    USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
    USART_Config(USARTX, &USART_ConfigStruct);
    // 使能接收中断
    USART_EnableInterrupt(USARTX, USART_INT_RXBNE);
    // 配置USART为中断源,抢占优先级为3,子优先级为0
    NVIC_EnableIRQRequest(USARTX_IRQn, 3, 0);
    // 使能接收中断
    USART_EnableInterrupt(USARTX, USART_INT_RXBNE);
    // 使能USART
    USART_Enable(USARTX);
}
/**
 * @brief 发送一个字节数据
 * @param byte 待发送的字符
 */
void Usart_SendByte(uint8_t byte)
{
    // 发送一个字节数据
    USART_TxData(USARTX, byte);
    // 等待发送完毕
    while (USART_ReadStatusFlag(USARTX, USART_FLAG_TXBE) == RESET)
        ;
}

/**
 * @brief 串口发送指定长度的字符串
 * @param str
 * @param strlen
 */
void Usart_SendStr_Length(uint8_t *str, uint32_t strlen)
{
    uint32_t k = 0;
    do
    {
        Usart_SendByte(*(str + k));
        k++;
    } while (k < strlen);
}

/**
 * @brief 串口发 送字符串,直到遇到字符串结束符
 * @param str 待发送字符串缓冲器
 */
void Usart_SendString(uint8_t *str)
{
    uint32_t k = 0;
    do
    {
        Usart_SendByte(*(str + k));
        k++;
    } while (*(str + k) != '\0');

    // 等待发送完成
    while (USART_ReadStatusFlag(USARTX, USART_FLAG_TXC) == RESET)
    {
    }
}

主程序

/**
 * @file main.c
 * @brief 本次串口使用的是USART1
 */

/* 包含头文件 ----------------------------------------------------------------*/
#include "bsp_usartx.h"
/* 私有变量 ------------------------------------------------------------------*/

uint8_t RxFlag = 0;
uint8_t Temp;
uint8_t RxBuffer[256];
int main(void)
{

	uint16_t RxCount;
	USARTx_Config();

	Usart_SendString("这是一个串口中断接收回显实验\n");
	Usart_SendString("输入数据并以回车键结束\n");
	// 简单的通信协议,遇到回车换行符认为1个命令帧
	RxCount = 0;

	while (1)
	{
		if (RxFlag)
		{
			if (RxCount < sizeof(RxBuffer))
			{
				RxBuffer[RxCount++] = Temp;
			}
			else
			{
				RxCount = 0;
			}
			if (Temp == 0x0A) // 换行字符,使用串口助手发送换行符时,推荐使用HEX模式发送
			{
				Usart_SendStr_Length(RxBuffer, RxCount);
				RxCount = 0;
			}
			RxFlag = 0;
		}
	}
}

中断程序

/*!
 * @file        apm32f10x_it.c
 *
 * @brief       Main Interrupt Service Routines
 *
 * @version     V1.0.0
 *
 * @date        2019-9-30
 *
 */

#include "apm32f10x_it.h"
#include "bsp_usartx.h"

/*!
 * @brief   This function handles NMI exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void NMI_Handler(void)
{
}

/*!
 * @brief   This function handles Hard Fault exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void HardFault_Handler(void)
{
    /* Go to infinite loop when Hard Fault exception occurs */
    while (1)
    {
    }
}

/*!
 * @brief   This function handles Memory Manage exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void MemManage_Handler(void)
{
    /* Go to infinite loop when Memory Manage exception occurs */
    while (1)
    {
    }
}

/*!
 * @brief   This function handles Bus Fault exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void BusFault_Handler(void)
{
    /* Go to infinite loop when Bus Fault exception occurs */
    while (1)
    {
    }
}
/*!
 * @brief   This function handles Usage Fault exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void UsageFault_Handler(void)
{
    /* Go to infinite loop when Usage Fault exception occurs */
    while (1)
    {
    }
}

/*!
 * @brief   This function handles SVCall exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void SVC_Handler(void)
{
}

/*!
 * @brief   This function handles Debug Monitor exception
 *
 * @param   None
 *
 * @retval  None
 *
 */
void DebugMon_Handler(void)
{
}

/*!
 * @brief   This function handles PendSV_Handler exception
 *
 * @param   None
 *
 * @retval  None
 *
 */

void PendSV_Handler(void)
{
}

/*!
 * @brief   This function handles SysTick Handler
 *
 * @param   None
 *
 * @retval  None
 *
 */
void SysTick_Handler(void)
{
}

/**
 * @brief 串口1的中断函数
 * @param
 */
void USART1_IRQHandler(void)
{
    extern uint8_t RxFlag;
    extern uint8_t Temp;
    //		uint8_t ch = 0x33;
    if (USART_ReadIntFlag(USARTX, USART_INT_RXBNE) != RESET)
    {
        USART_ClearIntFlag(USARTX, USART_INT_RXBNE);
        RxFlag = 1;
        Temp = USART_RxData(USARTX);
    }
}

代码分析

bsp_usartx.c

bsp_usartx.h文件中都是各种宏定义和函数原型声明,就不看了。我们主要来看下使用USART时相关GPIO怎么配置,和USART参数怎么选。

先看GPIO的配置,如下

    // 定义IO硬件初始化结构体变量
    GPIO_Config_T GPIO_ConfigStruct;
    // 使能USART功能GPIO时钟
    RCM_EnableAPB2PeriphClock(USARTX_RX_GPIO_CLOCK | USARTX_TX_GPIO_CLOCK);
    // 设定USART接收对应IO模式:浮空输入
    GPIO_ConfigStruct.mode = GPIO_MODE_IN_FLOATING;
    GPIO_ConfigStruct.pin = USARTX_RX_PIN;
    // 串口2M,I2C 10M,SPI 50M
    GPIO_ConfigStruct.speed = GPIO_SPEED_2MHz;
    GPIO_Config(USARTX_RX_GPIO_PORT, &GPIO_ConfigStruct);
    // 设定USART发送对应IO模式:复用推挽输出
    GPIO_ConfigStruct.mode = GPIO_MODE_AF_PP;
    GPIO_ConfigStruct.pin = USARTX_TX_PIN;
    GPIO_ConfigStruct.speed = GPIO_SPEED_2MHz;
    // 初始化USART发送对应IO
    GPIO_Config(USARTX_TX_GPIO_PORT, &GPIO_ConfigStruct);

我们之前说过标准库使用的套路,套路如下:

  1. 声明结构体
  2. 开时钟
  3. 配置结构体
  4. 结构体填入初始化函数中
  5. 若要开启相关外设还需开启相关外设

这次我们主要来看下使用串口时的GPIO怎么配置,配置接收引脚时模式选择了GPIO_MODE_IN_FLOATING,为什么要选这个?选其他的行不行,其实选其他的也行,APM32参考手册没写那么详细,中文的STM32参考手册也没写,但是STM32的的英文手册写了,如下图

串口引脚配置

从图中我们可以知道,RX接收引脚可以设置为输入浮空或者上拉输入,最好设置为上拉输入这样可以降低干扰。从图中也不难看出TX发送引脚设置为复用推挽,如果在其他地方看到有人写代码把RX也设置成复用推挽模式,不要觉得人家错了,其实也时可以的,看下图

复用推挽输出

复用输出读取io状态图

注意 虽然输入引脚可以配置成推挽输出,但是不建议用!建议使用输入上拉模式!

GPIO的速度我们之前讲过了,这里再提一下,如下图

GPIO速度配置

看完GPIO的配置,我们再来看下USART的配置

    // 定义USART初始化结构体变量
    USART_Config_T USART_ConfigStruct;
    // 使能USART时钟
    RCM_EnableAPB2PeriphClock(USARTX_CLOCK);
   // USART波特率:115200
    USART_ConfigStruct.baudRate = USARTX_BAUDRATE;
    // USART硬件数据流控制(硬件信号控制传输停止):无
    USART_ConfigStruct.hardwareFlow = USART_HARDWARE_FLOW_NONE;
    // USART工作模式使能:允许接收和发送
    USART_ConfigStruct.mode = USART_MODE_TX_RX;
    // USART校验位:无
    USART_ConfigStruct.parity = USART_PARITY_NONE;
    // USART停止位:1位
    USART_ConfigStruct.stopBits = USART_STOP_BIT_1;
    //   USART字长(有效位):8位
    USART_ConfigStruct.wordLength = USART_WORD_LEN_8B;
    USART_Config(USARTX, &USART_ConfigStruct);
    // 配置USART为中断源,抢占优先级为3,子优先级为0
    NVIC_EnableIRQRequest(USARTX_IRQn, 3, 0);
    // 使能接收中断
    USART_EnableInterrupt(USARTX, USART_INT_RXBNE);
    // 使能USART
    USART_Enable(USARTX);

这里的串口配置还是老套路,套路如下:

  1. 声明结构体
  2. 开时钟
  3. 配置结构体
  4. 结构体填入初始化函数中
  5. 若要开启相关外设还需开启相关外设,这里我们USART_Enable(USARTX);

我们来具体看下结构体配置

/**
 * @brief   USART Configure structure definition
 */
typedef struct
{
    uint32_t                  baudRate;          /*!< Specifies the baud rate */
    USART_WORD_LEN_T          wordLength;        /*!< Specifies the word length */
    USART_STOP_BIT_T          stopBits;          /*!< Specifies the stop bits */
    USART_PARITY_T            parity;            /*!< Specifies the parity */
    USART_MODE_T              mode;              /*!< Specifies the mode */
    USART_HARDWARE_FLOW_T     hardwareFlow;      /*!< Specifies the hardware flow control */
} USART_Config_T;

  • baudRate波特率,APM32F1系列最大波特率可达4.5Mbits/s,我们一般也用不到这么大,常用的波特率2400、4800、9600、19200、38400、57600、115200。双方通信时波特率要相同,否则会乱码。
  • wordLength数据位长度,可选USART_WORD_LEN_8B数据位长度位8位,USART_WORD_LEN_9B数据位长度为9位。
  • stopBits停止位,可选USART_STOP_BIT_1USART_STOP_BIT_0_5USART_STOP_BIT_2USART_STOP_BIT_1_5,分别为1、 0.5、 2、 1.5 个停止位。
  • USART_PARITY_T 校验位,可选USART_PARITY_NONEUSART_PARITY_EVENUSART_PARITY_ODD,分别为无校验,偶校验,奇校验。
  • USART_MODE_T串口模式,可选USART_MODE_RXUSART_MODE_TXUSART_MODE_TX_RX,分别为只发送模式、只接收模式、既发送也接收模式。
  • USART_HARDWARE_FLOW_T硬件流控制,可选参数USART_HARDWARE_FLOW_NONEUSART_HARDWARE_FLOW_RTSUSART_HARDWARE_FLOW_CTSUSART_HARDWARE_FLOW_RTS_CTS,分别为不使能硬件流、使能RTS、使能CTS、同时使能RTS和CTS。

我们配置的都是比较常用的,波特率为115200,数据位长度为8位,无校验,发送和接收,无硬件控制流。我们还使能了接收寄存器不为空中断USART_EnableInterrupt(USARTX, USART_INT_RXBNE);,使能接收中断后还要配置下NVIC,因为所有中断都是由NVIC管理的,如果不使用中断接收而使用轮询接收,效率是比较低的,当然中断接收效率不如DMA接收。

下面我们逐个看下发送函数

/**
 * @brief 发送一个字节数据
 * @param byte 待发送的字符
 */
void Usart_SendByte(uint8_t byte)
{
    // 发送一个字节数据
    USART_TxData(USARTX, byte);
    // 等待发送完毕
    while (USART_ReadStatusFlag(USARTX, USART_FLAG_TXBE) == RESET)
        ;
}

发送一个字节函数为什么这样写,我们来看下参考手册219页,如下图

单字节通讯

我们没有使能发送中断,所以发送时不会产生中断,由手册可以知道为了不覆盖前面的数据我们要等待发送寄存器被清空,也就是while (USART_ReadStatusFlag(USARTX, USART_FLAG_TXBE) == RESET);等待发送寄存器位空,这样我们才能继续发送。

再来看下发送字符串函数

/**
 * @brief 串口发 送字符串,直到遇到字符串结束符
 * @param str 待发送字符串缓冲器
 */
void Usart_SendString(uint8_t *str)
{
    uint32_t k = 0;
    do
    {
        Usart_SendByte(*(str + k));
        k++;
    } while (*(str + k) != '\0');

    // 等待发送完成
    while (USART_ReadStatusFlag(USARTX, USART_FLAG_TXC) == RESET)
    {
    }
}

我们还是来看下参考手册

串口发送字符

主要看最后三条,发送字节就是重复7-8,这个7-8也就是void Usart_SendByte(uint8_t byte)函数,最后一条要等待TXCFLG置1也就是while (USART_ReadStatusFlag(USARTX, USART_FLAG_TXC) == RESET)

我们具体来看下这两个标志位的区别

USART_FLAG_TXBE:当发送缓冲器为空,可以再次写入数据时,该位被硬件置起。对USART_DATA的写操作,将清零该位。此时,数据可能还没发送完成。

USART_FLAG_TXC:当发送数据完成,该位被硬件置起,由软件将其清零。换句话说,如果最后一次发送到数据缓冲区的数据完成了从移位寄存器到信号线TX 时,才置 1,表示数据发送完成,也就是说,这个标志位真正表示数据发送完成。

main.c

uint8_t RxFlag = 0;
uint8_t Temp;
uint8_t RxBuffer[256];

声明了三个变量,RxFlag时接收标志位,置位时表示接收到数据;Temp为接收字节的临时存放区;RxBuffer接收缓冲区,接收到的数据暂时放在里面。

while (1)
	{
		if (RxFlag)
		{
			if (RxCount < sizeof(RxBuffer))
			{
				RxBuffer[RxCount++] = Temp;
			}
			else
			{
				RxCount = 0;
			}
			if (Temp == 0x0A) // 换行字符,使用串口助手发送换行符时,推荐使用HEX模式发送
			{
				Usart_SendStr_Length(RxBuffer, RxCount);
				RxCount = 0;
			}
			RxFlag = 0;
		}
	}

接收标志位置位后,如果已经接收的到数据大小小于接收缓冲区的大小就继续接收,将接收到的数据Temp放在缓冲区中,否则的话将接收到数据的大小置零,下次接收时会覆盖前面已经接收的数据,如果接收到换行符也就是回车,它的ASCII码是10对应的十六进制就是0x0A。

apm32f10x_it.c

/**
 * @brief 串口1的中断函数
 * @param
 */
void USART1_IRQHandler(void)
{
    extern uint8_t RxFlag;
    extern uint8_t Temp;
    if (USART_ReadIntFlag(USARTX, USART_INT_RXBNE) != RESET)
    {
        USART_ClearIntFlag(USARTX, USART_INT_RXBNE);
        RxFlag = 1;
        Temp = USART_RxData(USARTX);
    }
}

该段代码主要就是置位接收标志位,读取接收寄存器中的数据。

我们之前说过,一个中断函数可以给很多中断用,我们要具体判断是哪一个中断发生了,我们要去读取中断的状态,我们这次用的中断是接收寄存器不为空中断,还有很多其他中断,如下图

串口中断表

确定中断发生后,进去第一件事就是清除中断标志位以防后面忘掉造成多次进中断。

标签:USART,void,串口,发送,ConfigStruct,USARTX,GPIO,APM32,接发
From: https://blog.csdn.net/showgu/article/details/143689111

相关文章

  • APM32实现printf串口打印
    Keil环境在Keil环境中使用printf,首先需要打开UseMicroLib,这个库是keil专门为嵌入式设备定制的,比C语言自带的库如stdio、string等占用空间更小,效率更高。首先要点击Keil的魔术棒,如下图把UseMicroLib打上勾,如下图还要包含头文件#include<stdio.h>,在Keil中串口重定向......
  • 虚拟串口工具和串口调试工具详解 - 附下载地址
    简介串口开发过程中,一般需要以下工具用于开发和调试: 虚拟串口工具简介虚拟串口软件,可以在系统中虚拟出串口,这样开发人员可以在没有物理串口设备的情况下进行开发.串口调试工具简介串口调试工具主要用于给串口发送信息,测试串口是否连通,发送消息是否正常被接收等.......
  • Linux系统编译QT5.15.0及串口问题
    编译流程:1>下载QT源码源码的下载可以到qt的官网http://www.qt.io/download/ 2>解压tarxvfqt-everywhere-src-x.x.x.tar.gz注意后缀和解压方式3>配置 ./configure进行环境配制。4>编译执行make编译,时间长,大概在三四个小时左右。5>安装sudomakeinstall需要5分钟......
  • Qt | 串口调试工具实现
    点击上方"蓝字"关注我们01、QSerialPort>>>QSerialPort是Qt框架中的一个类,用于串行通信。它提供了一个简单的接口,允许开发者通过串口与外部设备(如传感器、相机、单片机等)进行数据交换。QSerialPort支持多种串口操作,可以方便地设置波特率、数据位、停止位和校验位等通......
  • ESP32串口通信
    基于ArduinoIDE开发环境编写的ESP32程序示例:通过串口与电脑通信,按下boot按键开始以1Hz的频率发送学号,再次按下则停止发送//引入ESP32的相关库#include<Arduino.h>//定义学号,这里假设学号是123456,你需要替换成你自己的真实学号constchar*studentID="123456";......
  • Xamarin.Forms-手机串口调试程序开发文档
    Xamarin.Forms手机串口调试程序开发文档1.开发背景:因工作性质特殊,需要通过手持设备与电力设备进行报文通讯,达到设备状态、地址码等数据的下发及查询功能。但因为后期手持设备厂家停产,维护不及时,造成设备稀缺,无法满足正常工作需要,特制作此手机APP,通过串口驱动连接串口转红外设备......
  • HarmonyOS蓝牙串口协议(SPP)详解:实现设备间可靠数据交换
    本文旨在深入探讨华为鸿蒙HarmonyOSNext系统(截止目前API12)的技术细节,基于实际开发实践进行总结。主要作为技术分享与交流载体,难免错漏,欢迎各位同仁提出宝贵意见和问题,以便共同进步。本文为原创内容,任何形式的转载必须注明出处及原作者。在智能设备互联互通的世界里,蓝牙技术扮......
  • 基于ESP32的桌面小屏幕实战[3]:硬件设计之主控模块、墨水屏和TP、USB转串口、蜂鸣器、
    1.主控模块主控用的是ESP32-S。在立创商城搜索它,找到ESP32-S,复制编号。回到嘉立创EDA,用编号搜原件。把原件放置在原理图中。按照之前的外设接口说明接线。注意,给引脚命名的时候,要单击鼠标右键,点击属性,在名称处编辑。打开这个芯片的数据手册,找到外围设计原理图。参......
  • 广州大彩串口屏输入密码后加入加载中画面
    众所周知,我们在使用大型设备或者玩游戏的时候点击开始或者运行时总会有一个加载中的界面出现,这不仅提供了缓冲时间供给cpu进行加载,而且使得我们的设备显得更加的完整,变得更加的高级。但是网上对于输入密码后加入加载中界面的文章寥寥无几,许多学者想要设置相关的内容,但是却对此......
  • ubuntu串口权限修改
    文章目录一、确认串口设备名称二、修改串口权限方法一:临时修改权限方法二:永久修改权限(推荐)三、通过udev规则修改权限(高级)ubuntu串口权限修改一、确认串口设备名称首先,我们需要确认串口设备的名称。在Ubuntu系统中,串口设备通常位于/dev/ttyS*或/dev/ttyUSB*目录下......