为什么在入门笔记第2篇就直接写can,而是跳过了GPIO、uart这些常用外设呢?
原因很简单,因为我最急着要用。
作为车载产品,can肯定是最先调通的。
而这一系列文章就是调通一个外设,总结一个。
而已。
先是熟悉一下demo的流程,在样例中找到一个can_pal_mpc5744p。
简单实测了一下,利用cantest下发0x2为ID的can报文,1 1 1 1 1 1 1 1,板上的灯会变化颜色。
看了一个代码的调用流程,OK没问题,走起来。
要注意是,要通过12V电源供电,usb供电是不够can使用的。(修改供电还需要改跳线帽)
代码中用的是Can0(PB0 、PB1)
双击 .pe文件,双击componets。
打开组件库,添加can_pal。
can_pal的参数就不修改了,使用默认的can0和500k。
然后修改pin_mux,把的引脚设置上。
生成代码
点击后会在/Generated_Code文件夹下生成对应的配置代码。其实就是一些数组和参数,在调用/SDK/platform里面的接口时会用到这些。
添加自己的用户代码,实现自己的功能。
为了方便显示,这里我将所有相关代码都写在了main.c中,并且没有封装接口,直接调用的都是pal库的接口。
以下实现了最简单的收发,当收到ID为0x2,且报文第一个字节是0x2时,会发送can报文出来。
#include "Cpu.h"
#include "typedefs.h"
Uint8 g_BootLoaderVer[4] = {19,11,28,1};
#define TX_MAILBOX (1UL)
#define RX_MAILBOX (0UL)
can_buff_config_t buffCfg = //Can缓冲区域配置
{
.enableFD = false,
.enableBRS = false,
.fdPadding = 0U,
.idType = CAN_MSG_ID_STD,
.isRemote = false
};
int main(void)
{
#ifdef PEX_RTOS_INIT
PEX_RTOS_INIT();
#endif
CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_FORCIBLE);
PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);
CAN_Init(&can_pal1_instance, &can_pal1_Config0);
/* Configure RX buffer with index RX_MAILBOX */
CAN_ConfigRxBuff(&can_pal1_instance, RX_MAILBOX, &buffCfg, 0x02); //只收
while(1)
{
can_message_t recvMsg;
CAN_Receive(&can_pal1_instance, RX_MAILBOX, &recvMsg);
if(recvMsg.id == 0x02 && recvMsg.data[0]== 2)
{
/* Configure TX buffer with index TX_MAILBOX*/
CAN_ConfigTxBuff(&can_pal1_instance, TX_MAILBOX, &buffCfg);
/* Prepare message to be sent */
can_message_t message = //Can消息配置
{
.cs = 0U,
.id = 0xaa,
.data[0] = g_BootLoaderVer[0],
.data[1] = g_BootLoaderVer[1],
.data[2] = g_BootLoaderVer[2],
.data[3] = g_BootLoaderVer[3],
.length = 4U
};
/* Send the information via CAN */
CAN_Send(&can_pal1_instance, TX_MAILBOX, &message);
while(CAN_GetTransferStatus(&can_pal1_instance, TX_MAILBOX) == STATUS_BUSY);
}
}
}
代码写的很简单,没有做任何封装,就是用系统函数 。
初始化时钟,初始化引脚,初始化Can。
CAN_Init。
配置接收和发送的mailbox
CAN_ConfigRxBuff,CAN_ConfigTxBuff。
然后收发即可。
CAN_Receive,CAN_Send。
比较值得一记录的就是
CAN_ConfigRxBuff和CAN_ConfigTxBuff这两个接口,稍微难懂一点。
CAN_ConfigRxBuff
我们先看下函数接口,
status_t CAN_ConfigRxBuff(const can_instance_t * const instance,
uint32_t buffIdx,
const can_buff_config_t *config,
uint32_t acceptedId)
里面做了什么?
根据初始化实例的类型是CAN_INST_TYPE_FLEXCAN还是CAN_INST_TYPE_MCAN来判断调用不同的接口。
用的比较多是FLEXCAN。
下一步调用
status = FLEXCAN_DRV_ConfigRxMb((uint8_t) instance->instIdx,
(uint8_t) buffIdx,
&dataInfo,
acceptedId);
在下一步继续调用FLEXCAN_SetRxMsgBuff。
status_t FLEXCAN_SetRxMsgBuff(
CAN_Type * base,
uint32_t msgBuffIdx,
const flexcan_msgbuff_code_status_t *cs,
uint32_t msgId)
把msgId拼接完放到flexcan_mb[1](FLEXCAN_GetMsgBuffRegion获取)中,其实就是RAMn寄存器。
使用CAN_ConfigTxBuff接口实现
can_buff_config_t g_CanBufferConfig = //Can buff 配置,目前接受和发送buffer都用这个配置
{
.enableFD = false,
.enableBRS = false,
.fdPadding = 0U,
.idType = CAN_MSG_ID_STD,
.isRemote = false
};
void CAN_Config()
{
CAN_ConfigRxBuff(&can_pal1_instance, RX_MAILBOX, &g_CanBufferConfig, 0x00); //只收
CAN_SetRxFilter(&can_pal1_instance,FLEXCAN_MSG_ID_STD, RX_MAILBOX,0x0);
CAN_ConfigTxBuff(&can_pal1_instance, TX_MAILBOX, &g_CanBufferConfig);
}
int main(void)
{
#ifdef PEX_RTOS_INIT
PEX_RTOS_INIT(); /* Initialization of the selected RTOS. Macro is defined by the RTOS component. */
#endif
CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_FORCIBLE);
PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr);
CAN_Init(&can_pal1_instance, &can_pal1_Config0);
CAN_Config();
while(1)
{
can_message_t recvMsg;
CAN_Receive(&can_pal1_instance, RX_MAILBOX, &recvMsg);
if(recvMsg.data[0]== 2)
{
CAN0_Send(recvMsg.id,g_BootLoaderVer,4);
}
}
}