1.往上找教程,下载并移植CANFestival源码。
2.需要自己编写canSend()、getElapsedTime()、setTimer()函数;
canSend() ,作用是发送can数据,最终canfestival库发送数据,都会调用canSend()函数.
getElapsedTime(),作用是软件定时器的修正(通过一个硬件定时器,来模拟多个软件定时器).
setTimer(),作用是改变硬件定时器的溢出周期.
3.第二部之前需要先对can进行参数配置、can初始化、can使能,这样调用canSend才会有作用。
也需要配置stm32定时器之后,getElapsedTime()、setTimer()才可以起作用,这样time硬件定时器才能为CANFestival提供一个时钟基准。
主要就是提前配置can底层和定时器底层。
1.can底层配置代码案例,.c文件如下:
#include "stm32f4xx.h" #include "canfestival_can.h" #include "canfestival.h" /* CANOPEN字典 */ extern CO_Data masterObjdict_Data; /* 功能: GPIO配置 参数: 无 返回值:无 */ static void gpio_config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能PORTA时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //100MHz GPIO_Speed_100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉 GPIO_Init(GPIOD, &GPIO_InitStructure); //初始化 GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_CAN1); //复用为CAN1 GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_CAN1); //复用为CAN1 } /* 功能: 中断嵌套控制器配置 参数: 无 返回值:无 */ static void nvic_config(void) { NVIC_InitTypeDef NVIC_InitStructure; /* 设置CAN接收中断,先占优先级2,从站优先级2 */ NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 初始化中断 */ NVIC_Init(&NVIC_InitStructure); } /* 功能: CAN总线过滤器配置 参数: 无 返回值:无 */ static void can_filter_config(void) { CAN_FilterInitTypeDef CAN_FilterInitStructure; /* 配置过滤器0组,配置成标准标识符且低7位都为0时接受 */ CAN_FilterInitStructure.CAN_FilterNumber = 0; /* 设置过滤器组0 */ CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; /* 屏蔽模式 */ CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; /* 32位模式 */ CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; /* 在CANOpen中标准标识符的低7位表示节点ID */ CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0004; /* 在CANOpen中只用标准标识符,数据帧/远程帧都有 */ CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; /* 主节点ID为0 */ CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; /* 标准帧 */ CAN_FilterInitStructure.CAN_FilterFIFOAssignment = CAN_Filter_FIFO0; /* 过滤器关联到FIFO0 */ CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; /* 使能过滤器 */ CAN_FilterInit(&CAN_FilterInitStructure); } /* 功能: can总线配置 参数: 无 返回值:无 */ void CANOpen_can_config(void) { CAN_InitTypeDef CAN_InitStructure; /* 配置IO */ gpio_config(); /* 中断嵌套控制器配置 */ nvic_config(); /* 配置CAN总线时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); /* CAN1默认参数 */ CAN_DeInit(CAN1); /* 将结构体填入默认参数 */ CAN_StructInit(&CAN_InitStructure); /* 关闭时间触发模式 */ CAN_InitStructure.CAN_TTCM = DISABLE; /* 关闭自动离线管理 */ CAN_InitStructure.CAN_ABOM = ENABLE; /* 关闭自动唤醒 */ CAN_InitStructure.CAN_AWUM = ENABLE; /* 自动重传 */ CAN_InitStructure.CAN_NART = DISABLE; /* 禁止FIFO溢出时覆盖原报文 */ CAN_InitStructure.CAN_RFLM = DISABLE; /* 关闭优先级取决于ID */ CAN_InitStructure.CAN_TXFP = DISABLE; /* 正常模式 */ CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; /* 设置波特率:36MHz/9/(2+1+1)=1mbps */ CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_2tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_1tq; CAN_InitStructure.CAN_Prescaler = 9; /* 初始化CAN总线 */ CAN_Init(CAN1, &CAN_InitStructure); /* CAN总线过滤器配置 */ can_filter_config(); /* 接收挂起中断 */ CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); } /* can总线接收中断回调函数 */ void CAN1_RX0_IRQHandler(void) { CanRxMsg message; Message Rx_Message; /* 接收消息 */ CAN_Receive(CAN1, CAN_FIFO0, &message); /* 组装canopen数据包 */ Rx_Message.cob_id = message.StdId; /* 功能码和节点ID */ Rx_Message.rtr = (message.RTR == CAN_RTR_DATA) ? 0 : 1; /* 标识符类型 */ Rx_Message.len = message.DLC; /* 数据包长度 */ memcpy(Rx_Message.data, message.Data, message.DLC); /* 数据 */ /* canopen数据包分配处理函数 */ canDispatch(&masterObjdict_Data, &Rx_Message); } /* 功能: CAN发送数据函数 参数: notused can总线端口 message canopen数据包 返回值: 0 成功 1 失败 */ uint8_t canSend(CAN_PORT notused, Message *message) { uint32_t i = 0xFFFFFF; CanTxMsg TxMessage; uint8_t TransmitMailbox = 0; /* 组装CAN数据包 */ TxMessage.DLC = message->len; /* 数据长度 */ memcpy(TxMessage.Data, message->data, message->len); /* 数据 */ TxMessage.IDE = CAN_ID_STD; /* 标准ID */ TxMessage.StdId = message->cob_id; /* 标识符 */ TxMessage.RTR = (message->rtr == CAN_RTR_DATA) ? 0 : 2; /* 数据帧 */ /* 发送数据包 */ TransmitMailbox = CAN_Transmit(CAN1, &TxMessage); /* 等待发送成功 */ while((CAN_TransmitStatus(CAN1, TransmitMailbox) != CANTXOK) && --i); /* 成功0 超时1 */ return (i != 0) ? 0 : 1; }
.h文件如下
#ifndef __canfestival_can_H_ #define __canfestival_can_H_ /* 功能: can总线配置 参数: 无 返回值:无 */ void CANOpen_can_config(void); #endif
定时器.c文件如下:
#include "canfestival_timer.h" // 下面函数是用于配制canfestival的唯一硬件定时器TIM14, // 所以这个函数要在主函数中进行调用,然后传入的形参也是TIM14 void TIMConfig(TIM_TypeDef* TIMx, uint16_t TIM_Period, uint16_t TIM_Prescaler) { TIM_TimeBaseInitTypeDef TIM_BaseInitStructure; //TIM_RepetitionCounter(TIM1_RCR)=0,每次向上溢出都产生更新事件 TIM_DeInit(TIMx);//初始化 TIM1寄存器 /*分频和周期计算公式: Prescaler = (TIMxCLK / TIMx counter clock) - 1; Period = (TIMx counter clock / TIM3 output clock) - 1 TIMx counter clock 为你所需要的 TXM 的定时器时钟*/ TIM_BaseInitStructure.TIM_Period = TIM_Period-1;//查数据手册可知,TIM1与 TIM8为16位自动装载 /*在 system_stm32f4xx.c 中设置的 APB2 Prescaler = 2 ,可知 *APB1时钟为168M/2*2,因为如果 APB1分频不为1,则定时时钟 x2*/ TIM_BaseInitStructure.TIM_Prescaler = TIM_Prescaler-1;//分频为10K TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数 TIM_BaseInitStructure.TIM_RepetitionCounter = 0x0; TIM_TimeBaseInit(TIMx, &TIM_BaseInitStructure); //清中断,以免一启用中断后立即产生中断 TIM_ClearFlag(TIMx, TIM_FLAG_Update); //使能TIM1中断源 TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE); //TIM1总开关:开启 TIM_Cmd(TIMx, ENABLE);//使能 TIM1定时器 } void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE); NVIC_InitStructure.NVIC_IRQChannel = TIM8_TRG_COM_TIM14_IRQn; //更新事件 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; //抢占优先级0 原始0 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应优先级1 原始1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //允许中断 NVIC_Init(&NVIC_InitStructure); //写入设置 } // canfestival 定时器 timer14定时器 void TIM8_TRG_COM_TIM14_IRQHandler(void) { if(TIM_GetITStatus(TIM14,TIM_IT_Update)!=RESET) { TIM_ClearITPendingBit(TIM14, TIM_IT_Update); //清中断 TimeDispatch(); // canfestival的库 } } void setTimer(TIMEVAL value) { TIM14->ARR = TIM14->CNT + value; } TIMEVAL getElapsedTime(void) { return TIM14->CNT; }
定时器.h文件如下:
#ifndef __canfestival_timer_H #define __canfestival_timer_H #include "sys.h" #include "stdlib.h" #include "stm32f4xx.h" #include "canfestival.h" void TIMConfig(TIM_TypeDef* TIMx, uint16_t TIM_Period, uint16_t TIM_Prescaler); void NVIC_Configuration(void); void setTimer(TIMEVAL value); TIMEVAL getElapsedTime(void); #endif
4.配置字典,生成.c和.h文件。
5.总体步骤如下:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】