目录

1.  简介

1.1  物理层

1.2  协议层

2.  UART

2.1  发送

2.2  接收

2.3  波特率配置

2.4  收发端口互换(TX/RX互换)

3.  代码编写

3.1  UART0引脚初始化

3.2  UART0外设参数配置

3.3  使能中断

3.4  中断服务函数

4.  进阶功能:printf重定向


1.  简介

        串口通讯 (Serial Communication) 是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息。

        在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设。

        对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。

1.1  物理层

        根据通讯使用的电平标准不同,串口通讯可分为 TTL 标准及 RS-232 标准,见表 TTL 电平标准与 RS-232 电平标准。

标准逻辑 1(高电平)逻辑 0(低电平)典型应用场景传输距离
TTL+3.3V ~ +5V0V单片机内部、板级通信短(厘米级)
RS-232-3V ~ -15V+3V ~ +15V工业控制、老式外设(如调制解调器)较长(可达 15 米)

        因为控制器一般采用 TTL 电平标准,因此需借助MAX3232 芯片实现TTL 电平与 RS-232 电平的双向转换,这是串口通讯中实现控制器与 RS-232 设备互联的关键环节。

1.2  协议层

        串口通讯的数据包由发送设备通过自身的TXD 接口传输到接收设备的RXD 接口。在协议层,数据包由起始位、主体数据、校验位和停止位组成,通讯双方必须约定一致的数据包格式,才能正常收发数据。

组成部分作用典型配置
起始位标志一个数据包的开始,使接收方同步接收数据固定为 1 位,逻辑电平为低电平
主体数据实际传输的有效信息,长度可变常见为 8 位(1 字节)
校验位用于检测传输过程中是否发生错误,可选奇校验、偶校验或无校验1 位或无
停止位标志一个数据包的结束,提供接收方的缓冲时间常见为 1 位或 2 位,逻辑电平为高电平

2.  UART

        通用异步收发传输器(Universal Asynchronous Receiver/Transmitter),通常称作 UART,是一种异步收发传输器。

        UART 主要特征如下:

  • 支持全双工工作
  • 支持单线半双工工作
  • 支持 8/9 位数据位
  • 支持 1/2 停止位
  • 支持奇 / 偶 / 无校验模式
  • 带 1 字节发送缓存
  • 带 1 字节接收缓存
  • 支持 LIN 模式 break character 收发
  • 支持空闲帧检测
  • 支持一主多从的 Multi-drop Slave/Master 模式

2.1  发送

        UART 包括一个字节发送缓冲区,当发送缓冲区有数据时,UART 将发送缓冲区的数据移入串行移位寄存器,并通过UART_TXD端口发送出去。只要UART发送缓冲区有数据,UART就会自动进行发送。 完成加载后,产生发送缓冲区空中断,此时,用户可以往发送缓冲区填入下一个需要发送的字节,这样,发送完成后,UART将加载这个字节进行发送。

        完成发送后,会产生发送完成中断。

2.2  接收

        UART 包括一个字节的接收缓冲区,当完成一个字节的接收后,会产生接收中断,并将接收到字节存储到接收缓冲区,用户应当在UART接收完成下一个字节前完成此字节的读取,否则缓冲区会被写入新接收的字节。接收部分内部使用高速时钟对 UART_RX 信号进行过采样来判定稳态的数据信号,防止噪声干扰造成错误接收。

2.3  波特率配置

        UART输入时钟为系统主时钟,波特率通过两级分频实现。

        波特率=UART模块时钟/(256*DIVH+DIVL+1)

        可以通过SYS_CLK_DIV2对UART模块时钟进行分频。

        UART模块时钟=系统主时钟/(1+SYS_CLK_DIV2)

        波特率配置实例如下:

UART 波特率SYS_CLK_DIV2UART_DIVHUART_DIVL
3000x00070x9C0x3F
6000x00030x9C0x3F
12000x00010x9C0x3F
24000x00000x9C0x3F
48000x00000x4E0x1F
96000x00000x270x0F
192000x00000x130x87
384000x00000x090xC3
430000x00000x080xB8
560000x00000x060xB1
576000x00000x060x82
1152000x00000x030x40

注意,同一波特率,其配置系数可能不唯一。

2.4  收发端口互换(TX/RX互换)

        UART模块支持Tx与Rx端口互换。通过将Tx对应的GPIO配置为输入使能,Rx对应的GPIO 配置为输出使能,即可实现Tx与Rx端口的互换。此时,GPIO第二功能仍选择为UART,UART本 身配置无需修改。

        此外,如果要使用一个GPIO同时作为Tx和Rx,需要将IO分时复用为输入或输出,对应Rx 或Tx,即可实现单口半双工逻辑。

3.  代码编写

        这里直接沿用上一章,移植好的代码:

【凌鸥LKS32】基础篇(三)·EXTI外部中断按键检测功能设计-CSDN博客

        找到带有UART功能的引脚,这里选择UART0也就是P1_0和P0_15引脚:

        为了方便查看宏定义一下:

#define UART0_RX_PIN    GPIO_Pin_15
#define UART0_RX_PORT   GPIO0
#define UART0_RX_Source GPIO_PinSource_15

#define UART0_TX_PIN    GPIO_Pin_0
#define UART0_TX_PORT   GPIO1
#define UART0_TX_Source GPIO_PinSource_0

3.1  UART0引脚初始化

        初始化P0.15和P1.0引脚,并将引脚复用为串口功能:

    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_StructInit(&GPIO_InitStruct); //初始化结构体

    //配置UART0_RXD  P0.15
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Pin = UART0_RX_PIN;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(UART0_RX_PORT, &GPIO_InitStruct);
	
    //配置UART0_TXD  P1.0
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Pin = UART0_TX_PIN;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(UART0_TX_PORT, &GPIO_InitStruct);

    GPIO_PinAFConfig(UART0_RX_PORT, UART0_RX_Source, AF4_UART);
    GPIO_PinAFConfig(UART0_TX_PORT, UART0_TX_Source, AF4_UART);	
	

        MCU 的每个引脚通常具有多种功能(GPIO、UART、SPI、TIM 等),这里通过 GPIO_PinAFConfig 将 P0.15 和 P1.0 的复用功能指定为 AF4_UART(第4类复用功能 = UART 串口),这样引脚在硬件层面就连接到 UART0 外设的内部信号线上了。

3.2  UART0外设参数配置

        和STM32差不多,8数据位,1停止位,无校验,使能串口收发中断:

    UART_InitTypeDef UART_InitStruct;

    UART_StructInit(&UART_InitStruct);
    UART_InitStruct.BaudRate = 38400;                                    /* 设置波特率38400 */
    UART_InitStruct.WordLength = UART_WORDLENGTH_8b;                     /* 发送数据长度8位 */
    UART_InitStruct.StopBits = UART_STOPBITS_1b;                         /* 停止位1位 */
    UART_InitStruct.FirstSend = UART_FIRSTSEND_LSB;                      /* 先发送LSB */
    UART_InitStruct.ParityMode = UART_Parity_NO;                         /* 无奇偶校验 */
    UART_InitStruct.IRQEna = UART_IRQEna_SendOver | UART_IRQEna_RcvOver; /* 串口中断使能 */
    UART_Init(UART0, &UART_InitStruct);

3.3  使能中断

        然后在硬件初始化部分,将初始化函数调用,并初始化UART0的中断:

void Hardware_init(void)
{
    __disable_irq();                  /* 关闭中断 中断总开关 */
    SYS_WR_PROTECT = 0x7a83;          /* 解除系统寄存器写保护 */
    FLASH_CFG |= 0x00080000;          /* enable prefetch ,FLASH预取加速使能*/
    IWDG_DISABLE();                   /* 关闭看门狗*/
    GPIO_init();                      /* GPIO初始化 */
    GPIO_init_key();                  /* KEY初始化 */
	uart0_init();
    SoftDelay(100);                   /* 延时等待硬件初始化稳定 */
    NVIC_SetPriority(GPIO_IRQn, 0);   /* 设置GPIO中断优先级为最高 */
    NVIC_EnableIRQ(GPIO_IRQn);        /* 使能GPIO中断 */
    NVIC_SetPriority(UART0_IRQn, 3);  /* 设置UART0中断优先级为3 */
    NVIC_EnableIRQ(UART0_IRQn);       /* 使能UART0中断 */
    __enable_irq();                   /* 开启总中断 */
}

3.4  中断服务函数

        完成功能实现当接收到数据后,将接受到的数据重新发送出来:

u8 Uart0_Buff = 0;

void UART0_IRQHandler(void)
{
	if (UART_GetIRQFlag(UART0, UART_IF_SendOver))    // 发送完成中断
	{
		UART_ClearIRQFlag(UART0, UART_IF_SendOver);  // 清除发送完成标志位
	}
	if (UART_GetIRQFlag(UART0, UART_IF_RcvOver))     // 接收完成中断
	{
		UART_ClearIRQFlag(UART0, UART_IF_RcvOver);	 // 清除接收完成标志位
		Uart0_Buff = UART_ReadData(UART0);			 // 接收 1 Byte数据
		UART_SendData(UART0, Uart0_Buff); 			 // 发送数据
	}
}

        验证一下:

完整工程:凌鸥LKS32MC071系列单片机·UART实现数据收发资源-CSDN下载

4.  进阶功能:printf重定向

        上面我们只是完成简单的数据收发,如果我们想要打印一部分数据的时候,可以使用printf进行数据打印,将下面代码复制到uart.c即可:

/**
 * fputc 重定向
 * 标准库的 printf/puts 等函数内部都会调用 fputc
 */
int fputc(int ch, FILE *f)
{
    uart_putchar((uint8_t)ch);
    return ch;
}

        然后找到图示位置,勾选上:

        来到按键部分,找到GPIO中断服务函数,每当按键按下,通过printf打印提示信息,流水灯方向反转了:

extern u8 flow_direction;

 void GPIO_IRQHandler(void)
{
    if(EXTI_GetIRQFlag(KEY_STOP_PORT,KEY_STOP_Source))
    {
        flow_direction = !flow_direction;                  				/* 切换流水灯方向 */
			
		printf("按键按下,流水灯方向反转:%d\r\n",flow_direction);  /* 打印流水灯方向 */
			
        EXTI_ClearIRQFlag(KEY_STOP_PORT,KEY_STOP_Source);
    }
}

        看一下效果:

完整工程:凌鸥LKS32MC071系列单片机·UART实现数据传输printf重定向操作资源-CSDN下载

凌鸥LKS32_时光の尘的博客-CSDN博客

更多推荐