文章目录

概念说明

实现原理

嵌入式程序 

485通信

CAN通信

CAN参数补充说明


概念说明

  • 总线:总线(Bus)是一组能为多个部件分时共享的公共信息传送线路。分时和共享是总线的两个特点。计算机组成中总线分为地址总线、数据总线以及控制总线,他们挂接的计算机各功能部件(CPU,内存,显卡,硬盘),而我们文章提到的总线是各独立的智能硬件之间产品的串行通信方式,如果您能把多个产品(网关+n个终端)理解成一个智能产品系统,那么就不能理解为什么称这种通信方式为总线。
  • 差分信号:单端信号指的是用一个线传输的信号,以地为参考点,这样测量信号的精确值依赖系统内地的一致性,信号源和信号接收器距离越远,他们局部地的电压值之间有差异的可能性就越大。差分信号指的是用两根线传输信号,信号值是两个导体间的电压差,假如两条信号都收到同样的(同向、等幅度)的干扰信号,由于接收端是对接收的两条线信号进行减法处理,因此干扰信号会被基本抵消。
  • CSMA/CD:CSMA协议要求站点在发送数据之前先监听信道。如果信道空闲,站点就可以发送数据;如果信道忙,则站点不能发送数据。在早期的CSMA传输方式中,由于信道传播时延的存在,即使通信双方的站点,都没有侦听到载波信号,在发送数据时仍可能会发生冲突。如果发生冲突,信道上可以检测到超过发送站点本身发送的载波信号幅度的电磁波,由此判断出冲突的存在。一旦检测到冲突,发送站点就立即停止发送,并向总线上发一串阻塞信号,用以通知总线上通信的对方站点,快速地终止被破坏的帧,然后进行新一轮的总线竞争。
  • 收发器:收发器的主要功能是将CAN或UART控制器的TTL收发信号转换成CAN总线的单工差分信号,本例中485的收发器为MAX485,它的输入信号除了TX/RX,还有一个控制方向的引脚来控制当前芯片工作在收还是发模式,CAN的收发器为TJA1004。整体总线通信架构如下图:

STM32+收发器实现CAN和485总线-LMLPHP


实现原理

嵌入式程序跑在STM32平台上,正确配置UART以及CAN控制器的通信参数(速率帧格式等),正确驱动收发器(部分486收发器需要控制方向,CAN控制器有唤醒帧功能,需要进行正确操作)。然后嵌入式程序即可通过控制器进行数据的收发,下图是实现原理:

STM32+收发器实现CAN和485总线-LMLPHP


嵌入式程序 

485/CAN通信的嵌入式程序比较简单,基本分为两个部分:

  1. 控制器的初始化
  2. 收发器操作与数据的收发

流程架构图如下:

STM32+收发器实现CAN和485总线-LMLPHP


485通信

串口控制器的初始化包括:

  1. 开启串口控制器以及相应IO口时钟
  2. 初始化IO口以及配置串口中断
  3. 配置串口通信参数

下面是相关的接口代码(已经增加中文注释帮助理解),代码支持使用不同串口控制器来实现484通信:

//时钟初始化
void uart_RCC(USART_TypeDef* USARTx,uint32_t RCC_APB_Tx,uint32_t RCC_APB_Rx)
{
	//相对应串口时钟
	if(USARTx == USART1)
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	else if(USARTx == USART2)
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	else if(USARTx == USART3)
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	else if(USARTx == UART4)
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);	
	
	//相对串口的TxRx GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB_Tx|RCC_APB_Rx,ENABLE);
}
//GPIO初始化
void uart_GPIO(GPIO_TypeDef* TX_GpioPort,uint16_t TX_GpioPin,GPIO_TypeDef* RX_GpioPort,uint16_t RX_GpioPin,GPIO_TypeDef* Dir_GpioPort,uint16_t Dir_GpioPin)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	 //USARTx_TX
	GPIO_InitStructure.GPIO_Pin = TX_GpioPin;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽
	GPIO_Init(TX_GpioPort, &GPIO_InitStructure);
	
	//USARTx_RX
	GPIO_InitStructure.GPIO_Pin = RX_GpioPin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//浮空输入
	GPIO_Init(RX_GpioPort, &GPIO_InitStructure);

	//direction
	GPIO_InitStructure.GPIO_Pin = Dir_GpioPin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	GPIO_Init(Dir_GpioPort, &GPIO_InitStructure);
}
//串口中断配置
void uart_NVIC(uint8_t NVIC_IRQChannel,uint8_t NVIC_IRQChannelPreemptionPriority,uint8_t NVIC_IRQChannelSubPriority)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	//设置NVIC优先级分组为2
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	//串口接收中断打开 
	NVIC_InitStructure.NVIC_IRQChannel = NVIC_IRQChannel;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_IRQChannelPreemptionPriority;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = NVIC_IRQChannelSubPriority;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	
}
//
///华丽的分割线
///
//串口初始化
uint8 u8_drv_uart_Init(USART_TypeDef* USARTx)
{
	
	if(USARTx == USART1)
	{
		uart_RCC(USART1,D_UART1_TX_RCC,D_UART1_RX_RCC);	
	
		//重映射
		//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO,ENABLE);//开启端口B和复用时钟功能
		//GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);//使能端口重映射
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启端口B和复用时钟功能
		
		uart_GPIO(D_UART1_TX_PORT,D_UART1_TX_PIN,D_UART1_RX_PORT,D_UART1_RX_PIN);
		v_drv_uart_NVIC(USART1_IRQn,D_USART1_IRQn_PreemptionPriority,D_USART1_IRQn_SubPriority);
	}
	else if(USARTx == USART2)
	{
		uart_RCC(USART2,D_UART2_TX_RCC,D_UART2_RX_RCC);	
		uart_GPIO(D_UART2_TX_PORT,D_UART2_TX_PIN,D_UART2_RX_PORT,D_UART2_RX_PIN);
		v_drv_uart_NVIC(USART2_IRQn,D_USART2_IRQn_PreemptionPriority,D_USART2_IRQn_SubPriority);
	}
	else if(USARTx == USART3)
	{
		uart_RCC(USART3,D_UART3_TX_RCC,D_UART3_RX_RCC);	
		uart_GPIO(D_UART3_TX_PORT,D_UART3_TX_PIN,D_UART3_RX_PORT,D_UART3_RX_PIN);
		v_drv_uart_NVIC(USART3_IRQn,D_USART3_IRQn_PreemptionPriority,D_USART3_IRQn_SubPriority);
	}
	else if(USARTx == UART4)
	{
		uart_RCC(UART4,D_UART4_TX_RCC,D_UART4_RX_RCC);	
		uart_GPIO(D_UART4_TX_PORT,D_UART4_TX_PIN,D_UART4_RX_PORT,D_UART4_RX_PIN);
		uart_NVIC(UART4_IRQn,D_USART4_IRQn_PreemptionPriority,D_USART4_IRQn_SubPriority);
	}
	else
	{

	}
}
//串口发送
void uart_Send(USART_TypeDef* USARTx,char *pc_data,uint8_t u8_len)
{
	uint8_t i = 0;
	GPIO_SetBits(Dir_GpioPort, Dir_GpioPin);//收发器发送模式
	for(i=0;i<u8_len;i++)
	{
		USART_SendData(USARTx, *pc_data); 
		while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET){} 
		pc_data++;
	}
    GPIO_ResetBits(Dir_GpioPort, Dir_GpioPin);//收发器接收模式
}

下面是利用485总线发送“Hello world”以及接收中断函数,中断函数只提供了UART1,其余串口逻辑一致。代码如下:

//485通信初始化接口
uint8_t RS485_InitSerial(USART_TypeDef* USARTx,tsUart *psUart)
{
  u8_drv_uart_Init(USARTx);
  v_drv_uart_Configuration(USARTx, psUart->u32Uartbaud, psUart->u8WordLength, psUart->u8StopBits, psUart->u8Parity);
  uart_Send(USARTx,"Hello world",11);
  return 1;
}
//串口1的接收中断
void USART1_IRQHandler(void)
{
	unsigned char Temp=0;
	if(USART_GetFlagStatus(USART1, USART_FLAG_ORE) != RESET)  
	{   //OverRun Error interrupt
		USART_ReceiveData(USART1); 
	}   
	if(USART_GetFlagStatus(USART1, USART_FLAG_NE) != RESET) 
	{
        //Noise Error interrupt 
	}   
	if(USART_GetFlagStatus(USART1, USART_FLAG_FE) != RESET) 
	{
        //Framing Error interrupt 
	}   
	if(USART_GetFlagStatus(USART1, USART_FLAG_PE) != RESET) 
	{
        //Parity Error interrupt 
	}
	
	///
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
	{
		Temp=USART_ReceiveData(USART1);
        //添加处理字符的代码

		USART_ClearITPendingBit(USART1,USART_IT_RXNE);
	}
}

CAN通信

CAN控制器的初始化同样包括了中断,IO口初始化,以及发送帧的实现,下面是这部分代码,以提供了中文注释帮助大家理解:

//延时函数
void _NOP_(int x)
{
	int j=0;
	for(j=0;j<x;j++)
	{
	;	
	}
}
//中断初始化函数
static void CAN_NVIC_Configuration(uint32 canx)
{
		NVIC_InitTypeDef NVIC_InitStructure;

  	/* Configure the NVIC Preemption Priority Bits */  
  	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

	#ifdef  VECT_TAB_RAM  
	  /* Set the Vector Table base location at 0x20000000 */ 
	  NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); 
	#else  /* VECT_TAB_FLASH  */
	  /* Set the Vector Table base location at 0x08000000 */ 
	  NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);   
	#endif
	
	
	NVIC_InitStructure.NVIC_IRQChannel = can[canx].rx0_irq;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//0;//1;
	if (canx == 0)
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	else
		NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	NVIC_InitStructure.NVIC_IRQChannel = can[canx].sce_irq;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

#ifdef CAN_TX_INT_ENABLE	
	NVIC_InitStructure.NVIC_IRQChannel = can[canx].tx_irq;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
#endif
}
//CAN IO口初始化
tatic void CAN_PinInit(uint32 canx)
{
	GPIO_InitTypeDef  GPIO_InitStructure;

	/* Configure CAN pin: RX */
	RCC_APB2PeriphClockCmd(can[canx].gpio_rx.rccx | RCC_APB2Periph_AFIO, ENABLE);
	GPIO_InitStructure.GPIO_Pin = can[canx].gpio_rx.index;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(can[canx].gpio_rx.channel, &GPIO_InitStructure);
	
	/* Configure CAN pin: TX */
	RCC_APB2PeriphClockCmd(can[canx].gpio_tx.rccx | RCC_APB2Periph_AFIO, ENABLE);
	GPIO_InitStructure.GPIO_Pin = can[canx].gpio_tx.index;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//GPIO_Mode_AF_PP;//GPIO_Mode_Out_PP;
	GPIO_Init(can[canx].gpio_tx.channel, &GPIO_InitStructure);
	
	/* Configure CAN pin: STB */
	RCC_APB2PeriphClockCmd(can[canx].gpio_stb.rccx | RCC_APB2Periph_AFIO, ENABLE);
	GPIO_InitStructure.GPIO_Pin = can[canx].gpio_stb.index;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
	GPIO_Init(can[canx].gpio_stb.channel, &GPIO_InitStructure);
}
//CAN控制器配置
static int CAN_Configuration(uint32 canx)
{
	CAN_InitTypeDef        CAN_InitStructure;
    CAN_FilterInitTypeDef  CAN_FilterInitStructure;
	
    if(canx==0)
	{
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
	}
	else
	{
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);
	}
		
    // CAN register init 
    CAN_DeInit(can[canx].canx);
    CAN_StructInit(&CAN_InitStructure);

    // CAN cell init 
    CAN_InitStructure.CAN_TTCM=DISABLE;//禁止时间触发通信模式
    CAN_InitStructure.CAN_ABOM=ENABLE;
    CAN_InitStructure.CAN_AWUM=DISABLE;//睡眠模式通过清除sleep位唤醒
    CAN_InitStructure.CAN_NART=DISABLE;//报文自动重传
    CAN_InitStructure.CAN_RFLM=DISABLE;//接收溢出时,FIFO未锁定
    CAN_InitStructure.CAN_TXFP=DISABLE;//发送的优先级由标识符的大小决定
    CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;//正常模式
    
	//设置CAN的通信速率为50kbps,采样点百分之八十
    CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1=CAN_BS1_14tq;
    CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;
    CAN_InitStructure.CAN_Prescaler=4;
    if (CAN_Init(can[canx].canx, &CAN_InitStructure) == 0)
			return -1;

    // CAN filter init 
	if (canx == 0)
		CAN_FilterInitStructure.CAN_FilterNumber=0;
	else
		CAN_FilterInitStructure.CAN_FilterNumber=14;
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//CAN_FilterScale_16bit; //32bit
    CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
    CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;
    CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;   //时能过滤器
    CAN_FilterInit(&CAN_FilterInitStructure);

    CAN_ITConfig(can[canx].canx, CAN_IT_FMP0, ENABLE);
	CAN_ITConfig(can[canx].canx, CAN_IT_ERR, ENABLE);
#ifdef CAN_TX_INT_ENABLE		
	CAN_ITConfig(can[canx].canx, CAN_IT_TME, ENABLE);
#endif
		
	can[canx].state |= INIT_COMPLETE;
		
	return 0;
} 
//CAN数据帧实现
static unsigned char _CAN_SendData(uint32 canx, uint32 StdId, uint8 DLC, uint8 * Data)
{
        uint16 i;
	    uint16 count = 0;
	    CanTxMsg TxMessage;
        unsigned char TransmitMailbox;

		if((DLC-0)==0)
		{
			return 1;
		}
		
		TxMessage.StdId=StdId; 	//标准标识符
		TxMessage.ExtId=StdId; 
		TxMessage.RTR=CAN_RTR_DATA;
		TxMessage.IDE=CAN_ID_EXT/*CAN_ID_STD*/;
		TxMessage.DLC=DLC; 		//数据长度
		if(DLC>8)
		{
			TxMessage.Data[0]=0xFF;
		}
		else
		{
			for(i=0;i<DLC;i++)
			{
				TxMessage.Data[i]=Data[i];
			}
		}
#ifdef CAN_TX_INT_ENABLE
waiting:
		if (canx == 0) {
			if (sem_value_can1 > 0) {
				sem_value_can1--;
			} else {
				_NOP_(1);
				goto waiting;
			}
		} 
#ifdef STM32F10X_CL		
		else {
			if (sem_value_can2 > 0) {
				sem_value_can2--;
			} else {
				_NOP_(1);
				goto waiting;
			}
		}
#endif
		TransmitMailbox=CAN_Transmit(can[canx].canx,&TxMessage);
#else
		count = 0;
retry:		
		TransmitMailbox=CAN_Transmit(can[canx].canx,&TxMessage);
		if (TransmitMailbox == CAN_TxStatus_NoMailBox) 
		{
			count++;
			if (count > MAXTRYCOUNT)
			{	
				//can_init(canx);
				goto out;
			}
			goto retry;
		}
		
		i = 0xFFF;
		do {
			_NOP_(1);
		} while((CAN_TransmitStatus(can[canx].canx, TransmitMailbox) != CANTXOK) && (--i));
		
		count = 0;
		if (i <= 0x01)
		{
			count++;
			if (count > MAXTRYCOUNT)
			{	
				//can_init(canx);
				goto out;
			}
			goto retry;
		}
		//return 0;
		else
			return 1;
out:
		return 0;
#endif
}

下面的代码按定义参数正确配置了CAN控制器,并封装了供上层调用的发送接口,我还附上了CAN接收中断代码:

static uint8 u8CanSndId[2];
static struct can_info can[CAN_NUM] = {
	{
		RCC_APB1Periph_CAN1,

		19, 20, 21/*CAN1_TX_IRQn, CAN1_RX0_IRQn, CAN1_RX1_IRQn*/,

		22/*CAN1_SCE_IRQn*/, 
		{RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_11},
		{RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_12}, 

		{RCC_APB2Periph_GPIOA, GPIOA, GPIO_Pin_8},

		CAN1,
	},

	{
		RCC_APB1Periph_CAN2,
		63, 64, 65/*CAN2_TX_IRQn, CAN2_RX0_IRQn, CAN2_RX1_IRQn*/,
		66/*CAN2_SCE_IRQn*/,
		{RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_12},
		{RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_13},
		{RCC_APB2Periph_GPIOB, GPIOB, GPIO_Pin_11},
		CAN2,
	},

};//can通信参数

//CAN初始化
void can_init(uint32 canx)
{	
	canx -= 1;
	
	CAN_PinInit(canx);									//Configurate the GPIO
	CAN_NVIC_Configuration(canx);						//Configurate the NVIC
	CAN_Configuration(canx);
}
//CAN发送数据
unsigned char CAN_SendData(uint32 canx, uint16 addr, uint16 Length, uint8 *Data)
{
	unsigned int SendNumber=0;
	unsigned int i=0;
	unsigned int res=0;
	uint32 ExtId;
	SendNumber=Length/8;
	
	canx -= 1;
	//---------------------------
	u8CanSndId[canx]++;
	if(u8CanSndId[canx] >= 255)
	{
		u8CanSndId[canx] = 1;
	}
	//---------------------------
	
	ExtId =(0x10000000 | addr) + (u8CanSndId[canx]<<16);
	res=_CAN_SendData(canx, ExtId, 8, Data);
	if(res!=1)
	{
		return 0;
	}
		
	for(i=1; i<SendNumber; i++)
	{
		//---------------------------
		u8CanSndId[canx]++;
		if(u8CanSndId[canx] >= 255)
		{
			u8CanSndId[canx] = 1;
		}
		//---------------------------
		//ExtId = addr;
		ExtId = addr + (u8CanSndId[canx]<<16);
		res=_CAN_SendData(canx, ExtId,8,Data+8*i);
#ifndef CAN_TX_INT_ENABLE	
		if(res!=1)
		{
			return 8*i;
		}
#endif
	}
			
	//---------------------------
	u8CanSndId[canx]++;
	if(u8CanSndId[canx] >= 255)
	{
		u8CanSndId[canx] = 1;
	}
	//---------------------------
	//ExtId = addr;
	ExtId = addr + (u8CanSndId[canx]<<16);
	res=_CAN_SendData(canx, ExtId, Length-(SendNumber*8), Data+8*i);
#ifndef CAN_TX_INT_ENABLE	
	if(res!=1)
	{
		return 8*i;
	}
#endif
	return Length;
}
//CAN1接收中断
void CAN1_RX0_IRQHandler(void)
{
	uint8 i=0;
	uint8 u8QueueIndex = 0;
	uint16 u16Address = 0;
	uint32 u32CanRevId = 0;
	
	CanRxMsg RxMessage;
    CAN_Receive(CAN1,CAN_FIFO0, &RxMessage);

	s_app_can_paraD1.u8Mode = 1;
	
	s_app_can_paraD1.u32WaitReceiveFinishCount = sDeviceConfig.sCan1.u32WaitReceiveFinishCount;	
	
	//-------------------------------------------------------------
	u32CanRevId = RxMessage.ExtId;
	//-------------------------------------------------------------
	u8QueueIndex = u8_app_can_FindQueueIndexD1(u32CanRevId);
	if(u8QueueIndex == 0)
	{
		return;
	}	
	else if(u8QueueIndex>=CANDQUEUENUM-1)
	{
		return;
	}
	else if(u8QueueIndex == 0xff)
	{	
		return;
	}

	//-------------------------------------------------------------
	for(i=0;i<RxMessage.DLC;i++)
	{ 
		//CAN接收处理函数(RxMessage.Data[i],u8QueueIndex);
	}

	
	s_app_can_paraD1.u8Mode = 0;
}
	

CAN参数补充说明

  • CAN采样点:CAN发送的每一位数据(0或者1)维持的时间由由几段时间构成:同步段(SS),物理延时段(TSEG1),误差补偿段(TSEG2),实时同步补偿段(SJW)。采样点是接收机采样确定电平高低的位置,在TSEG1段之后,大部分情况为百分之80%,车厂会提相应要求。采样点位置如下图:STM32+收发器实现CAN和485总线-LMLPHP
  • CAN速率与采样点配置:如上代码所示CAN速率是由几个时间段参数计算而成,ST提供了工具帮助我们生成针对不同速率与采样点位置的参数,软件截图如下:STM32+收发器实现CAN和485总线-LMLPHP
  •  CAN总线负载率:总线负载率=总线每秒上传输的实际bit的总时间/1s *100%。原理非常简单,波特率的定义就是每秒CAN总线上可以传输多少CAN数据bit,总线负载率自然就是总线实际传输的bit数量比上总线可以承载的最大bit数了。CAN FD由于支持速率可变,总线占用时间的计算就稍微麻烦一些,需要分开计算:STM32+收发器实现CAN和485总线-LMLPHP

04-20 17:08