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

news2025/6/16 1:45:56

RS485总线是一种常见的(Recommended Standard)串行总线标准(485是它的标识号),采用平衡发送与差分接收的方式,因此具有抑制共模干扰的能力。CAN是控制器局域网络(Controller Area Network, CAN)的简称,是一种能够实现分布式实时控制的串行通信网络,属于CSMA(多路载波侦听)/CD(冲突检测)+AMP(基于消息优先级的冲突检测)总线并具备错误检测能力。

CAN总线起初用于实现汽车内ECU(Electronic Control Unit)之间可靠的通信,后因其简单实用可靠等特点,而广泛应用于工业自动化、船舶、医疗等其它领域。串口是工业自动化系统中非常重要的通讯方式,在工业自动化通讯系统始终占据非常重要的地位。因为485具有通信距离远,易布线等优点,常用于工业自动化智能终端,后因其技术特点,大部分的仪器仪表以及众多一主多从通信架构的产品都采用了这种通信方式。

本篇文章介绍了使用STM32+MAX485实现485通信以及使用STM32+TJA1004实现CAN通信的方法,并补充说明了CAN通信采样点和负载率的公式计算说明。

文章目录

概念说明

实现原理

嵌入式程序 

485通信

CAN通信

CAN参数补充说明


概念说明

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


实现原理

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


嵌入式程序 

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

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

流程架构图如下:


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%,车厂会提相应要求。采样点位置如下图:
  • CAN速率与采样点配置:如上代码所示CAN速率是由几个时间段参数计算而成,ST提供了工具帮助我们生成针对不同速率与采样点位置的参数,软件截图如下:
  •  CAN总线负载率:总线负载率=总线每秒上传输的实际bit的总时间/1s *100%。原理非常简单,波特率的定义就是每秒CAN总线上可以传输多少CAN数据bit,总线负载率自然就是总线实际传输的bit数量比上总线可以承载的最大bit数了。CAN FD由于支持速率可变,总线占用时间的计算就稍微麻烦一些,需要分开计算:(\frac{bits_{datarate}*T_{data}+bits_{normalrate}*T_{normal}}{T})*100%

十六宿舍 原创作品,转载必须标注原文链接。

©2023 Yang Li. All rights reserved.

欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412088.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

推特爆火!揭晓大模型的未来何去何从

文 | 智商掉了一地巨大挑战 or 发展契机&#xff0c;ChatGPT 和 GPT-4 出现后&#xff0c;大模型的未来方向该何去何从&#xff1f;近期&#xff0c;自然语言处理领域的快速发展引起了广泛的关注&#xff0c;尤其是大型语言模型&#xff08;LLM&#xff09;的兴起已经推动了该领…

4.13、TCP通信流程

4.13、TCP通信流程1.TCP与UDP的区别&#xff08;传输层协议&#xff09;2.TCP通信流程①服务器端&#xff08;被动接受连接的角色&#xff09;②客户端&#xff08;主动发起连接&#xff09;1.TCP与UDP的区别&#xff08;传输层协议&#xff09; UDP:用户数据报协议&#xff0…

【Linux系统文件管理(cat,awk指令)和网络IP配置,广播地址,修改子网掩码以及ping网关地址】

文本操作实验 &#xff08;1&#xff09;使用cat&#xff0c;创建文件test1&#xff0c;输入"Line1"并且按下Ctrl-D保存文件。 创建text01.txt文件&#xff1a; cat > text01.txt 回车&#xff1b;输入自己想要输入的内容 键盘上面按下Ctrl-D就可以成功创建并保存…

【Java版oj】day24洗牌、MP3光标位置

目录 一、洗牌 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、MP3光标位置 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 一、洗牌 &#x…

西安石油大学C++上机实验 上机三:继承和派生程序设计(2学时)

上机三&#xff1a;继承和派生程序设计&#xff08;2学时&#xff09; 实验目的 了解继承在面向对象程序设计中的重要作用。 理解继承与派生的概念。 掌握通过继承派生出一个新类的方法。 掌握虚基类的作用和用法。 实验内容 P169&#xff1a;5.19, 5.22 上机内容 先设…

Windows 10 上使用 CMake GUI 编译 Krita 源代码并使用 MinGW 64 作为构建工具

krita系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文…

TCP和UDP(嵌入式学习)

TCP和UDPTCP&#xff08;即传输控制协议&#xff09;TCP连接建立(三次握手)TCP连接终止&#xff08;四次挥手&#xff09;UDP &#xff1a;用户数据报协议适用情况UDP 洪水UDP TCP 协议相同点TCP&#xff08;即传输控制协议&#xff09; 是一种面向连接的传输层协议&#xff0…

Vue ElementUI Axios 前后端案例(day01)

前言js 问js是什么&#xff0c;他有什么作用&#xff0c;与html和css的区别是什么 JavaScript&#xff08;简称JS&#xff09;是一种脚本语言&#xff0c;用于在网页上实现交互效果、动态效果和动态数据更新等功能。它是一种解释性语言&#xff0c;需要在浏览器中解释和执行。…

Linux中安装新版minio(centos7版本)

1. 背景需求 由于一些限制,在客户现场的Linux操作系统中,没有安装docker k8s等容器,无法直接使用镜像安装,而且客户要求只能在原始的操作系统中安装最新版的minio,(为什么需要安装最新版的minio,因为检测国网检测到之前版本的minio有漏洞,需要安装新版的minio). 2. 安装minio…

Direct3D 12——纹理——纹理缩小

纹理缩小(miniflcation )是纹理放大的逆运算。在缩小的过程中&#xff0c;大量纹素将被映射到少数纹理之 上 。例如&#xff0c;考虑下列情景&#xff1a;假设有一堵被256x256纹理所映射的墙壁&#xff0c;玩家的观察视角正紧盯着它&#xff0c;并 逐渐向后退却。在此过程中&am…

QML控件--Drawer

文章目录一、控件基本信息二、控件使用三、属性成员一、控件基本信息 Import Statement&#xff1a;import QtQuick.Controls 2.14 Since&#xff1a;Qt 5.7 Inherits&#xff1a;Popup 二、控件使用 Drawer&#xff1a;提供一个可以使用滑动手势打开和关闭的侧面板&#xff…

简述内网渗透中常用的隧道工具

frp 项目地址 https://github.com/fatedier/frp/ 简介 frp 是一个可用于内网穿透的高性能的反向代理应用&#xff0c;支持 tcp, udp 协议&#xff0c;为 http 和 https 应用协议提供了额外的能力&#xff0c;且尝试性支持了点对点穿透。 跨平台支持linux&#xff0c;win&a…

【cmake教程】find_path、find_library、find_program

目录 1、find_path 2、find_library 3、find_program find_path 参考文章&#xff1a;CMake中find_path的使用-CSDN博客 find_library 参考文章&#xff1a;CMake中find_library的使用 1、find_path find_path 一般用于在某个目录下查找一个或者多个头文件&#xff0c;命令…

ASP.NET Core - 依赖注入(一)

1. Ioc 与 DI Ioc 和DI 这两个词大家都应该比较熟悉&#xff0c;这两者已经在各种开发语言各种框架中普遍使用&#xff0c;成为框架中的一种基本设施了。 Ioc 是控制反转&#xff0c; Inversion of Control 的缩写&#xff0c;DI 是依赖注入&#xff0c;Inject Dependency 的…

WebSpider蓝蜘蛛网页抓取工具5.1用户手册

概述 关于网页抓取工具 本工具可以抓取互联网上的任何网页&#xff0c;包括需要登录后才能访问的页面。对抓取到页面内容进行解析&#xff0c;得到结构化的信息&#xff0c;比如&#xff1a;新闻标题、作者、来源、正文等。支持列表页的自动翻页抓取&#xff0c;支持正文页多页…

《Vue3实战》 第二章 创建项目和目录结构

1、创建项目 1.1、命令格式&#xff1a;vue create 项目名称 vue create vue3_example0011.2、运行项目 npm run serve1.2.1、增加run命令 启动时想修改命令&#xff0c;例如&#xff1a; npm run dev1、找到项目根路径下的package.json文件&#xff1b; 2、找到【scripts…

webgl-根据鼠标点击而移动

html <!DOCTYPE html> <head> <style> *{ margin: 0px; padding: 0px; } </style> </head> <body> <canvas id webgl> 您的浏览器不支持HTML5,请更换浏览器 </canvas> <script src"./main.js"></script&g…

DDoS攻击实验笔记

DoS&DDoS简介 DoS(Denial of Service)&#xff0c;拒绝服务攻击是通过一些方法影响服务的可用性&#xff0c;比如早期主要基于系统和应用程序的漏洞&#xff0c;只需要几个请求或数据包就能导致长时间的服务不可用&#xff0c;但易被入侵检测系统发现。 DDoS(Distributed D…

大数据Flink进阶(十八):Flink执行图和TaskSlot问题思考

文章目录 Flink执行图和TaskSlot问题思考 一、Flink执行图 二、TaskSlot问题思考 Flink执行图和TaskSlot问题思考 一、Flink执行图 Flink代码提交到集群执行时最终会被转换成task分布式的在各个节点上运行,在前面我们学习到DataFlow数据流图

【中级软件设计师】—操作系统考点总结篇(二)

【中级软件设计师】—操作系统考点总结篇&#xff08;二&#xff09; 1.操作系统概述 1.1操作系统的功能 1.2 特殊的操作系统 1.3 进程的概念和状态 进程与程序的区别&#xff1a; 进程是程序的一次执行过程&#xff0c;没有程序就没有进程 程序是一个静态的概念&#xff0c;…