目录

前言:

一、串/并行通信简介

串行通信:

并行通信:

二、UART串口通信简介

三、CS32L015C8T6串口硬件资源介绍

四、串口配置流程

五、完整代码及注释

六、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~


前言:

        长颈鹿最近在使用芯海的CS32L015C8T6单片机进行工程开发,发现网上有关这款单片机的资料非常少,而且官方提供的驱动代码有些不适用,因此决定记录一下造轮子的过程,前车之鉴,后车之师,希望能够帮助后来的开发者减少一些重复性造轮子的工作,少走一些弯路。

一、串/并行通信简介

        我们今天要介绍的串口通信,在嵌入式系统中指的就是UART串口协议,属于串行通信中的一种具体的方法,需要知道 串行通信 和 串口通信 的区别:串行通信(Serial Port)是一个广泛的类,不仅包括UART串口协议,还包括SPI、IIC、CAN等我们所熟知的协议;区别于多数据线同时收发数据的并行通讯(同时传输多个比特位),在串行通信中,数据以位的形式,按顺序通过单一通信线路发送接收。

串行通信:

        非常常见,我们在开发过程中经常遇到的IIC、SPI、UART、CAN、RS232、RS485、USB等,全部都是以串行通信为基础的通信协议,并且串行通信的抗干扰能力强,因为只用一根数据线进行发送/接收,不用担心IO口高速工作状态下的高频信号干扰问题和时钟同步问题,再结合上保障通信安全的校验位,通信速度已经可以做到非常快了,现在的USB3.0,SATA等串行通信技术,通信速率已经可以达到5Gbps以上,这个速度远超过常规并行通信能达到的200Mbps。

并行通信:

        如今,并行通信在实际项目中已经不常见了,少部分LCD显示屏还会使用并口通信。主要是因为并行通信的对硬件资源要求高,要占用许多IO口,但是速度却受限。并行通信就相当于许多个高速工作的IO口排列在一起,当电信号快速翻转时,可以想象一下,插上一根铁丝那不就成天线发射接收器了吗,所以说相互之间非常容易受到干扰;还有一点,就是高速工作状态下的时钟信号协同比较困难,容易出错,这两点因素限制了并行通信的发展,强调一点,不是通信引线越多,通信速度就越快,至少2025年来看是这样的。

        但是有个例外,CPU中的总线采用的仍是并行通信,例如STM32MCU中的32位数据总线和地址总线,采用并口通信进行数据通信,每次能处理32位数据,能以非常高速稳定运行,这是为什么呢?

        因为CPU内部,数据通信不需要引线连接,而是通过光刻或者腐蚀制造的金属互连层来进行通信,通信距离非常短,只有微米级,IO口之间的干扰可以降到很低,并且芯片内部时钟非常精确,可以改善并行通信高速状态下时钟信号难以同步的问题,所以可以使用并行通信达到很高的传输速率(Gbps),这是一个特殊情况。

二、UART串口通信简介

串口通信连接示意图

        UART串口通信是嵌入式开发中非常常见的一种 串行 异步 数据通信协议,一共有4个引脚,VCC(供电引脚)、GND(共地引脚)、Tx(数据发送引脚)、Rx(数据接收引脚),主机和从机在通信开始前,需要先约定好双方通信的频率(也叫波特率)、数据有效位个数、校验位、停止位等参数,在硬件连接上,主机的Tx端口需要连接到从机的Rx上,Rx端口需要连接到从机的Tx端口上,GND互相连接,作为基准低电平,连接方式如上图所示。

串口通信时序图

        UART时序基于波特率而非外部时钟信号,使用起始位、数据位、奇偶校验位和停止位的格式来传输数据,按帧发送接收数据。最常用UART数据帧格式是:8/N/1,8指的是8位数据位,N指的是无校验位,1指的是1位停止位。如图所示,就是一帧UART数据的格式,一帧共10位,包括1位起始位,8位数据位,1位停止位,非常简单明了,当然,UART也有其他的格式,例如9位数据位、2位停止位等,但都大差不差,可自行脑部。

三、CS32L015C8T6串口硬件资源介绍

        CS32L015C8T6单片机有4个通用串口外设(UART),1个支持低功耗模式的串口外设(LPUART),两者的区别如下图所示:

        由图可知,低功耗串口LPUART相较于通用串口,缺少了IrDA模式,这里简单介绍一下IrDA协议:串口的IrDA模式是指将串口配置为使用IrDA协议(红外数据通信协议)进行数据通信的一种特殊工作模式,那什么是IrDA呢?IrDA是一种通过红外线进行数据传输的协议,由1993年红外数据协会制定。在IrDA模式下,串口的Tx和Rx不再传输电信号,而是驱动红外LED光发射红外光信号,同时使用Rx管脚接收外设发射的红外光信号。普通串口通过电信号来进行数据通信,需要硬件物理链接,IrDA模式通过红外光信号进行数据通信,不需要物理连接。

        GPIO口配置为串口输入输出时,对应的复用功能可以通过查找芯片数据手册得到,如下图所示:

四、串口配置流程

        不管什么类型的MCU,配置UART都有相关的一套流程,相差无几,但需要注意的是,CS32L015串口配置中有一个参数叫 “BaudDouble”,字面意思似乎是双倍波特率,但实际上该参数开启后,在相同的系统时钟下,可以支持更高的波特率,但串口本身配置的波特率不会变化。串口配置流程如下所示:

        1、GPIO复用功能初始化

        2、UART串口配置初始化

        3、设置串口中断

        4、配置中断回调函数

五、完整代码及注释

1、GPIO初始化

/******************************************************************************
[函数名称]MX_UART_MspInit
[函数功能]串口GPIO初始化函数
[参    数]
[返 回 值]
[备    注]
******************************************************************************/
void MX_UART_MspInit()
{
    __HAL_RCC_GPIOA_CLK_ENABLE();	//使能GPIOA的RCC时钟
	GPIO_InitTypeDef GPIOx = {0};
	
	GPIOx.Mode            = GPIO_MODE_AF;			//GPIO复用模式
	GPIOx.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;	//关闭去抖动
	GPIOx.SlewRate        = GPIO_SLEW_RATE_HIGH;	//高速
	GPIOx.DrvStrength     = GPIO_DRV_STRENGTH_HIGH;	//强驱动
	GPIOx.Alternate       = GPIO_AF_MODE_1;	        //配置串口复用功能
	GPIOx.OpenDrain       = GPIO_PUSHPULL;	        //GPIO推挽输出

	GPIOx.Pin             = UART_TX_Pin;
    GPIOx.Pull            = GPIO_PULLUP;		    //上拉
	HAL_GPIO_Init(UART_TX_GPIO_Port, &GPIOx);	

	GPIOx.Pin      	 	  = UART_RX_Pin;
    GPIOx.Pull            = GPIO_NOPULL;
	HAL_GPIO_Init(UART_RX_GPIO_Port, &GPIOx);
}

2、串口UART初始化

UART_HandleTypeDef huart1 = {0};
/******************************************************************************
[函数名称]MX_UART_Init
[函数功能]串口初始化函数
[参    数]
[返 回 值]
[备    注]
******************************************************************************/
void MX_UART_Init()
{
	MX_UART_MspInit();
	__HAL_RCC_UART0_CLK_ENABLE();	//使能UART0串口时钟
	
	huart1.Instance        = UART0;		
	huart1.Init.BaudRate   = 115200;                    //设置波特率
	huart1.Init.BaudDouble = UART_BAUDDOUBLE_ENABLE;	//支持更高波特率
	huart1.Init.WordLength = UART_WORDLENGTH_8B;		//8位数据长度
	huart1.Init.Parity     = UART_PARITY_NONE;			//无奇偶校验
	huart1.Init.Mode       = UART_MODE_TX_RX;			//全双工
	HAL_UART_Init( &huart1 );
	
	//HAL_NVIC_SetPriority( UART0_IRQn, 2 );
	NVIC_EnableIRQ( UART0_IRQn );		//开启串口中断使能
	
    HAL_UART_Receive_IT(&huart1,  &receData, 1);	//开启接收中断
}

3、中断回调函数配置

/*****************************************************************************
[函数名称]HAL_UART_RxCpltCallback
[函数功能]接收中断回调函数
[参    数]
[备    注]1、在中断函数中处理,接收完成1个字节后,进入接收中断回调函数
					2、将接收到的数据放入接收队列中
					3、重新使能串口接收中断	
*****************************************************************************/ 
extern “C”{
    extern UART_HandleTypeDef huart1;
    void UART0_IRQHandler(void)    //串口中断回调
    {
	    HAL_UART_IRQHandler( &huart1 );
    }

	void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
	{
		if(huart->Instance == UART1)
		{
			m_bRcvTime = TRUE;						//接收忙标志置1
			m_psQueueRcv->bAddItem(rec_buf);	    //将接收到的1字节数据添加到队列中
			HAL_UART_Receive_IT(huart, &rec_buf, 1);//重新使能串口接收中断
		}
	}
}

4、UART发送接收函数

        芯海提供的HAL库已经封装好了UART的发送接收函数,本例程中使用常规方法——UART中断接收,发送直接调用HAL_UART_Transmit();函数进行阻塞发送。

HAL_UART_Transmit(&huart1, &u8Item, 1, 0xFFFF);		// 发送1字节数据
HAL_UART_Receive_IT(&huart1, &rec_buf, 1);          // 启动接收中断

六、如果这篇文章能帮助到你,请点个赞鼓励一下吧ξ( ✿>◡❛)~

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐