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

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

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PegasusYu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值