CH32V208芯片支持串口通讯,查看数据手册可知,其有3个串口。
官方的参考手册中对USART的功能进行了详细的介绍
在官方提供的演示例程中,提供了串口的多种功能演示例程。
本次对其中的DMA、Interrupt以及Printf功能进行测试和学习。使用MounRiver Studio的导入功能,将相应的工程导入到工作空间中。
导入后的效果如下所示
通过串口打印数据是嵌入式开发中必备的一项功能,在导入的USART_Printf工程下可以看到与串口打印数据相关的源代码。
相关的代码如下
#ifndef __DEBUG_H
#define __DEBUG_H
#include "stdio.h"
#include "ch32v20x.h"
#ifdef __cplusplus
extern "C" {
#endif
/* UART Printf Definition */
#define DEBUG_UART1 1
#define DEBUG_UART2 2
#define DEBUG_UART3 3
/* DEBUG UATR Definition */
#ifndef DEBUG
#define DEBUG DEBUG_UART1
#endif
void USART_Printf_Init(uint32_t baudrate);
#if(DEBUG)
#define PRINT(format, ...) printf(format, ##__VA_ARGS__)
#else
#define PRINT(X...)
#endif
#ifdef __cplusplus
}
#endif
#endif
/*********************************************************************
* @fn USART_Printf_Init
*
* @brief Initializes the USARTx peripheral.
*
* @param baudrate - USART communication baud rate.
*
* @return None
*/
void USART_Printf_Init(uint32_t baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
#if(DEBUG == DEBUG_UART1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
#elif(DEBUG == DEBUG_UART2)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
#elif(DEBUG == DEBUG_UART3)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
#endif
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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_Tx;
#if(DEBUG == DEBUG_UART1)
USART_Init(USART1, &USART_InitStructure);
USART_Cmd(USART1, ENABLE);
#elif(DEBUG == DEBUG_UART2)
USART_Init(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
#elif(DEBUG == DEBUG_UART3)
USART_Init(USART3, &USART_InitStructure);
USART_Cmd(USART3, ENABLE);
#endif
}
/*********************************************************************
* @fn _write
*
* @brief Support Printf Function
*
* @param *buf - UART send Data.
* size - Data length
*
* @return size: Data length
*/
__attribute__((used))
int _write(int fd, char *buf, int size)
{
int i;
for(i = 0; i < size; i++){
#if(DEBUG == DEBUG_UART1)
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData(USART1, *buf++);
#elif(DEBUG == DEBUG_UART2)
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
USART_SendData(USART2, *buf++);
#elif(DEBUG == DEBUG_UART3)
while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET);
USART_SendData(USART3, *buf++);
#endif
}
return size;
}
程序的演示效果如下
配合USART的中断实现收发功能,实现实时地收发数据,同时可以减小CPU的使用率。示例程序中演示了使用串口地TXE和RXNE中断实现数据的收发。
程序硬件上使用USART2和USART3作为收发的硬件,两个串口都开启中断功能。主程序中初始化硬件,并配置相关外设的功能后,USART3发送数据并通过检查TXE中断标志位和计数变量确定数据是否发送完毕,同时USART2开始了接收中断,在中断函数中进行数据接收和计数处理;接下来USART2和USART3互换位置,进行数据的收发。数据接收完毕后,对缓存区中的数据进行对比,确定数据是否完整地实现了传输。
串口配置和中断检测函数
/*********************************************************************
* @fn USARTx_CFG
*
* @brief Initializes the USART2 & USART3 peripheral.
*
* @return none
*/
void USARTx_CFG(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
USART_InitTypeDef USART_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 | RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
/* USART2 TX-->A.2 RX-->A.3 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART3 TX-->B.10 RX-->B.11 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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_Tx | USART_Mode_Rx;
USART_Init(USART2, &USART_InitStructure);
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Init(USART3, &USART_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_Cmd(USART2, ENABLE);
USART_Cmd(USART3, ENABLE);
}
发送中断检测代码如下:
while(TxCnt2 < TxSize2) /* USART3--->USART2 */
{
USART_SendData(USART3, TxBuffer2[TxCnt2++]);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET) /* waiting for sending finish */
{
}
}
while(TxCnt1 < TxSize1) /* USART2--->USART3 */
{
USART_SendData(USART2, TxBuffer1[TxCnt1++]);
while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET) /* waiting for sending finish */
{
}
}
接收数据中检测函数
/*********************************************************************
* @fn USART2_IRQHandler
*
* @brief This function handles USART2 global interrupt request.
*
* @return none
*/
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
RxBuffer1[RxCnt1++] = USART_ReceiveData(USART2);
if(RxCnt1 == TxSize2)
{
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
Rxfinish1 = 1;
}
}
}
/*********************************************************************
* @fn USART3_IRQHandler
*
* @brief This function handles USART3 global interrupt request.
*
* @return none
*/
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
RxBuffer2[RxCnt2++] = USART_ReceiveData(USART3);
if(RxCnt2 == TxSize1)
{
USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);
Rxfinish2 = 1;
}
}
}
借助DMA配合USART可以实现大量数据地传输的同时减少CPU的使用率。在官方提供的示例程序中,配置了USART2和USART3的参数和DMA通道参数。随后,使用DMA的数据传输,并在主循环中对DMA传输的状态位进行检测,当检测到所有的DMA传输完毕后,对缓存区内的数据进行对比,并置位相应的状态位。通过调试串口向上位机发送调试数据。
DMA配置函数如下:
/*********************************************************************
* @fn DMA_INIT
*
* @brief Configures the DMA for USART2 & USART3.
*
* @return none
*/
void DMA_INIT(void)
{
DMA_InitTypeDef DMA_InitStructure = {0};
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel7);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DATAR); /* USART2->DATAR:0x40004404 */
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)TxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = TxSize1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel7, &DMA_InitStructure);
DMA_DeInit(DMA1_Channel6);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART2->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = TxSize2;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
DMA_DeInit(DMA1_Channel2);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART3->DATAR); /* USART2->DATAR:0x40004804 */
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer2;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = TxSize2;
DMA_Init(DMA1_Channel2, &DMA_InitStructure);
DMA_DeInit(DMA1_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART3->DATAR);
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)RxBuffer2;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = TxSize1;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);
}
主函数中DMA启动以及传输完成判断标志位检测
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("USART DMA TEST\r\n");
DMA_INIT();
USARTx_CFG(); /* USART2 & USART3 INIT */
USART_DMACmd(USART2, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
USART_DMACmd(USART3, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);
while(DMA_GetFlagStatus(DMA1_FLAG_TC7) == RESET) /* Wait until USART2 TX DMA1 Transfer Complete */
{
}
while(DMA_GetFlagStatus(DMA1_FLAG_TC6) == RESET) /* Wait until USART2 RX DMA1 Transfer Complete */
{
}
while(DMA_GetFlagStatus(DMA1_FLAG_TC2) == RESET) /* Wait until USART3 TX DMA1 Transfer Complete */
{
}
while(DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET) /* Wait until USART3 RX DMA1 Transfer Complete */
{
}
运行的输出结果如图所示:
更多回帖