STM32实现UART-CAN融合式高速串口
创始人
2024-06-02 20:46:34
0

STM32实现UART-CAN融合式高速串口

STM32的UART硬件电路,在进行线接传输时,一般低于230400bps的波特率,因为单端信号传输的特性,限制了传输距离和传输速度。而在同一块PCB板內进行短距离UART传输,则可以达到2Mbps及至4Mbps的传输速率,所以STM32的UART接口,能支持配置为2M或4M波特率。

如果要实现接线方式的串口信号传输距离加长,常用的方式为采用RS232电平转换芯片,将UART的TTL电平转换为RS232电平,但因为RS232标准依然是单端信号标准,所以也不能有效提高传输速率,一些RS232电平转换芯片也只是支持到最高230400bps的信号转换。常见的替代方式为采用RS485/RS422电平转换芯片替代RS232电平转换芯片,RS485/RS422为差分传输方式,能够支持高速远距离传输,但RS485/RS422方式为差分强驱方式,RS485芯片需要控制方向管脚,因此增加了直接和UART接口连接的复杂性。RS422标准并不包含方向控制管脚,市面上有很多用两路RS485集成的RS422功能芯片,也就有了方向控制管脚,RS485芯片卖得多,从而导致不含方向控制RS422销量小而贵一些。

实际上,如果不是传输距离特别长,采用UART-CAN融合式高速串口方式,是大部分系统更适合的方式。当采用半双工模式时,比RS485减少了方向控制管脚的使用,当采用点对点全双工模式时,相当于RS422。

这里的高速是指Mbps级传输速率,相比于Kbps级传输速率,而当和高速差分如LVTTL等比较时,则不能称为高速。

UART-CAN融合式借用CAN的物理层收发器,而不采用CAN的数据链路层协议和电路,这部分用UART收发电路替代。CAN物理层的驱动高电平态(隐性)由上拉实现,所以不是强驱型而是Open-drain型驱动,不会出现总线上电平冲突导致短路,这也是CAN在传统汽车总线里应用比较广的原因。

UART的TX,在不发送数据时输出高电平态, 而CAN的TX单端侧,也是高电平态,对应差分线上的隐性。

UART-CAN融合式高速串口连接方式

1)UART-CAN融合式半双工网络
在这里插入图片描述
这种网络连接方式适用于一个主机,其它为从机。主机发送信息给各个从机,各个从机判断是否是发给自己的,确认是发给自己的信息或命令,再根据信息或命令的要求,发送信息给主机。主机的STM32在发送信息时,不打开接收中断,而在发送信息后,则清除之前的接收中断标志后,打开UART接收中断,等待并接收来自从机的回复,同时还开启一个时间计数器,如果超时没有接收到从机回复,则做相应处理,如果收到从机回复,则关闭时间计数器。

这种网络在协议上可升级为Y令牌循环式协议,从而去除主机和从机关系,提高总线的利用率。每个节点有从0递增开始的节点地址(常通过MCU读取外部硬件多位拨码开关状态识别地址)。上电之后,识别自己为0地址节点的STM32,可以向总线发送信息,而在得到对应节点的回复,且自身目前没有业务处理,则向总线发出将令牌递交给地址+1的节点的信息,在得到回复后,完成令牌移交。得到令牌的节点,则也是可以占用总线发送接收信息,然后再将令牌递交给下一个节点,如果下一个节点没有在有效时间内回复,则向地址为0的节点移交令牌。按照此网络协议,则任何节点都可以主动向任何另一个节点发送信息和接收回复,从而打破了单个主机查询,多个从机被动回复的模式。在需要减少总线交互的场景(如低功耗),可以由0节点进行控制,当0节点获得令牌并无事可做时,可以延时设定的时间再将令牌交出。
在这里插入图片描述
Y令牌网络逻辑只有1个节点0为发起节点,除此特性外和其他节点作用一样。需要注意在物理连接上,CAN物理层差分连接仍然采用菊花链方式。

2)UART-CAN融合式全双工连接
在这里插入图片描述
全双工为点对点模式,STM32的每个UART对应2个CAN收发器,CAN收发器标称支持到1Mbps速率,实际上在同速度级芯片还有一定能力冗余,因此在此模式下,STM32的UART可以配置为1Mbps,2Mbps或 4Mbps,基于差分接线实际长度验证即可。

STM32 UART-CAN融合式高速串口测试范例

这里用STM32F103C6T6开发板,用两个UART连接两个CAN收发器模块,通过环回方式进行测试:
在这里插入图片描述

在这里插入图片描述
测试方案如下:
在这里插入图片描述

测试逻辑为UART1定时发送数据,UART2收到数据后通过USB虚拟串口转发到PC测试工具。测试时的UART速率为2Mbps。电路连接时需注意CAN收发器模块的TX和RX具体对应的方向。

STM32工程配置

这里以STM32CUBEIDE为开发环境,STM32F103C6T6为例进行工程配置和代码实现,首先建立基本工程并设置时钟:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置USB串口:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置UART1和UART2, UART1能支持到4Mbps, UART2只能支持到2Mbps,这里环回测试模式,也就设置为2Mbps:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

保存并生成基本工程代码:
在这里插入图片描述

STM32工程代码

编译时需要采用节省存储的编译方式,可参考: STM32 region `FLASH‘ overflowed by xxx bytes 问题解决
STM32虚拟串口的设置 ,可参考: STM32 USB VCOM和HID的区别,配置及Echo功能实现(HAL)
代码里用到的延时函数,可参考: STM32 HAL us delay(微秒延时)的指令延时实现方式及优化

设置USB虚拟串口收到数据时回发,此处用于接口测试:
在这里插入图片描述

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{/* USER CODE BEGIN 6 */CDC_Transmit_FS(Buf, *Len);USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);USBD_CDC_ReceivePacket(&hUsbDeviceFS);return (USBD_OK);/* USER CODE END 6 */
}

main.c文件里则设置UART2接收中断函数收到数据时,通过USB虚拟串口发送出去:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart=&huart2){CDC_Transmit_FS(&U2_RX, 1);HAL_UART_Receive_IT(&huart2, &U2_RX, 1);}}

main.c文件里则运行后启动UART2的接收中断,并定时从UART1发送数据:

  HAL_UART_Receive_IT(&huart2, &U2_RX, 1);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){PY_Delay_us_t(1000000);U1_TX = 0x55;HAL_UART_Transmit(&huart1, &U1_TX, 1, 2700);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}

完整的main.c文件如下:

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2023 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usb_device.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
__IO float usDelayBase;
void PY_usDelayTest(void)
{__IO uint32_t firstms, secondms;__IO uint32_t counter = 0;firstms = HAL_GetTick()+1;secondms = firstms+1;while(uwTick!=firstms) ;while(uwTick!=secondms) counter++;usDelayBase = ((float)counter)/1000;
}void PY_Delay_us_t(uint32_t Delay)
{__IO uint32_t delayReg;__IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);delayReg = 0;while(delayReg!=usNum) delayReg++;
}void PY_usDelayOptimize(void)
{__IO uint32_t firstms, secondms;__IO float coe = 1.0;firstms = HAL_GetTick();PY_Delay_us_t(1000000) ;secondms = HAL_GetTick();coe = ((float)1000)/(secondms-firstms);usDelayBase = coe*usDelayBase;
}void PY_Delay_us(uint32_t Delay)
{__IO uint32_t delayReg;__IO uint32_t msNum = Delay/1000;__IO uint32_t usNum = (uint32_t)((Delay%1000)*usDelayBase);if(msNum>0) HAL_Delay(msNum);delayReg = 0;while(delayReg!=usNum) delayReg++;
}
/* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;/* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t U1_TX;
uint8_t U1_RX;
uint8_t U2_TX;
uint8_t U2_RX;
/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_USART2_UART_Init();MX_USB_DEVICE_Init();/* USER CODE BEGIN 2 */PY_usDelayTest();PY_usDelayOptimize();HAL_UART_Receive_IT(&huart2, &U2_RX, 1);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){PY_Delay_us_t(1000000);U1_TX = 0x55;HAL_UART_Transmit(&huart1, &U1_TX, 1, 2700);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USB;PeriphClkInit.UsbClockSelection = RCC_USBCLKSOURCE_PLL_DIV1_5;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}
}/*** @brief USART1 Initialization Function* @param None* @retval None*/
static void MX_USART1_UART_Init(void)
{/* USER CODE BEGIN USART1_Init 0 *//* USER CODE END USART1_Init 0 *//* USER CODE BEGIN USART1_Init 1 *//* USER CODE END USART1_Init 1 */huart1.Instance = USART1;huart1.Init.BaudRate = 2000000;huart1.Init.WordLength = UART_WORDLENGTH_8B;huart1.Init.StopBits = UART_STOPBITS_1;huart1.Init.Parity = UART_PARITY_NONE;huart1.Init.Mode = UART_MODE_TX_RX;huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart1.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart1) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART1_Init 2 *//* USER CODE END USART1_Init 2 */}/*** @brief USART2 Initialization Function* @param None* @retval None*/
static void MX_USART2_UART_Init(void)
{/* USER CODE BEGIN USART2_Init 0 *//* USER CODE END USART2_Init 0 *//* USER CODE BEGIN USART2_Init 1 *//* USER CODE END USART2_Init 1 */huart2.Instance = USART2;huart2.Init.BaudRate = 2000000;huart2.Init.WordLength = UART_WORDLENGTH_8B;huart2.Init.StopBits = UART_STOPBITS_1;huart2.Init.Parity = UART_PARITY_NONE;huart2.Init.Mode = UART_MODE_TX_RX;huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;huart2.Init.OverSampling = UART_OVERSAMPLING_16;if (HAL_UART_Init(&huart2) != HAL_OK){Error_Handler();}/* USER CODE BEGIN USART2_Init 2 *//* USER CODE END USART2_Init 2 */}/*** @brief GPIO Initialization Function* @param None* @retval None*/
static void MX_GPIO_Init(void)
{/* GPIO Ports Clock Enable */__HAL_RCC_GPIOD_CLK_ENABLE();__HAL_RCC_GPIOA_CLK_ENABLE();}/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{if(huart=&huart2){CDC_Transmit_FS(&U2_RX, 1);HAL_UART_Receive_IT(&huart2, &U2_RX, 1);}}/* USER CODE END 4 *//*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

STM32代码测试

将STM32F103C6T6通过USB连接PC后,打开串口工具连接,这里的虚拟串口波特率随便设置,不是UART1和UART2的波特率。代码里UART1定时发送0x55, UART2收到后转发虚拟串口:
在这里插入图片描述

STM32例程下载

STM32F103C6T6 UART-CAN融合式高速串口例程

–End

相关内容

热门资讯

虞书欣谈成名的代价令人心疼,如... 虞书欣谈成名的代价令人心疼,如今她在圈内的前景如何?虞书欣在圈内前景是很不错的,大家都很喜欢她,也有...
最有效的减肥食谱 最有效的减肥食谱谁有最有效的减肥食谱呢?麻烦给一个吧?急求最有效的减肥食谱,听说有一个什么汤的可以很...
关晓彤鹿晗被曝月底会分手,你对... 关晓彤鹿晗被曝月底会分手,你对于他们两个的爱情有什么看法?当初我也觉得他们俩的感情肯定不会长久的,但...
她曾在家中喂猪,却被张艺谋相中... 她曾在家中喂猪,却被张艺谋相中一夜成名,魏敏芝如今过得怎样?魏敏芝过得非常不错啊,而且当地的头衔也是...
电影《中邪》主要讲了什么? 电影《中邪》主要讲了什么?中邪的剧情简介 · · · · · ·大学生丁鑫和刘梦为拍摄农村风俗纪录片...
可以在营业厅查到短信的内容吗? 可以在营业厅查到短信的内容吗?查自己的短信==如果你是本人应该可以…不是本人是不可以的那是别人的隐私...
春日时雨时晴,杏花开时,小雨落... 春日时雨时晴,杏花开时,小雨落在身上,衣服欲湿未湿;杨柳风最柔,吹到脸上也下觉其寒。是什么诗句沾衣欲...
低学历的女人真的不能娶吗? 低学历的女人真的不能娶吗?我觉得不是的,娶妻子还是主要看对方的品性如何,而不是在意对方的学历。不一定...
电影配音问题 懂的进来 不是很... 电影配音问题 懂的进来 不是很难我想知道一个问题 比如说 一个电影 演员是 X 那他的说...可以是...
关于大蒜的谜语有哪些 关于大蒜的谜语有哪些有关蒜的谜语有:两二小,头长草 (打字一)谜底:蒜弟兄七八个,围着柱子坐,只要一...
怎么夸老师漂亮 怎么夸老师漂亮老师您长得太有气质了,非常出众,我身为一个女孩子都快要把持不住了。就直接一点说:老师你...
一般怎么钢琴即兴伴奏,一拿到简... 一般怎么钢琴即兴伴奏,一拿到简谱就能伴奏的和弦 天空之城简易般的可一参考 摸摸就出来的 弹一个音 ...
我对异地女友说,异地太苦了我真... 我对异地女友说,异地太苦了我真心问你,你和我一起内心快乐吗?她说,不管日子再苦,有你就是甜的?你一个...
为什么叫镇江 为什么叫镇江为什么叫镇江意义为"Garrison of the Yangtze River"
在足球历史上,阿贾克斯都获得过... 在足球历史上,阿贾克斯都获得过多少次欧冠?一共应该是获得过4次冠军,而且他真的是特别厉害,很少有人可...
时间简史是谁写的? 时间简史是谁写的?史蒂芬·威廉·霍金
我爱她,但她爱他。 我爱她,但她爱他。如果换了我是你!我会一直的爱着她!她爱着他,你又爱着他!她知道爱一个不爱自己人的资...
歌词:我是你的月亮,是你夜里的... 歌词:我是你的月亮,是你夜里的光芒。叫月光曲,嘿嘿
枪神纪里面的英文歌曲叫什么? 枪神纪里面的英文歌曲叫什么?枪神纪里面的英文歌曲叫什么?一首是男的一首是女的,女的那首好像有一句是,...
中外小朋友 大家手拉手 来自五... 中外小朋友 大家手拉手 来自五大洲 是什么歌名?中外小朋友 大家手拉手 来自五大洲 是什么歌名?《庆...