zly188 发表于 2021-11-27 11:04:13

CH32V103应用教程——USART

本帖最后由 zly188 于 2021-11-27 11:05 编辑

本章教程通过CH32V103的串口与上位机之间进行数据的发送和接收,具体执行步骤如下:
上位机向CH32V103发送数据;
CH32V103接收数据并发送给上位机;
上位机接收CH32V103发送的数据并显示。

1、USART简介及相关函数介绍
USART全称为Universal Synchronous/Asynchronous Receiver/Transmitter(通用同步/异步串行接收/发送器),是一个全双工通用同步/异步串行收发模块,是一个高度灵活的串行通信设备。嵌入式中所说串口一般为UART,其全称为Universal Asynchronous Receiver/Transmitter(通用异步串行接收/发送器)。相较于USART,UART只有异步通信功能,即串口通信时无需对外提供时钟输出。

CH32V103的USART支持全双工或半双工的同步或异步通信,支持NRZ数据格式,支持分数波特率发生器(最高4.5Mbps)、支持可编程的数据长度和可配置的停止位,支持LIN、lrDA编码器和智能卡,支持DMA并具有多种中断源。

USART双向通信至少需要2个引脚:接收数据输入(RX)和发送数据输出(TX)。
RX:接收数据输入。
TX:发送数据输出。

串口收发通过调用库函数内部相关函数并结合相关寄存器进行配置实现通信。库函数内部函数及介绍如下:
void USART_DeInit(USART_TypeDef* USARTx);
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
void USART_SendBreak(USART_TypeDef* USARTx);
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);1.1、void USART_DeInit(USART_TypeDef* USARTx)
功能:将USARTx外围寄存器初始化为其默认重置值。通俗讲即可通过该函数实现串口复位。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备。

1.2、void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
功能:串口参数初始化。根据USART_InitStruct中的指定参数初始化USART外设。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_InitStruct指向包含指定USART外围设备的配置信息的USART_InitTypeDef结构的指针。

1.3、void USART_StructInit(USART_InitTypeDef* USART_InitStruct)
功能:用默认值填充每个USART_InitStruct成员。
输入:USART_InitStruct:指向要初始化的USART_InitTypeDef结构的指针。

1.4、void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct)
功能:根据USART_ClockInitStruct中指定的参数初始化USARTx外围时钟。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_ClockInitStruct:指向包含指定USART外围设备的配置信息的USART_ClockInitTypeDef结构的指针。

1.5、void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct)
功能:用默认值填充每个USART_ClockInitStruct成员。
输入:USART_ClockInitStruct:指向将被初始化的USART_ClockInitTypeDef结构的指针。

1.6、void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用指定的USART外围设备。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;NewState为对应状态:ENABLE,使能;DISABLE,禁止使能。

1.7、void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
功能:启用或禁用指定的USART中断。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_IT为指定要启用或禁用的USART中断源;NewState为对应状态:ENABLE,使能中断;DISABLE,禁止使能中断。

1.8、void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState)
功能:启用或禁用USART DMA接口。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_DMAReq:指定DMA请求。USART_DMAReq_Tx:USART DMA发送请求。USART_DMAReq_Rx:USART DMA接收请求。NewState为对应状态:ENABLE,使能USART DMA;DISABLE,禁止使能USART DMA。

1.9、void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address)
功能:设置USART节点的地址。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_Address:表示USART节点的地址。

1.10、void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp)
功能:选择USART唤醒方法。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_WakeUp:指定USART唤醒方法。USART_WakeUp_IdleLine:由空闲线路检测唤醒。USART_WakeUp_AddressMark:通过地址标记唤醒。

1.11、void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:确定USART是否处于静音模式。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;NewState为对应状态:ENABLE,启用静音模式;DISABLE,禁止启用静音模式。

1.12、void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength)
功能:设置USART LIN中断检测长度。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;USART_LINBreakDetectLength:指定LIN中断检测长度。USART_LINBreakDetectLength_10b:10位中断检测。USART_LINBreakDetectLength_11b:11位中断检测。

1.13、void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用USART LIN模式。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;NewState为对应状态:ENABLE,启用USART LIN模式;DISABLE,禁止启用USART LIN模式。

1.14、void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
功能:通过USARTx外围设备传输单个数据。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备;Data为要发送传输的数据。

1.15、uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
功能:返回USARTx外围设备最近接收的数据。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备。

1.16、void USART_SendBreak(USART_TypeDef* USARTx)
功能:传输中断字符。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备。

1.17、void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime)
功能:USART设置指定的时间。
输入:USARTx:其中x可以是1、2或3来选择UART外围设备。USART_GuardTime:指定保护时间。

1.18、void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler)
功能:设置系统时钟预分频器。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。USART_Prescaler:指定预分频器时钟。

1.19、void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用USART智能卡模式。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。NewState为对应状态:ENABLE,启用USART智能卡模式;DISABLE,禁止启用USART智能卡模式。

1.20、void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用NACK传输。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。NewState为对应状态:ENABLE,启用NACK传输;DISABLE,禁止启用NACK传输。

1.21、void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用USART半双工通信。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。NewState为对应状态:ENABLE,启用USART半双工通信;DISABLE,禁止启用USART半双工通信。

1.22、void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用USART的8x过采样模式。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。NewState为对应状态:ENABLE,启用USART的8x过采样模式;DISABLE,禁止启用USART的8x过采样模式。

1.23、void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用USART的一位采样方法。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。NewState为对应状态:ENABLE,启用USART的一位采样方法;DISABLE,禁止启用USART的一位采样方法。

1.24、void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode)
功能:配置USART的IrDA接口。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。USART_IrDAMode:指定IrDA模式。USART_IrDAMode_LowPower和USART_IrDAMode_Normal。

1.25、void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState)
功能:启用或禁用USART的IrDA接口。
输 入:USARTx:其中x可以是1、2或3来选择UART外围设备。NewState为对应状态:ENABLE,启用USART的IrDA接口;DISABLE,禁止启用USART的IrDA接口。

1.26、FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)
功能:检查是否设置了指定的USART标志。
输入:USARTx:其中x可以是1、2、3来选择USART外围设备;USART_FLAG:指定要检查的标志。

1.27、void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG)
函能:清除USARTx的挂起标志。
输入:USARTx:其中x可以是1、2、3来选择USART外围设备;USART_FLAG:指定要清除的标志。

1.28、ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
功能:检查指定的USART中断是否已发生。
输入:USARTx:其中x可以是1、2、3来选择USART外围设备;USART_IT:指定要检查的USART中断源。

1.29、void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT)
功能:用于清除USARTx的中断挂起位。
输入:USARTx:其中x可以是1、2、3来选择USART外围设备;USART_IT:指定要清除的中断挂起位。

1.30、void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
功能:配置优先级分组:抢占优先级和子优先级。
输入:NVIC_PriorityGroup:指定优先级分组位长度。

1.31、void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
功能:根据NVIC_InitStruct中指定的参数初始化NVIC外围设备。
输入:NVIC_InitStruct:指向包含指定NVIC外围设备配置信息的NVIC_InitTypeDef结构的指针。

以上函数均为本次串口收发实验需要用到或可能用到的函数,在进行串口收发程序编写时,相关函数只需在程序中进行调用即可。

2、硬件设计
由于CH32V103系列MCU的串口1已经通过串口芯片连接在开发板右侧USB口,写入程序后,可以直接通过上位机连接右侧的USB口。


3、软件设计
串口收发在传输过程中,串口发送端将字节数据以串行方式逐个比特发送出去,串口接收端逐个比特接收数据,然后重新将其组织为字节数据。串口收发程序具体实现步骤为:

[*]定义一个GPIO_InitTypeDef类型结构体,一个USART_InitTypeDef类型结构体、NVIC_InitTypeDef类型体;
[*]使能串口1 RX引脚和TX引脚GPIO时钟和USART1时钟;
[*]GPIO端口模式设置及串口参数初始化;
[*]配置中断控制器;
[*]使能串口并使能USART接收中断;
[*]编写发送单个数据函数和发送字符串函数;
[*]编写USART接收中断服务函数并实现数据接收和发送。


串口1初始化,并设置中断。

/*******************************************************************************
* 函数名: USARTx_CFG
* 描述    : USART1初始化.
* 输入    : None
* 返回    : None
*******************************************************************************/
void USARTx_CFG(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDefNVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能串口1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟

USART_DeInit(USART1);
/* USART1 TX-->A.9   RX-->A.10 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;            //设置PA9为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //设置PA10为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 115200;               //设置串口波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;       //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;          //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_Init(USART1, &USART_InitStructure);                  //初始化串口

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;      //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);                              //中断优先级初始化

USART_Cmd(USART1, ENABLE);                                 //使能串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);               //开启中断
}
串口1发送字符
/*******************************************************************************
* 函数名: USARTx_SendByte
* 描述    : 串口发送字节数据.
* 输入    : None
* 返回    : None
*******************************************************************************/
void USARTx_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
    USART_SendData(pUSARTx, data);
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}串口1发送字符串
/*******************************************************************************
* 函数名: USARTx_SendStr
* 描述    : 串口发送字符串.
* 输入    : None
* 返回    : None
*******************************************************************************/
void USARTx_SendStr(USART_TypeDef* pUSARTx, char *str)
{
    uint8_t i = 0;
    do
    {
       USARTx_SendByte(pUSARTx, *(str+i));
       i++;
    }while(*(str+i) != '\0');
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}
串口1中断函数,接收到字符马上返回上传相同字符
/*******************************************************************************
* 函数名: USART2_IRQHandler
* 描述    : 串口中断函数.
* 输入    : None
* 返回    : None
*******************************************************************************/
extern "C"{
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //中断产生
{
    USART_ClearITPendingBit(USART1,USART_IT_RXNE);    //清除中断标志
    ucTemp = USART_ReceiveData(USART1);               //接收数据
    USART_SendData(USART1, ucTemp);                   //发送数据
}
}
}由于天问Block采用C++编译,中断函数必须用:extern "C"{},定义这是一个C代码,不然中断不会跳入。


所有程序:
#include <CH32V103.h>

/*******************************************************************************
* 函数名: USARTx_CFG
* 描述    : USART1初始化.
* 输入    : None
* 返回    : None
*******************************************************************************/
void USARTx_CFG(void)
{
GPIO_InitTypeDefGPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDefNVIC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能串口1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能GPIOA时钟

USART_DeInit(USART1);
/* USART1 TX-->A.9   RX-->A.10 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;            //设置PA9为复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;      //设置PA10为浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);

USART_InitStructure.USART_BaudRate = 115200;               //设置串口波特率为115200
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;       //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;          //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //发送和接收模式
USART_Init(USART1, &USART_InitStructure);                  //初始化串口

NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;      //抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         //子优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);                              //中断优先级初始化

USART_Cmd(USART1, ENABLE);                                 //使能串口
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);               //开启中断
}

/*******************************************************************************
* 函数名: USARTx_SendByte
* 描述    : 串口发送字节数据.
* 输入    : None
* 返回    : None
*******************************************************************************/
void USARTx_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
    USART_SendData(pUSARTx, data);
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/*******************************************************************************
* 函数名: USARTx_SendStr
* 描述    : 串口发送字符串.
* 输入    : None
* 返回    : None
*******************************************************************************/
void USARTx_SendStr(USART_TypeDef* pUSARTx, char *str)
{
    uint8_t i = 0;
    do
    {
       USARTx_SendByte(pUSARTx, *(str+i));
       i++;
    }while(*(str+i) != '\0');
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET);
}

/*******************************************************************************
* 函数名: USART2_IRQHandler
* 描述    : 串口中断函数.
* 输入    : None
* 返回    : None
*******************************************************************************/
extern "C"{
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //中断产生
{
    USART_ClearITPendingBit(USART1,USART_IT_RXNE);    //清除中断标志
    ucTemp = USART_ReceiveData(USART1);               //接收数据
    USART_SendData(USART1, ucTemp);                   //发送数据
}
}
}

int main(void)
{
CH32_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
USARTx_CFG();
USARTx_SendStr(USART1, "This is a test data.\n");
while(1){

}
return 1;
}




4.演示验证
在天问Block插入USB左边的TypeC口下载这个程序,再插入右侧的USB口,打开软件监视器。上位机波特率设置成115200,点击“连接串口”。如下图:

按下KEY1(RUN)键,监视器显示:This is a test data.
发送1234,串口监视器马上返回显示:1234

源代码在范例代码->CH32V外设代码范例->2.USART.hd



gaobaoliu 发表于 2022-11-5 08:37:27

很好的例子
页: [1]
查看完整版本: CH32V103应用教程——USART