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

这篇具有很好参考价值的文章主要介绍了STM32+收发器实现CAN和485总线。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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+收发器实现CAN和485总线


实现原理

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

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


嵌入式程序 

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

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

流程架构图如下:

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


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

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

©2023 Yang Li. All rights reserved.

欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。文章来源地址https://www.toymoban.com/news/detail-446534.html

到了这里,关于STM32+收发器实现CAN和485总线的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • stm32f407单片机上通过HAL库实现can总线数据的收发

      最近在使用can总线,由于这个以前接触的比较少,所以调试代码的时候直接是下载的正点原子的例程,在这个基础上修改调试的。现在将调试中遇到的问题,总结一下,避免以后踩坑。目前写了一个查询方式的,一个中断方式的。项目代码下载地址: https://download.csdn.n

    2024年02月09日
    浏览(50)
  • FPGA-结合协议时序实现UART收发器(一):UART协议、架构规划、框图

    记录FPGA的UART学习笔记,以及一些细节处理,主要参考奇哥fpga学习资料。 本次UART主要采用计数器方法实现,实现uart的稳定性发送和接收功能,最后实现串口数据回环进行功能测试。 UART协议如图。 包含:空闲位、起始位、数据位、校验位、停止位、空闲位(一般没有) 对于

    2024年02月08日
    浏览(63)
  • UIOTOS前端零代码 第7节:(实践)利用嵌套+收发器组件,零代码实现简单计算器功能

    通过嵌套容器嵌套底层页面,再利用收发器组件和工具函数之间的转换,真正做到零代码实现简单计算器功能。 底层页面             步骤1: 打开编辑器,在右边页面中,选择合适的页面路径 新建页面 。 步骤2: 把当前页面命名“按钮”,并缩放至合适的大小,在右边属

    2024年04月14日
    浏览(47)
  • GT高速收发器

    GT高速收发器概述 10G以太网子系统通过AXI4-Stream接口提供10gb /s以太网MAC、物理编码子层(PCS)和物理介质附件(PMA)传输和接收功能。该子系统还提供了可选的高精度时间戳功能,兼容IEEE Std 1588-2008(也称为IEEE1588v2)。这适用于10GBASE-R标准。10G以太网MAC子系统框图如图所示。 Xilinx

    2024年02月05日
    浏览(39)
  • AD9361收发器中文手册

    因最近公司需要,借此机会和大家一起学习AD9361 制作不易,记得三连哦,给我动力,持续更新! 工程文件下载: 纯硬件SPI配置AD9361    提取码:g9jy ----------------------------------------------------------------------------------------         接收RF信号,并将其转换成可供BBP使用的数字数据

    2023年04月19日
    浏览(44)
  • 缓冲器/驱动器/收发器IC

    记录学习未使用过的IC,开发使用新的IC,哎,就是玩 本编文章主要介绍缓冲器/驱动器/收发器 FPGA或MCU低驱动能力引脚 单向长距离信号 1.SN74HCT245NSR DIR高电平,A到B可控制,B到A不可控制 DIR低电平,B到A可控制,A到B不可控制 OE高电平,所有通道端口高阻 OE低电平,所有通道端

    2024年02月09日
    浏览(48)
  • “GT/Serdes/高速收发器”相关的FPGA调研

    根据FPGA使用的要点,GT/Serdes/高速收发器这样的,进行检索,及FPGA的接口培训信息,整理成表如下: 序号 一级搜集 二级搜集 引申 1 知乎ID FPGA个人练习生 FPGA实现图像去雾 基于暗通道先验算法 纯verilog代码加速 提供2套工程源码和技术支持 没玩过GT资源都不好意思说自

    2024年02月08日
    浏览(41)
  • 基于vivado+Verilog FPGA开发 — GT收发器

    代码规范:Verilog 代码规范_verilog代码编写规范-CSDN博客 开发流程:FPGA基础知识----第二章 FPGA 开发流程_fpga 一个项目的整个流程-CSDN博客   源码下载:GitHub - Redamancy785/FPGA-Learning-Record: 项目博客:https://blog.csdn.net/weixin_51460407 零、低速通信接口的缺陷 1、同步通信要求传输数据

    2024年04月17日
    浏览(60)
  • FPGA-结合协议时序实现UART收发器(五):串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive

    串口顶层模块UART_TOP、例化PLL、UART_FIFO、uart_drive,功能实现。 对照代码,串口发送模块UART_TOP实现功能包括: PLL锁相环,实现稳定系统输入时钟功能 UART_FIFO,数据先进先出,实现数据缓存功能,防止出现数据错乱 w_clk_rst = ~w_system_pll_locked;保证复位电平是先高位再地位 r_use

    2024年02月08日
    浏览(74)
  • FPGA的高速收发器(GTX/GTY/GTP)的快速上手教程

            工作中有对GT收发器的使用需求, 学习的过程中,看手册,看别人的文章。有些大佬写得非常好,但他们可能不是针对使用来写的,我在实际使用IP核的过程中,还是会有很多疑惑。         所以我就针对怎么使用GTX等IP核写的这几篇文章,希望可以帮助到想快速学

    2024年04月15日
    浏览(54)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包