最近想玩一下,CH32V307这款开发板,主要看到是RISC-V架构,并且功能比较丰富,所以就想来看看这个开发板功能强大。先来玩玩串口。
一、串口介绍
1、串口的作用
1)设备互联
单片机的串口像一个小型“数据中转站”,允许它与电脑交换调试信息(如温度数据)、控制传感器(如读取湿度)、连接WiFi模块等;就好像智能家居中,单片机通过串口接收手机指令,再控制灯泡开关
2)实现远距离通信
串口只需两根数据线(TXD发送、RXD接收)和一根地线(GND),比并行通信(需8根以上)更省硬件成本,且可通过RS485等标准传输到几百米外。
2、工作原理
想象两个人用摩斯密码发电报:
1)数据拆解:单片机把数据(如字母“A”)拆成二进制位(如01000001),一位一位发送,像逐颗发糖。
2)数据打包:每帧数据像一个小包裹,包含:
起始位(低电平):喊一声“开始!”(占用1位);
数据位(5-9位):实际传输的信息(如8位二进制);
停止位(高电平):喊一声“结束!”(占用1-2位)(可选校验位:防传输错误)
3)同步节奏(波特率)双方需约定传输速度(波特率),如9600bps=每秒传9600位。若速度不一致,接收方会“听错”数据。
4)全双工通信发送(TXD)和接收(RXD)可同时进行,像双向车道,允许一边说话一边听对方回复
3、串口的优点
简单可靠:2根线解决通信问题,抗干扰强(尤其RS485)。
资源占用少:单片机内部有专用硬件(UART),编程时只需配置寄存器(如SCON)。
生态丰富:几乎所有模块(GPS、蓝牙等)都支持串口指令控制
总之,串口就是单片机与外界对话的“嘴巴”和“耳朵”,用最低成本实现高效数据交换,堪称嵌入式系统的通信基石
4、CH32V307串口的特点
1)全双工或半双工的同步或异步通信
2)NRZ 数据格式
3)分数波特率发生器,最高 9Mbps
4)可编程数据长度
5)可配置的停止位
6)支持 LIN,IrDA 编码器,智能卡
7)支持 DMA
8)多种中断源
二、代码实现
本次主要通过串口中断接收数据,另外使用两个串口进行数据互发,看看数据是否能够正常接收
1、中断接收和配置两个串口,一个串口发送,一个串口接收
代码编写
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
/*
*@Note
USART Print debugging routine:
USART1_Tx(PA9).
This example demonstrates using USART1(PA9) as a print debug port output.
*/
#include "debug.h"
/* Global typedef */
/* Global define */
#define RX_BUF_SIZE 512
#define TX_BUF_SIZE 512
#define USART_MODE_DMA 0
/* Global Variable */
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void USART2_Init(void);
void DMA_INIT(void);
void SYSTICK_Init_Config(u_int64_t ticks);
uint8_t g_usart2RxBuf[RX_BUF_SIZE] = {0};
uint8_t g_usart2TxBuf[TX_BUF_SIZE] = {0};
uint16_t g_usart2RxCnt = 0;
uint16_t g_usart2TxCnt = 0;
uint8_t g_usart2SendOK = 1;
volatile uint8_t g_usart2RecvOK = 0;
uint8_t g_rxTimeOut = 10;
/*********************************************************************
* @fn main
*
* [url=home.php?mod=space&uid=247401]@brief[/url] Main program.
*
* [url=home.php?mod=space&uid=266161]@return[/url] none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
SYSTICK_Init_Config((SystemCoreClock / 1000)-1);
#if USART_MODE_DMA
DMA_INIT();
#else
#endif
USART2_Init();
#if USART_MODE_DMA
USART_DMACmd(USART2, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);
#else
#endif
while(1)
{
if (g_usart2RecvOK) {
g_usart2RecvOK = 0;
printf("recved data:\r\n");
for(uint16_t i = 0;i< g_usart2RxCnt;i++) {
printf("%c",g_usart2RxBuf[i]);
}
g_usart2RxCnt = 0;
printf("\r\n");
}
}
}
/*********************************************************************
* @fn USART2_Init
*
* @brief Initializes the USART2 peripheral.
*
* @return none
*/
void USART2_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
USART_InitTypeDef USART_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, 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);
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);
#if USART_IDLE_RECV_MODE
USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
#else
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
#endif
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);
USART_Cmd(USART2, ENABLE);
}
/*********************************************************************
* @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)g_usart2TxBuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = TX_BUF_SIZE;
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)g_usart2RxBuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = RX_BUF_SIZE;
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
}
void USART2_Send_Data(uint8_t *data,uint16_t size) {
while(!g_usart2SendOK);
USART_SendData(USART2, *data++);
g_usart2SendOK = 0;
}
/*********************************************************************
* @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)
{
USART_ClearFlag(USART2,USART_FLAG_RXNE);
if(g_usart2RxCnt > RX_BUF_SIZE) {
g_usart2RxCnt = 0;
} else {
g_rxTimeOut = 1;
g_usart2RxBuf[g_usart2RxCnt++] = USART_ReceiveData(USART2);
}
}
}
/*********************************************************************
* @fn SYSTICK_Init_Config
*
* @brief SYSTICK_Init_Config.
*
* @return none
*/
void SYSTICK_Init_Config(u_int64_t ticks)
{
SysTick->SR &= ~(1 << 0);//clear State flag
SysTick->CMP = ticks;
SysTick->CNT = 0;
SysTick->CTLR = 0xF;
NVIC_SetPriority(SysTicK_IRQn, 15);
NVIC_EnableIRQ(SysTicK_IRQn);
}
/*********************************************************************
* @fn SysTick_Handler
*
* @brief SysTick_Handler.
*
* @return none
*/
void SysTick_Handler(void)
{
if(SysTick->SR == 1)
{
SysTick->SR = 0;//clear State flag
if (g_rxTimeOut) {
g_rxTimeOut--;
if (!g_rxTimeOut)
{
g_usart2RecvOK = 1; //一帧接收完成
}
}
}
}
2、下载验证
1)串口助手设置100ms定时发送
接收和发送都为59条数据
2)串口助手设置5ms定时发送
波特率为115200 bit/s 以一个起始位、一个停止位计算 传输一个字节;
传输1个bit位需要 1/115200 = 8.6805us;
传输10bit = 86.805us; 那么两个字节之间的间隔为一个停止位和起始位
36个字节传输需要:86.805*36 = 3.125ms
再加上每次1ms的延时,以及printf的执行,会导致有些打印出来,因此只能搜索is来统计数据
串口中断处理程序
综上:间隔100ms和间隔5ms发送和接收都是没有问题,可见CH32V307上手还是很快的,直接参考例程即可上手,快速开发。
|