首页 > 其他分享 >STM32之串口232通讯

STM32之串口232通讯

时间:2024-11-28 12:59:45浏览次数:10  
标签:HAL USART UART uint8 STM32 串口 GPIO rs232 232

STM32F407 系列文章 -RS232通讯(六)


文章目录

前言

一、串口(UART)

二、RS23-硬件特性

三、RS232-程序实现

1.函数rs232_init()

2.函数USART_UX_IRQHandler()

3.函数rs232_send_data()

4.函数rs232_receive_data()

5.函数rs232_receive_data()

总结


前言

一般STM32F407芯片都会自带好几路UART串口,但从407芯片端口输出的串口UART信号为TTL信号,这时我们就需要232驱动芯片,也可以说是电平转换芯片,将TTL信号转换为所需要的232信号,一般市场上所卖的板子都带这一功能的,因此要实现串口RS232通讯功能,需准备STM32F407开发板一块。

一、串口(UART)

常见的UART串口有RS232、RS485、RS422、TTL等,它们的区别在于通信协议、电平标准不同,我们今天主要讲解RS232的功能特性及实现方法。

二、RS23-硬件特性

RS232 是由美国电子工业协会(EIA)制定的串行数据通信接口 标准,使用两根线(一根发送,一根接收)进行全双工通信,传输速 率较低,传输距离有限。

MCU输出的是TTL电平,需要做电平转换,这里使用TPT3232E芯片来做232电平转换,该芯片官方推荐的设计原理图如下所示。其提供两路输入和输出,输入连接在 MCU 端,输出连接在 232 总线上。其中引脚ROUT1、TIN1作为一路输入,ROUT2、TIN2作为另一路输入,接在MCU的UART上;引脚TOUT1、RIN1 作为一 路输出,TOUT2、RIN2 作为另一路输出,接在外部232上;。

图中C1推荐值为0.2uF,C2、C3、C4推荐值为0.1uF。

三、RS232-程序实现

知道了RS232的硬件特性后,更好于我们进行程序设计。具体实现方式其实和TTL是一样的,和RS485有区别的是,485有特定的IO引脚来控制其发送和接收工作模式,这点要注意,其它是一样的。具体的本专栏里面有讲到和相应的代码软件。

1.函数rs232_init()

主要完成RS232设置,包括硬件初始化、引脚时钟使能、工作模式设置、参数初始化设置。中断设置等等,该函数被main()函数调用。代码如下(示例):

/* 引脚 和 串口 定义 
 * 默认是针对USART1的.
 * 注意: 通过修改这12个宏定义,可以支持USART1~UART7任意一个串口.
 */
#define USART_TX_GPIO_PORT              GPIOA
#define USART_TX_GPIO_PIN               GPIO_PIN_9
#define USART_TX_GPIO_AF                GPIO_AF7_USART1
#define USART_TX_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* 发送引脚时钟使能 */
#define USART_RX_GPIO_PORT              GPIOA
#define USART_RX_GPIO_PIN               GPIO_PIN_10
#define USART_RX_GPIO_AF                GPIO_AF7_USART1
#define USART_RX_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* 接收引脚时钟使能 */
#define USART_UX                        USART1
#define USART_UX_IRQn                   USART1_IRQn
#define USART_UX_IRQHandler             USART1_IRQHandler
#define USART_UX_CLK_ENABLE()           do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0)  /* USART1 时钟使能 */

/**
 * @brief       串口232初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @note        注意: 必须设置正确的时钟源, 否则串口波特率就会设置异常.
 *              这里的USART的时钟源在sys_stm32_clock_init()函数中已经设置过了.
 * @retval      无
 */
void rs232_init(uint32_t baudrate)
{
    g_rs232_handle.Instance = USART_UX;                      /* USART1 */
    g_rs232_handle.Init.BaudRate = baudrate;                 /* 波特率 */
    g_rs232_handle.Init.WordLength = UART_WORDLENGTH_8B;     /* 字长为8位数据格式 */
    g_rs232_handle.Init.StopBits = UART_STOPBITS_1;          /* 一个停止位 */
    g_rs232_handle.Init.Parity = UART_PARITY_NONE;           /* 无奇偶校验位 */
    g_rs232_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;     /* 无硬件流控 */
    g_rs232_handle.Init.Mode = UART_MODE_TX_RX;              /* 收发模式 */
    g_rs232_handle.Init.OverSampling = UART_OVERSAMPLING_16; /* 过采样 */
    HAL_UART_Init(&g_rs232_handle);                          /* HAL_UART_Init()会使能UART1 */
}

/**
 * @brief       UART底层初始化函数
 * @param       huart: UART句柄类型指针
 * @note        此函数会被HAL_UART_Init()调用
 *              完成时钟使能,引脚配置,中断配置
 * @retval      无
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;
    if(huart->Instance == USART_UX)                             /* 如果是串口1,进行串口1 MSP初始化 */
    {
        USART_TX_GPIO_CLK_ENABLE();                             /* 发送引脚时钟使能 */
        USART_RX_GPIO_CLK_ENABLE();                             /* 接收引脚时钟使能 */
        USART_UX_CLK_ENABLE();                                  /* USART1 时钟使能 */
        gpio_init_struct.Pin = USART_TX_GPIO_PIN;               /* TX引脚 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* 高速 */
        gpio_init_struct.Alternate = USART_TX_GPIO_AF;          /* 复用为USART1 */
        HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);   /* 初始化发送引脚 */

        gpio_init_struct.Pin = USART_RX_GPIO_PIN;               /* RX引脚 */
        HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   /* 初始化接收引脚 */
        __HAL_UART_DISABLE_IT(huart, UART_IT_TC);
        HAL_NVIC_EnableIRQ(USART_UX_IRQn);                      /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART_UX_IRQn, 3, 3);              /* 抢占优先级3,子优先级3 */
        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);              /* 使能UART1接收中断 */
        __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);              /* 使能UART总线空闲中断 */
    }
}

2.函数USART_UX_IRQHandler()

串口中断函数,主要完成接收外部232总线上的数据,在收到数据时,UART接收中断被触发生效,开始接收数据,直到UART空闲中断被触发,知道数据接收完毕,代码如下(示例):

static struct
{
    uint8_t buf[USART_REC_LEN];         /* 帧接收缓冲 */
    struct
    {
        uint16_t len    : 15;           /* 帧接收长度,sta[14:0] */
        uint16_t finsh  : 1;            /* 帧接收完成标志,sta[15] */
    }sta;                               /* 帧状态信息 */
}g_rs232_rx_buf = {0};                  /* UART1接收帧缓冲信息结构体 */

/**
 * @brief       串口232中断服务函数
 * @param       无
 * @retval      无
 */
void USART_UX_IRQHandler(void)
{
    uint8_t res;
    /* UART接收过载错误中断 */
    if (__HAL_UART_GET_FLAG(&g_rs232_handle, UART_FLAG_ORE) != RESET)
    {
        __HAL_UART_CLEAR_OREFLAG(&g_rs232_handle);           /* 清除接收过载错误中断标志*/
        (void)g_rs232_handle.Instance->SR;                   /* 先读SR寄存器再读DR寄存器*/
        (void)g_rs232_handle.Instance->DR;
    }
    /* UART接收数据中断 */
    if ((__HAL_UART_GET_FLAG(&g_rs232_handle, UART_FLAG_RXNE) != RESET))
    {
        HAL_UART_Receive(&g_rs232_handle, &res, 1, 1000);
        if (g_rs232_rx_buf.sta.len < USART_REC_LEN)          /* 缓冲区未满 */
        {
            g_rs232_rx_buf.buf[g_rs232_rx_buf.sta.len] = res;/* 记录接收到的值 */
            g_rs232_rx_buf.sta.len++;                        /* 接收数据增加1 */
        }
        else {
            g_rs232_rx_buf.sta.len = 0;                      /* 覆盖之前收到的数据 */
            g_rs232_rx_buf.buf[g_rs232_rx_buf.sta.len] = res;/* 将接收到的数据写入缓冲*/
            g_rs232_rx_buf.sta.len++; 
        }
    }
    /* UART总线空闲中断 */
    if (__HAL_UART_GET_FLAG(&g_rs232_handle, UART_FLAG_IDLE) != RESET)
    {
        g_rs232_rx_buf.sta.finsh = 1;                      /* 标记帧接收完成 */
        __HAL_UART_CLEAR_IDLEFLAG(&g_rs232_handle);        /* 清除UART总线空闲中断 */
    }
}

3.函数rs232_send_data()

串口发数函数,主要完成串口数据成功传送到外部232总线上,代码如下(示例):

/**
 * @brief   RS232发送len个字节
 * @param   buf : 发送区首地址
 * @param   len : 发送的字节数(为了和本代码的接收匹配,这里建议不要超过 USART_REC_LEN 个字节)
 * @retval  无
 */
uint8_t rs232_send_data(uint8_t *buf, uint8_t len)
{
	return HAL_UART_Transmit(&g_rs232_handle, buf, len, 1000);
}

4.函数rs232_receive_data()

串口接收函数,主要完成将UART接收中断上接收到的数据传递出去,并返回return 接收到数据长度,代码如下(示例):

/**
 * @brief       RS232查询接收到的数据
 * @param       buf     : 接收缓冲区首地址
 * @param       len     : 接收到的数据长度
 *   @arg               0   , 表示没有接收到任何数据
 *   @arg               其他, 表示接收到的数据长度
 * @retval      无
 */
uint8_t rs232_receive_data(uint8_t *buf)
{
    uint8_t len = 0;
	if(g_rs232_rx_buf.sta.finsh)
	{
		len = (uint8_t)g_rs232_rx_buf.sta.len;
		for (uint8_t i=0; i<len; i++)
			buf[i] = g_rs232_rx_buf.buf[i];
		g_rs232_rx_buf.sta.len     = 0;
		g_rs232_rx_buf.sta.finsh   = 0;
	}
	return len;
}

5.函数rs232_receive_data()

串口数据收发处理函数,该功能放在main()函数中被调用。做主机使用时,主要完成向从机发送请求指令,将数据投递到从机节点上;做从机使用时,接收来自主机的指令消息,并进行反馈;本文设置当主机使用,代码如下(示例):

/**
 * @brief       232串口数据收发处理函数
 * @param       无
 * @retval      无
 */
void RS232_Data_Process(void)
{
	static uint32_t LastDispatchTime = 0;			/* 最近一次的调度时间 */
	uint8_t sbuf[5] = {0};
	uint8_t rbuf[14] = {0};
	uint8_t rlen = 0;
	/* 232发送数据处理方式 */
	if(g_1msTick - LastDispatchTime > 500)    /* 500ms请求一次 */
	{
		sbuf[0] = 0x68;                        /* 标识符 */
		sbuf[1] = 0x04;                        /* 数据长度 从数据长度到校验和*/
		sbuf[2] = 0x00;                        /* 地址码 默认00 */
		sbuf[3] = 0x04;                        /* 命令字 */
		sbuf[4] = 0x08;                        /* 校验和 */
		uint8_t checksum = CRC_8_CHECKSUM(sbuf, 3);
		if (rs232_send_data(sbuf, 5) == HAL_OK)  /* 发送倾角传感器数据请求帧 */
			LastDispatchTime = g_1msTick;
		g_rs232_idle_flag = false;
	}
		
	/* 232接收数据处理方式 */
	rlen = rs232_receive_data(rbuf);
	if(rlen)
	{
		if(rlen != 14)
		{
			char *p = "232Len ERR!\r\n";
			CircleArray_Push(&g_UiUCycArray, (uint8_t*)p, sizeof("232Len ERR!\r\n"));
			return;
		}
		if(*(uint16_t*)&rbuf[0] != 0x0D68)
		{
			char *p = "232Head ERR!\r\n";
			CircleArray_Push(&g_UiUCycArray, (uint8_t*)p, sizeof("232Head ERR!\r\n"));
			return;
	    }
		uint8_t crc = CRC_8_CHECKSUM(&rbuf[1],12);
		if(crc != *(uint8_t*)&rbuf[13])
		{
			char *p = "232CRC ERR!\r\n";
			CircleArray_Push(&g_UiUCycArray, (uint8_t*)p, sizeof("232Head ERR!\r\n"));
			return;
		}
		/* 推送给Wife模块 */
		Wife_Message msg = {0};
		msg.Head = rbuf[0];
		msg.SN = g_wife_msg_sn++;
		msg.Time = g_1msTick;
		memcpy(msg.Data, &rbuf[4], 9);
		msg.MesCRC = CRC_8_CHECKSUM((uint8_t*)&msg.Head,WIFE_MSG_LEN-1);
		BlockCircleArray_Push(&g_PushWifeArray, (uint8_t*)&msg);
		CircleArray_Push(&FileArray, (uint8_t*)&msg, WIFE_MSG_LEN);
		/* 数据计算 */
		float X_Pos = (rbuf[4]&0x0f)*100 + (rbuf[5] >> 4)*10 + (rbuf[5]&0x0f) +                 
                      (rbuf[6]>>4)*0.1 + (rbuf[6]&0x0f)*0.01;
		float Y_Pos = (rbuf[7]&0x0f)*100 + (rbuf[8] >> 4)*10 + (rbuf[8]&0x0f) + 
                      (rbuf[9]>>4)*0.1 + (rbuf[9]&0x0f)*0.01;
		if(rbuf[4] >> 4)
			X_Pos *= -1;
		if(rbuf[7] >> 4)
			Y_Pos *= -1;
		/* 倾角锁定 */
		QJ_Lock_Out(Y_Pos);
		/* 推送到LCD队列 */
		UI_CycMessage m;
		m.id = UI_XJD_ID;
		sprintf(m.p, "%.2f", X_Pos);
		BlockCircleArray_Push(&g_PushLCDArray, (uint8_t*)&m);
		m.id = UI_YJD_ID;
		sprintf(m.p, "%.2f", Y_Pos);
		BlockCircleArray_Push(&g_PushLCDArray, (uint8_t*)&m);
		g_rs232_idle_flag = true;
	}
}

总结

RS232不管硬件还是软件,其实现的方式还是比较简单的,好入手,更适合初学者。下面提供的代码,基于STM32F407ZGT芯片编写,可直接在原子开发板上运行,也可运行在各工程项目上,但需要注意各接口以及相应的引脚应和原子开发板上保持一致。

相应的代码链接:代码程序

标签:HAL,USART,UART,uint8,STM32,串口,GPIO,rs232,232
From: https://blog.csdn.net/weixin_47006346/article/details/144103207

相关文章

  • STM32——GPIO
    目录一、基础知识      (一)基本特性                        数字和模拟信号                        IO端口基本结构      (二)引脚配置       多路复用——基于stm32f103       引......
  • 26 基于STM32的智能门禁系统(指纹、蓝牙、刷卡、OLED、电机)
    目录一、主要功能二、硬件资源三、程序编程四、实现现象一、主要功能基于STM32单片机,六个按键,分别代表指纹、蓝牙、刷卡的正确进门与错误进门;比如第一个按键按下,表示指纹正确,OLED显示指纹正确,开门,第二个按键按下的话,则指纹错误,OLED显示指纹错误,请重试,第二个按键连......
  • 20222322 2024-2025-1 《网络与系统攻防技术》实验五实验报告
    1.实验内容1.1实验要求(1)从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取相关信息。(2)尝试获取BBS、论坛、QQ、MSN中某一好友的IP地址,并查询获取该好友所在的具体地理位置。(3)使用nmap开源软件对靶机环境进行扫描,回答以下问题并给出操作命令。(4)使用Ne......
  • 20222326 2024-2025-1 《网络与系统攻防技术》实验五实验报告
    1.实验内容实验具体内容:一、从www.besti.edu.cn、baidu.com、sina.com.cn中选择一个DNS域名进行查询,获取如下信息:DNS注册人及联系方式该域名对应IP地址IP地址注册人及联系方式IP地址所在国家、城市和具体地理位置PS:使用whoisdignslookuptraceroute以及各类......
  • STM32和STM8开发工具、常用软件和开发环境汇总
    文章目录一、前言二、KeilC51软件三、KeilMDK-ARM四、STM32CubeMX及HAL库五、STM32CubeIDE六、STM8CubeMX七、STM32ST-LINKUtility一、前言整理一些常见的STM32/STM8开发所需要的安装包和工具。可以分别去官网下载最新的安装包。也可以通过关注【小康师兄】......
  • 代码随想录算法训练营第十天(LeetCode232.用栈实现队列;LeetCode225.用队列实现栈;LeetCo
    LeetCode232.用栈实现队列题目链接:用栈实现队列题目链接思路队列是先进先出,栈是先进后出,为了能够让栈可以模拟队列的先进先出,我们设置两个栈,一个栈作为入栈,一个栈作为出栈,我们在入栈存储完数据后,将入栈中的数据全部存储到出栈中,那么从出栈中弹出来的数据就是先进先出的......
  • STM32 cubeIDE 可执行文件 *.elf 的烧录
    1.准备工具:ST_LINKST-Link/V2 JTAG/SWD标准的接口排列:2.用杜邦线连接单片机3.开始烧录方法1:Ctrl+B编译项目成功后,右键项目名-Runas- STM32 C/C++Application方法2:Ctrl+B编译项目成功后,点击RunDebug按钮,直接烧录。当有多个程序时,默认烧写最近烧录过的程序,点击......
  • 除Keil和IAR之外的STM32开发环境
    3.STM32CubeIDESTM32CubeIDE是ST公司针对STM32推出的一款集成开发环境。 官网地址:https://www.st.com/stm32cubeide1.优点ST官方软件,对STM32开发很友好。基于Eclipse工具链,界面更现代化。免费、免费、免费。2.缺点仅限STM32,不支持其他厂家单片机。推出时间不长,存在一......
  • stm32 CRC32实现代码及软硬方式测试
    一、概叙:1、本文主要是针对常用的crc32的实现方式进行说明2、crc32的软件方式和硬件方式时间上的差异二、实现:1、软件实现代码1/***@brief软件crc32计算*@paramptr输入的32位数组*@paramlen32位数组的长度*@returnu32......
  • 关于STM32存储、内存分析(为何从0x8000起始)
    关于图上的几个问题(图从左往右看)1.为什么STM32的理论地址范围(寻址范围)为4G?STM32的地址总线为32位,32位可以理解为物理的引脚,32只引脚通过拉高拉低,也就是选择0或者1,32位2进制=2的32次方=4294967296组地址,一个地址对应一个字节,即:4294967296字节=4194304KB=4096MB=4GB地址总线......