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融合式高速串口例程文章来源:https://www.toymoban.com/news/detail-766738.html
–End文章来源地址https://www.toymoban.com/news/detail-766738.html
到了这里,关于STM32实现UART-CAN融合式高速串口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!