STM32嵌入式基础开发附C-STM32-CAN通讯

1 博客内容

      博客内容基于STM32F103 RET6芯片(Keil.STM32F1xx_DFP.2.3.0.pack)+PCA82C250,使用CAN通讯环回模式测试CAN收发。CAN引脚对应PA11(RX)和PA12(TX),使用PB11电压测试数据是否成功。实际测试中PB11输出正常,但PA11和PA12异常,接示波器排查判断PCA82C250硬件故障。

2 STM32芯片、TJA1050、上位机

      STM32芯片引脚电平为3.3V,而CAN逻辑电压为0和2.5V。因此STM32芯片和CAN解析器(eg:周立功_USBCAN/Vector_CANape)之间需要收发器,将TTL电平转CAN逻辑电压。本文使用的收发器为PCA82C250,其中TJA1050和PCA82C250可以相互替换。
在这里插入图片描述

3 CAN环回模式、轮询/中断接收模式

      CAN环回模式用于CAN测试,对外发送同时自己接收。
在这里插入图片描述
      CAN接收理解两种模式:

  • 轮询接收模式。不论收件箱是否有邮件,都在不停收,直到收到为止。博客内容参考STM32官方帮助文件。文件位置:D:\Soft\Keil\Demo\en.stsw-stm32054\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\CAN\LoopBack。
    在这里插入图片描述

  • 中断接收模式。CAN接收FIFO(( First Input First Output))寄存器CAN_RF0R,到有消息到达,该寄存器的0和1位会累加(硬件执行),显示消息挂起。通过该位判断是否接收。
    在这里插入图片描述
    中断模式查看挂起消息数量(官方自带库函数):

uint8_t CAN_MessagePending(CAN_TypeDef* CANx, uint8_t FIFONumber)
{
  uint8_t message_pending=0;
  /* Check the parameters */
  assert_param(IS_CAN_ALL_PERIPH(CANx));
  assert_param(IS_CAN_FIFO(FIFONumber));
  if (FIFONumber == CAN_FIFO0)
  {
    message_pending = (uint8_t)(CANx->RF0R&(uint32_t)0x03);
  }
  else if (FIFONumber == CAN_FIFO1)
  {
    message_pending = (uint8_t)(CANx->RF1R&(uint32_t)0x03);
  }
  else
  {
    message_pending = 0;
  }
  return message_pending;
}

4 程序原理

      CAN对外发送(TX)标准ID为0x11的数据帧,长度为2个字节(内容为0xCA,0xFE)。对接收(RX)内容进行判断,如果全部正确,PB11高电平(实际该引脚连接接示波器,未连接LED)。

  if (RxMessage.StdId!=0x11)
  {
    return 0;  
  }

  if (RxMessage.IDE!=CAN_ID_STD)
  {
    return 0;
  }

  if (RxMessage.DLC!=2)
  {
    return 0;  
  }

  if ((RxMessage.Data[0]<<8|RxMessage.Data[1])!=0xCAFE)
  {
    return 0;
  }
  

5 主程序(Main.c)

//================================================
//    名称:  Main.c
//    作者:  Morven_X
//    版本:  1.1
//    编制:  2021/03/5 23:55
//    更新:  2021/03/08 20:37
//    功能:  基于STM32F103 RET6芯片,使用CAN发送和接收信息
//    简介:  使用回环模式查看CAN发送,使用USBCAN接收数据(失败)
//    Email:  morven_xie@163.com
//================================================


# include "stm32f10x.h"
# include "stm32f10x_gpio.h"
# include "stm32f10x_can.h"
# include "Delay.h" 
# include "CAN.h" 


int main(void)
{
  LED_Init();
  LED_PB_Init();
  Delay_Init();
  CAN_GPIO_Init();
  NVIC_Configuration();		
  JTAG_Init();
  
  /* Turns selected LED Off */  
  LED_Off_PB11;
  LED_Off_PB12;
  LED_Off_PB13;
  LED_Off_PB14;
  int i;
  int TestRx;
  while (1) 
  {
		
		 /* CAN transmit at 125Kb/s and receive by polling in loopback mode */
  TestRx = CAN1_Polling();

  if (TestRx == 0)
  {
    /* Faild to turn on led PB13 */
    LED_On_PB13;
  }
  else
  {
    /*Success to turn on PB11 */
    LED_On_PB11;
  }

    /*PC3-LED_ON */	
	LED_On;
	
	Delay_ms(100);

//	 while(i < 0xFFFFF)
//  {
//    i++;
//  }
	
	
	LED_Off_PB11;
    LED_Off_PB12;
	LED_Off;
	 
//	while(i >0x01)
//  {
//    i--;
//  }	
	Delay_ms(100);

  }
}

6 CAN程序(CAN.c)

# include "stm32f10x_rcc.h"
# include "stm32f10x_gpio.h"
# include "CAN.h"


void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);  //使能C口GPIO时钟
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;     //选择推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_3 | GPIO_Pin_6;						//指定引脚3
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;		//设置输出速率50MHz//
	GPIO_Init(GPIOC,&GPIO_InitStruct);							//初始化外设GPIOx寄存器
}

void LED_PB_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //使能B口GPIO时钟
	
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;     //选择推挽输出
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14;		//指定引脚
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;		//设置输出速率50MHz//
	GPIO_Init(GPIOB,&GPIO_InitStruct);							//初始化外设GPIOx寄存器
}


void CAN_GPIO_Init(void)
{
		//**********GPIO初始化**********//
	GPIO_InitTypeDef        GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);  //使能A口GPIO时钟
	
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_11 ;				  //指定引脚PA11,CAN_RX上拉输入
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;		//设置输出速率50MHz//
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;        //选择上拉输入
	GPIO_Init(GPIOA,&GPIO_InitStruct);							//初始化外设GPIOA寄存器

	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_12 ;				  //指定引脚PA12,CAN_TX复用推挽
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;		//设置输出速率50MHz//
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;     //选择推挽输出	
	GPIO_Init(GPIOA,&GPIO_InitStruct);							//初始化外设GPIOA寄存器
}
	



	/**
  * @brief  Configures the NVIC and Vector Table base address.
  * @param  None
  * @retval None
  */
void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;

  NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}


void JTAG_Init(void)
{	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);    //先开启开启AFIO复用时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);   //使能GPIOA外设时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);   //使能GPIOB外设时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//关闭JTAG,保留SWD(若同时关闭SWD,芯片作废)
}


int CAN1_Polling(void)
{	
  CAN_InitTypeDef        CAN_InitStructure;
  CAN_FilterInitTypeDef  CAN_FilterInitStructure;
  CanTxMsg TxMessage;
  CanRxMsg RxMessage;
  uint32_t i = 0;
  uint8_t TransmitMailbox = 0;

	
  /* CAN register init */
  CAN_DeInit(CAN1);

  CAN_StructInit(&CAN_InitStructure);
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);

  /* CAN cell init */
  CAN_InitStructure.CAN_TTCM=DISABLE;
  CAN_InitStructure.CAN_ABOM=DISABLE;
  CAN_InitStructure.CAN_AWUM=DISABLE;
  CAN_InitStructure.CAN_NART=DISABLE;
  CAN_InitStructure.CAN_RFLM=DISABLE;
  CAN_InitStructure.CAN_TXFP=DISABLE;
  CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;
  
  /* Baudrate = 125kbps*/
  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
  CAN_InitStructure.CAN_BS1=CAN_BS1_2tq;
  CAN_InitStructure.CAN_BS2=CAN_BS2_3tq;
  CAN_InitStructure.CAN_Prescaler=48;
  CAN_Init(CAN1, &CAN_InitStructure);

  /* CAN filter init */

  CAN_FilterInitStructure.CAN_FilterNumber=0;
  CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
  CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_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=0;


  CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;
  CAN_FilterInit(&CAN_FilterInitStructure);

  /* transmit */
  TxMessage.StdId=0x11;
  TxMessage.RTR=CAN_RTR_DATA;
  TxMessage.IDE=CAN_ID_STD;
  TxMessage.DLC=2;
  TxMessage.Data[0]=0xCA;
  TxMessage.Data[1]=0xFE;

  TransmitMailbox=CAN_Transmit(CAN1, &TxMessage);
  i = 0;
  while((CAN_TransmitStatus(CAN1, TransmitMailbox) != CANTXOK) && (i != 0xFFFF))
  {
    i++;
  }

  i = 0;
  while((CAN_MessagePending(CAN1, CAN_FIFO0) < 1) && (i != 0xFFFF))
  {
    i++;
  }
	
	
  /* receive */
  RxMessage.StdId=0x00;
  RxMessage.IDE=CAN_ID_STD;
  RxMessage.DLC=0;
  RxMessage.Data[0]=0x00;
  RxMessage.Data[1]=0x00;
  CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);

  if (RxMessage.StdId!=0x11)
  {
    return 0;  
  }

  if (RxMessage.IDE!=CAN_ID_STD)
  {
    return 0;
  }

  if (RxMessage.DLC!=2)
  {
    return 0;  
  }

  if ((RxMessage.Data[0]<<8|RxMessage.Data[1])!=0xCAFE)
  {
    return 0;
  }
  
  return 1; /* Test Passed */
}


7 CAN头文件(CAN.h)

#ifndef  _CAN_H
#define  _CAN_H

void LED_Init(void);
void LED_PB_Init(void);
void NVIC_Configuration(void);
void CAN_GPIO_Init(void);
void JTAG_Init(void);
int CAN1_Polling(void);

#define LED_On       GPIO_SetBits(GPIOC,GPIO_Pin_3) ;
#define LED_On_PB11  GPIO_SetBits(GPIOB,GPIO_Pin_11) ;
#define LED_On_PB12  GPIO_SetBits(GPIOB,GPIO_Pin_12) ;
#define LED_On_PB13  GPIO_SetBits(GPIOB,GPIO_Pin_13) ;
#define LED_On_PB14  GPIO_SetBits(GPIOB,GPIO_Pin_14) ;

#define LED_Off_PB11 GPIO_ResetBits(GPIOB,GPIO_Pin_11) ;
#define LED_Off_PB12 GPIO_ResetBits(GPIOB,GPIO_Pin_12) ;
#define LED_Off_PB13 GPIO_ResetBits(GPIOB,GPIO_Pin_13) ;
#define LED_Off_PB14 GPIO_ResetBits(GPIOB,GPIO_Pin_14) ;
#define LED_Off      GPIO_ResetBits(GPIOC,GPIO_Pin_3) ;

#endif


8 程序测试

      PB11和接地,与示波器连接:
在这里插入图片描述
      示波器显示结果_Delay(100ms):
在这里插入图片描述
      示波器显示结果_(i=0,i < 0xFFFFF,i++):
在这里插入图片描述
      增加82C250,其与电源连接。引脚连接RX和TX、CANH和CANL。检查4个电压:
在这里插入图片描述
      CAN收发器通过DB9接头接入USBCAN出现异常,连接示意:
在这里插入图片描述
      CANtest数据异常展示,基于以上判断转换器故障。
在这里插入图片描述

9 总结

      通过B站 刘洋:基础篇 19 STM32 CAN总线通讯实验
视频学习和STM32官方帮助文件学习(官方帮助文件ST STM32F10x_StdPeriph_Lib_V3.5.0),重码官方的STM32芯片的回环模式程序,了解CAN主控制函数(CAN_MCR)、唤醒方式、波特率等相关IO口和寄存器设置。虽然最终未能结合DBC查看数据,但实现初始目标。
      至此,嵌入式基础开发告一段落。

>> 更多相关内容,点击Morven_Xie博客概览

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值