硬件设备
STM32F103C8T6工控板、USB转TLL接口、TLL转CAN串口小板,继电器,降压模块,警示灯
软件工具
CANPro协议分析平台、CubeMX、MDK-ARM
实现过程及功能
基础功能:PC端通过CANPro设置报文,发出串口信号经过USB和串口小板转成CAN信号,在单片机上接收特定ID的报文,通过判断需要接收的首byte数据执行相应操作(LED亮)
进阶功能:通过修改CAN_Filter完成对两种报文的筛选,并设计一个特定报文,选择执行两种报文命令,通过连接继电器和警示灯,更直观的表现出来选择效果。
CubeMX的配置
定义引脚 根据板子的原理图来定义,这里CAN是PB8、PB9,LED是PB14、PB15
CAN的配置,最重要的是使得接收的波特率和发送的信号一致,至于怎么配置,可以查查别人的文章,我记得之前有看到过,我这里是因为后续使用在汽车上测试的,所以设置为500K
时钟的初始化,输入频率看板子上的晶振,在范围内选择。单独使用CAN的话,数值不太重要,高速时钟为72MHz,APB1外围时钟为最大频率即可。
配置完回去检查一下,随后工程管理设置一下,生成代码即可
代码
main.c
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "can.h"
#include "gpio.h"
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void SystemClock_Config(void);
CAN_HandleTypeDef hcan;
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();//引脚初始化函数
MX_CAN_Init();//CAN初始化函数
/* USER CODE BEGIN 2 *//
MY_CAN_Filter();//自定义滤波器设置函数
HAL_CAN_Start(&hcan);//调用HAL启动CAN
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
}
/* USER CODE END 3 */
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {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();
}
}
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
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */
can.c
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "can.h"
void MX_CAN_Init(void)
{
hcan.Instance = CAN1;
hcan.Init.Prescaler = 12;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.TimeSeg1 = CAN_BS1_3TQ;
hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = DISABLE;
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = DISABLE;
hcan.Init.ReceiveFifoLocked = DISABLE;
hcan.Init.TransmitFifoPriority = DISABLE;
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
Error_Handler();
}
}
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
/* CAN1 clock enable */
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**CAN GPIO Configuration
PB8 ------> CAN_RX
PB9 ------> CAN_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
__HAL_AFIO_REMAP_CAN1_2();
/* CAN1 interrupt Init *///这里也可以设置一下,在cubemx里NVIC
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);//没设置就直接添加
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
}
}
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
if(canHandle->Instance==CAN1)
{
/* Peripheral clock disable */
__HAL_RCC_CAN1_CLK_DISABLE();
/**CAN GPIO Configuration
PB8 ------> CAN_RX
PB9 ------> CAN_TX
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_8|GPIO_PIN_9);
/* CAN1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
}
}
/* USER CODE BEGIN 1 */
CAN_FilterTypeDef CAN_FilterStructure;//过滤器结构体
static CAN_RxHeaderTypeDef RxMessage;//接收的结构体
void MY_CAN_Filter(void)
{
uint32_t mask;
CAN_FilterStructure.FilterBank=0;
CAN_FilterStructure.FilterMode=CAN_FILTERMODE_IDMASK;
CAN_FilterStructure.FilterScale=CAN_FILTERSCALE_32BIT;
CAN_FilterStructure.FilterIdHigh=0x0018<<5;//CAN报文标准帧识别是11位,向左移5位,变为16位
CAN_FilterStructure.FilterIdLow=CAN_ID_STD;//我的需求是标准帧,扩展帧,远程帧就没设置了
mask = 0x0018;//用于筛选ID
mask =~mask;
mask<<=3;
mask |=0x02;
CAN_FilterStructure.FilterMaskIdHigh=(mask>>16)&0xffff;
CAN_FilterStructure.FilterMaskIdLow=mask&0xffff;//筛选模式可以设置,例如mask&0x0000
CAN_FilterStructure.FilterFIFOAssignment=CAN_FILTER_FIFO0;
CAN_FilterStructure.FilterActivation=CAN_FILTER_ENABLE;
CAN_FilterStructure.SlaveStartFilterBank=0;
HAL_CAN_ConfigFilter(&hcan,&CAN_FilterStructure);
HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
}
//接收回调函数
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
uint8_t data[8];//根据所需要发送的报文byte决定
RxMessage.DLC=8;
RxMessage.StdId=0x18;//接收报文的ID
RxMessage.RTR=CAN_ID_STD;//ID类型如上,标准帧,扩展帧等等可选
RxMessage.IDE=CAN_RTR_DATA;
if (HAL_CAN_GetState(hcan)!= RESET)
{
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxMessage, data);
if(data[0] == 0x00)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15,GPIO_PIN_SET);
}
}
}
/* USER CODE END 1 */
gpio.c
/* Includes ------------------------------------------------------------------*/
#include "gpio.h"
/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14|GPIO_PIN_15, GPIO_PIN_RESET);
/*Configure GPIO pins : PB14 PB15 */
GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 2 */
void GPIO_Init()
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);
}
/* USER CODE END 2 */
当然,也可以实现单片机之间的相互发送报文,再定义一下 CAN_RxHeaderTypeDef结构体即可。这里由于我们是PC端发报文,不多此一举了。在实际的应用中可以加入CAN_Debug,Receive_Debug等等
下一章:记录一下我对CAN总线/CAN协议/CAN报文的理解。
与君共勉!!!
进阶功能
记得配置输出引脚
修改的can.c部分一样的系统代码就不放了
/*Define filter function*/
void MY_CAN_Filter(void)
{
uint16_t mask,num,tmp,i;
uint16_t STDIDArray[3]={0x18,0x559,0x666};//
// /*0x599 Filter setting*/
CAN_FilterStructure.FilterBank=0;//Set filter 0,ranges from 0 to 13
CAN_FilterStructure.FilterMode=CAN_FILTERMODE_IDMASK;//Mask mode
CAN_FilterStructure.FilterScale=CAN_FILTERSCALE_32BIT;//The bit width is set to 32
CAN_FilterStructure.FilterIdHigh=STDIDArray[1]<<5;//Sets the identifier register height byte
CAN_FilterStructure.FilterIdLow=0;//The low byte is set to 0
mask=0xfff;//Start calculating the mask code,
num=sizeof(STDIDArray)/sizeof(STDIDArray[1]);
for(i=0;i<num;i++)//Mask the same or result of all members of the StdIdArray[] array
{
tmp = STDIDArray[i]^(~STDIDArray[1]);//All array members operate with the 0th member
mask &=tmp;
}
CAN_FilterStructure.FilterMaskIdHigh=(mask<<5);
CAN_FilterStructure.FilterMaskIdLow=0|0x02;//Only data frames are received
CAN_FilterStructure.FilterFIFOAssignment=CAN_FILTER_FIFO0;//Set the data frame that passes into FIFO0
CAN_FilterStructure.FilterActivation=CAN_FILTER_ENABLE;
HAL_CAN_ConfigFilter(&hcan,&CAN_FilterStructure);
HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO0_MSG_PENDING);
//
}
int flag=0;//Set flag bits to select execution
CAN_Receive can_receive;//Custom structure,Check in stm32f1xx_hal_can_h.//Note that modifying it in CUNBEMX causes the struct to be lost
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//Receive the callback function
{
if (HAL_CAN_GetState(hcan)!= RESET)
{
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &can_receive.RxMessage,can_receive.data); //Receive data in FIFO0
if(can_receive.RxMessage.StdId==0x666)//Determines whether to control the packet ID
{
if(can_receive.data[0]==0x10)//The first byte is 0x10, and 0x18 is assigned to flag
{flag =0x18;}
if(can_receive.data[0]==0x20)//The first byte is 0x20, and 0x18 is assigned to flag
{flag =0x599;}
}
/*The flag is 0x18 and the ID of the received packet is 0x18,Switch to single-node mode*/
if(flag==0x18)
{
if(can_receive.RxMessage.StdId==0x18)
{Single_node_model();}
}
/*The flag is 0x559 and the ID of the received packet is 0x559,Switch to BX1E mode*/
if(flag==0x599)
{
if(can_receive.RxMessage.StdId==0x559)
{BX1E();}
}
}
HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING);
}
/*Single node mode execution function */
void Single_node_model(void)
{
uint8_t TMP_Twobit;//Set the variable to take the first two bits of the second byte
TMP_Twobit=can_receive.data[1];
TMP_Twobit>>=6;
if(can_receive.data[0] == 0x19)//Currently ten bits are 100 in decimal
{
if(TMP_Twobit==0x00)
{//PB-LED Used for warning lamp faults
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//PB14-LEDl on
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);//PB15-LED2 off
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2 | GPIO_PIN_3,GPIO_PIN_RESET);//Yellow light
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4 | GPIO_PIN_1,GPIO_PIN_SET);
}
}
if(can_receive.data[0] == 0x19)//Currently ten bits are 101 in decimal
{
if(TMP_Twobit==0x01)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);//PB14-LEDl off
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);//green light
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_3| GPIO_PIN_4 ,GPIO_PIN_SET);
}
}
if(can_receive.data[0] == 0xe1)//Currently ten bits are 900 in decimal
{
if(TMP_Twobit==0x00)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//PB14-LED1 on
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3|GPIO_PIN_4,GPIO_PIN_RESET);// The buzzer sounded and red light
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 ,GPIO_PIN_SET);
}
}
}
/*BX1E mode execution function*/
void BX1E(void)
{
uint8_t TMP_FB;//Define a variable to take the first four bits of the first byte
TMP_FB=can_receive.data[0];
TMP_FB>>=4;
if(TMP_FB==0x0)//the first four bits are 0 in decimal
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET); //PB14-LED1 on
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET); //PB15-LED2 off
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2 | GPIO_PIN_3,GPIO_PIN_RESET);//Yellow light
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4 | GPIO_PIN_1,GPIO_PIN_SET);
}
if(TMP_FB==0x1)//the first four bits are 1 in decimal
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);//PB14-LED1 off
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);//Green light
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_3| GPIO_PIN_4 ,GPIO_PIN_SET);
}
if(TMP_FB==0x2)//the first four bits are 2 in decimal
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);//PB14-LED1 on
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);//PB15-LED2 on
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_3|GPIO_PIN_4,GPIO_PIN_RESET);//The buzzer sounded and red light
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 ,GPIO_PIN_SET);
}
}
/* USER CODE END 1 */
下面的结构体不定义也可以,定义了CAN_Receive,方便查找
typedef struct CAN_Receive
{
CAN_RxHeaderTypeDef RxMessage;
uint8_t data[8];//Received message
}CAN_Receive;
实际效果
0 0x00000666 1s 橙色
1 0x00000018 1s 黄色
2 0x00000018 1s 绿色
3 0x00000018 1s 红色+蜂鸣器
4 0x00000559 1s 红色+蜂鸣器
5 0x00000666 1s 红色+蜂鸣器
6 0x00000559 1s 红色+蜂鸣器
7 0x00000559 1s 绿色
8 0x00000559 1s 黄色文章来源:https://www.toymoban.com/news/detail-703058.html
不知道怎么上传视频,但是实际效果符合预期。文章来源地址https://www.toymoban.com/news/detail-703058.html
到了这里,关于CAN通讯配置及接收报文--学习笔记1~2的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!