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

这篇具有很好参考价值的文章主要介绍了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融合式半双工网络
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
这种网络连接方式适用于一个主机,其它为从机。主机发送信息给各个从机,各个从机判断是否是发给自己的,确认是发给自己的信息或命令,再根据信息或命令的要求,发送信息给主机。主机的STM32在发送信息时,不打开接收中断,而在发送信息后,则清除之前的接收中断标志后,打开UART接收中断,等待并接收来自从机的回复,同时还开启一个时间计数器,如果超时没有接收到从机回复,则做相应处理,如果收到从机回复,则关闭时间计数器。

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

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

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

这里用STM32F103C6T6开发板,用两个UART连接两个CAN收发器模块,通过环回方式进行测试:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
测试方案如下:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

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

STM32工程配置

这里以STM32CUBEIDE为开发环境,STM32F103C6T6为例进行工程配置和代码实现,首先建立基本工程并设置时钟:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
设置USB串口:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
设置UART1和UART2, UART1能支持到4Mbps, UART2只能支持到2Mbps,这里环回测试模式,也就设置为2Mbps:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

保存并生成基本工程代码:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

STM32工程代码

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

设置USB虚拟串口收到数据时回发,此处用于接口测试:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

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收到后转发虚拟串口:
usart over can,STM32,stm32,高速串口,UART,CAN,UART-CAN

STM32例程下载

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

–End文章来源地址https://www.toymoban.com/news/detail-766738.html

到了这里,关于STM32实现UART-CAN融合式高速串口的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • stm32基于UART串口实现modbusRTU(软件方式)

     此程序中, 串口通信方式: 115200-n-8-1, modbus协议要求帧与帧之间的间隔必须大于3.5个字符时间间隙作为帧与帧之间的分割.  字符时间计算公式: interval_time = character_interval * 8 / baud_speed * 10 ^ 6 (微秒) 1.1 设置定时器, 超时时间为interval_time. 1.2 设置stm32的uart串口接收数据中断, 每

    2024年02月15日
    浏览(24)
  • STM32CubeMX教程9 USART/UART 异步通信

    开发板(正点原子stm32f407探索者开发板V2.4) STM32CubeMX软件(Version 6.10.0) keil µVision5 IDE(MDK-Arm) ST-LINK/V2驱动 逻辑分析仪nanoDLA 野火DAP仿真器 XCOM V2.6串口助手 使用STM32CubeMX软件配置STM32F407开发板 USART1与PC进行异步通信(阻塞传输方式、中断传输方式) ,具体为 使用WK_UP按键

    2024年02月03日
    浏览(53)
  • 基于STM32CubeMX和keil采用USART/UART实现非中断以及中断方式数据回环测试借助CH340以及XCOM

    这篇博客从串口通信的接口讲起,阐述原理,介绍通信方式,最后上机测试。 本篇博客主要以异步通信为例。 串口分为很多种,我们STM32学习过程中常见的就是UART/USART,前者是同步模式,后者是异步模式。还有RS485,RS232这种也是串口。我们平常使用的串口模块,大多都是类似

    2024年02月06日
    浏览(37)
  • STM32--USART串口

    通信接口 是指连接中央处理器(CPU)和标准通信子系统之间的接口,用于实现数据和控制信息在不同设备之间的传输和交换 。通信接口可以是硬件或软件实现,其目的是使不同设备之间能够进行有效地通信。 上图是常见的通用通信类型。 双工指的是接口能够实现双向数据传

    2024年02月11日
    浏览(28)
  • STM32单片机(九)USART串口----第一节:USART串口协议

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月16日
    浏览(35)
  • 【【STM32-USART串口协议】】

    USART串口协议 •通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统 •通信协议:制定通信的规则,通信双方按照协议规则进行数据收发 就是我们并不能在芯片上设计完全部的一下子完成所有的设计,我们总需要一些外设 所以需要学会通信接口 掌握通信协议来

    2024年02月12日
    浏览(31)
  • STM32——USART串口协议

    下图是51单片机串口通信的区分,跟stm32差不多,51单片机的串口通信的uart是只能异步通信,而stm32串口的usart是同步跟异步都可以选择,同步靠时钟线,异步靠比特率,比特率就是通信速度不一样,可以调成一样的。 (1).图一是串口通信的基本介绍。 (2).图二是串口通信的连线

    2024年02月03日
    浏览(28)
  • STM32单片机(九)USART串口----第三节:USART串口实战练习(串口发送)

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月10日
    浏览(33)
  • STM32单片机(九)USART串口----第四节:USART串口实战练习(串口发送+接收)

    ❤️ 专栏简介:本专栏记录了从零学习单片机的过程,其中包括51单片机和STM32单片机两部分;建议先学习51单片机,其是STM32等高级单片机的基础;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 :适用于想要从零基础开始学习入门单片机,且有一定C语言基础的的童鞋

    2024年02月10日
    浏览(34)
  • 【STM32】STM32学习笔记-USART串口协议(25)

    按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少量数据信号线(一般是8根以下), 地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式。而并行通讯一般是指使用8、16、32及64根或更多的数据线进行传输的通讯方式, 它们的通

    2024年01月19日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包