【Avnet | NXP FRDM-MCXN947试用活动】N947的CAN 总线实现仪表
感谢能拿到NXP的新产品MCX系列,FRDM-MCXN947,这款板子性能还是很OK的。用过之前的LPC55S69,这个MCXN也算是加强版本了!今天就试试CAN triger GUI-Guider 数字仪表显示## 简介
在数字仪表显示使用的模拟的数据的方式triger 仪表盘组件进行显示,实车上这些数据的来源一般都是从CAN总线读取速度信息进行显示,仪表的ODO 的数据也会通过CAN总线上去给其他ECU节点使用,基于此我们在FRDM-N947板卡上实现CAN驱动对接到数字仪表,实现CAN总线的双向通信。
## CAN 驱动适配
### 硬件连接
**CAN硬件原理图,Can tx/rx 连接到P1_10/P1_11端口,Can 收发器不需额外引脚控制状态,默认工作状态**
!(https://www.eefocus.com/forum/data/attachment/forum/202412/04/170614n4hjqfx9xfhujqfj.png)
### 代码适配
##### PIN MUX 代码适配
配置P1_10/P1_11 引脚路由到CAN0 IP,对应代码修改点如下:
```
--- a/cluster/sdk/Core/board/pin_mux.c
+++ b/cluster/sdk/Core/board/pin_mux.c
@@ -196,6 +196,8 @@ void BOARD_InitLcdPins(void)
CLOCK_EnableClock(kCLOCK_Port2);
/* Enables the clock for PORT4: Enables clock */
CLOCK_EnableClock(kCLOCK_Port4);
+ /* Enables the clock for PORT1: Enables clock */
+ CLOCK_EnableClock(kCLOCK_Port1);
gpio_pin_config_t gpio0_pinC13_config = {
.pinDirection = kGPIO_DigitalOutput,
@@ -806,6 +808,52 @@ void BOARD_InitLcdPins(void)
kPORT_UnlockRegister};
/* PORT4_7 (pin T4) is configured as PIO4_7 */
PORT_SetPinConfig(PORT4, 7U, &port4_7_pinT4_config);
+
+ const port_pin_config_t port1_10_pinC3_config = {/* Internal pull-up/down resistor is disabled */
+ kPORT_PullDisable,
+ /* Low internal pull resistor value is selected. */
+ kPORT_LowPullResistor,
+ /* Fast slew rate is configured */
+ kPORT_FastSlewRate,
+ /* Passive input filter is disabled */
+ kPORT_PassiveFilterDisable,
+ /* Open drain output is disabled */
+ kPORT_OpenDrainDisable,
+ /* Low drive strength is configured */
+ kPORT_LowDriveStrength,
+ /* Pin is configured as CAN0_TXD */
+ kPORT_MuxAlt11,
+ /* Digital input enabled */
+ kPORT_InputBufferEnable,
+ /* Digital input is not inverted */
+ kPORT_InputNormal,
+ /* Pin Control Register fields are not locked */
+ kPORT_UnlockRegister};
+ /* PORT1_10 (pin C3) is configured as CAN0_TXD */
+ PORT_SetPinConfig(PORT1, 10U, &port1_10_pinC3_config);
+
+ const port_pin_config_t port1_11_pinD3_config = {/* Internal pull-up/down resistor is disabled */
+ kPORT_PullDisable,
+ /* Low internal pull resistor value is selected. */
+ kPORT_LowPullResistor,
+ /* Fast slew rate is configured */
+ kPORT_FastSlewRate,
+ /* Passive input filter is disabled */
+ kPORT_PassiveFilterDisable,
+ /* Open drain output is disabled */
+ kPORT_OpenDrainDisable,
+ /* Low drive strength is configured */
+ kPORT_LowDriveStrength,
+ /* Pin is configured as CAN0_RXD */
+ kPORT_MuxAlt11,
+ /* Digital input enabled */
+ kPORT_InputBufferEnable,
+ /* Digital input is not inverted */
+ kPORT_InputNormal,
+ /* Pin Control Register fields are not locked */
+ kPORT_UnlockRegister};
+ /* PORT1_11 (pin D3) is configured as CAN0_RXD */
+ PORT_SetPinConfig(PORT1, 11U, &port1_11_pinD3_config);
}
/***********************************************************************************************************************
* EOF
```
##### Clock Config配置
因系统时钟使用的是pll0,这块选择CAN0的时钟源为pll0,并设置分频系数为5,对应CA0 IP 时钟源配置为150M/5=30M,对应代码修改如下:
```
--- a/cluster/sdk/Core/source/lvgl_guider.c
+++ b/cluster/sdk/Core/source/lvgl_guider.c
@@ -187,6 +187,11 @@ int main(void)
CLOCK_SetClkDiv(kCLOCK_DivFlexioClk, 1u);
CLOCK_AttachClk(kPLL0_to_FLEXIO);
+
+ /* attach can0 to pll0 */
+ CLOCK_SetClkDiv(kCLOCK_DivFlexcan0Clk, 5U);
+ CLOCK_AttachClk(kPLL0_to_FLEXCAN0);
+
```
#### CAN外设初始化
CAN0 配置为500K 速率,MB1 配置为发送用于发送0x220 CAN消息,用于发送仪表的ODO数据至CAN总线,配置MB2 配置为接收消息maixbox 用于接收0x110 CAN消息。对应配置代码如下:
```
void can_init(void)
{
-
+ flexcan_config_t flexcanConfig;
+ flexcan_timing_config_t timing_config;
+ flexcan_rx_mb_config_t mbConfig;
+
+ /* Get FlexCAN module default Configuration. */
+ /*
+ * flexcanConfig.clkSrc = kFLEXCAN_ClkSrc0;
+ * flexcanConfig.bitRate = 1000000U;
+ * flexcanConfig.bitRateFD = 2000000U;
+ * flexcanConfig.maxMbNum = 16;
+ * flexcanConfig.enableLoopBack = false;
+ * flexcanConfig.enableSelfWakeup = false;
+ * flexcanConfig.enableIndividMask = false;
+ * flexcanConfig.disableSelfReception = false;
+ * flexcanConfig.enableListenOnlyMode = false;
+ * flexcanConfig.enableDoze = false;
+ */
+ FLEXCAN_GetDefaultConfig(&flexcanConfig);
+ flexcanConfig.bitRate = 500000U;
+
+ memset(&timing_config, 0, sizeof(flexcan_timing_config_t));
+ if (FLEXCAN_CalculateImprovedTimingValues(CAN0, flexcanConfig.bitRate,
+ CLOCK_GetFlexcanClkFreq(0U), &timing_config))
+ {
+ /* Update the improved timing configuration*/
+ memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
+ }
+ else
+ {
+ PRINTF("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
+ }
+
+ FLEXCAN_Init(CAN0, &flexcanConfig, CLOCK_GetFlexcanClkFreq(0U));
+
+ /* Create FlexCAN handle structure and set call back function. */
+ FLEXCAN_TransferCreateHandle(CAN0, &flexcanHandle, NULL, NULL);
+
+ /* Set Rx Masking mechanism. */
+ FLEXCAN_SetTxMbConfig(CAN0, 1, true);
+
+ frame_0x220.id = FLEXCAN_ID_STD(0x220);
+ frame_0x220.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
+ frame_0x220.type = (uint8_t)kFLEXCAN_FrameTypeData;
+ frame_0x220.length = (uint8_t)8;
+
+ txXfer.mbIdx = (uint8_t)(1);
+ txXfer.frame = &frame_0x220;
+
+ /* Set Tx Masking mechanism.g */
+ /* Setup Rx Message Buffer. */
+ mbConfig.format = kFLEXCAN_FrameFormatStandard;
+ mbConfig.type = kFLEXCAN_FrameTypeData;
+ mbConfig.id = FLEXCAN_ID_STD(0x110);
+
+ /* Setup Rx individual ID mask. */
+ FLEXCAN_SetRxMbConfig(CAN0, 2 , &mbConfig, true);
+
}
```
### 创建CAN TX/RX 任务用于手法0X220/0X110 消息
对应can 任务代码如下:
```
void can_txtask_entry(void *pvParameters)
{
+ can_init();
+
while(1)
{
vTaskDelay(100);
+ FLEXCAN_TransferSendBlocking(CAN0,1 , &frame_0x220);
}
}
+
+
+void can_rxtask_entry(void *pvParameters)
+{
+ can_init();
+
+ while(1)
+ {
+ vTaskDelay(10);
+ FLEXCAN_TransferReceiveBlocking(CAN0,2,&frame_rx);
+ //PRINTF("[%x]%x %x %x %x %x %x %x %x\r\n",frame_rx.id >> 18,frame_rx.dataByte0,frame_rx.dataByte1,frame_rx.dataByte2,frame_rx.dataByte3,\
+ // frame_rx.dataByte4,frame_rx.dataByte5,frame_rx.dataByte6,frame_rx.dataByte7);
+ if(frame_rx.id >> 18 == 0x110)
+ {
+ decode_frame_0x110_speed(&frame_rx);
+ }
+ }
+}
```
### 添加更新ODO 及 获取速度结构
添加上述接口便于GUI 的仪表盘显示刷新回调函数对接用于发送ODO 数据及根据CAN 上的消息刷新显示,0x220 CAN 消息的byte0-byte3携带ODO数据,0x110的byte4-byte7为车速信息。对应函数如下:
```
+flexcan_handle_t flexcanHandle;
+flexcan_frame_t frame_0x220;
+flexcan_frame_t frame_rx;
+flexcan_mb_transfer_t txXfer;
+
+void update_frame_0x220_odo(int odo)
+{
+ frame_0x220.dataByte0 = odo&0xff;
+ frame_0x220.dataByte1 = (odo>>8)&0xff;
+ frame_0x220.dataByte2 = (odo>>16)&0xff;
+ frame_0x220.dataByte3 = (odo>>24)&0xff;
+}
+
+static int speed1 = 0;
+
+int get_speed(void)
+{
+ return speed1;
+}
+
+void decode_frame_0x110_speed(flexcan_frame_t * frame)
+{
+ int speed = 0;
+ speed = (frame->dataByte7) | (frame->dataByte6 << 8 )| (frame->dataByte5 << 16) | (frame->dataByte4 << 24);
+
+ speed1 = speed%200;
+
+ if((speed/200)%2 == 1)
+ speed1 = 200 - speed%200;
+
+}
```
### 对接数字仪表显示刷新处理
回调函数滴调用get_speed、update_frame_0x220_odo 用于更新CAN 消息及刷新显示,对应修改如下:
```
diff --git a/cluster/custom/custom.c b/cluster/custom/custom.c
index be8b0d8..0a2e8d7 100644
--- a/cluster/custom/custom.c
+++ b/cluster/custom/custom.c
@@ -38,6 +38,9 @@ static float trip = 12.4;
static int32_t ODO = 288;
static bool is_increase = true;
+
+extern void update_frame_0x220_odo(int odo);
+extern int get_speed(void);
/**
* Create a demo application
*/
@@ -56,18 +59,19 @@ void speed_meter_timer_cb(lv_timer_t * t)
if(speed >= 150) is_increase = false;
if(speed <= 40) is_increase = true;
// update meter needle vale
- lv_meter_set_indicator_value(gui->home_meter_board, gui->home_meter_board_scale_0_ndline_0, speed);
+ lv_meter_set_indicator_value(gui->home_meter_board, gui->home_meter_board_scale_0_ndline_0, get_speed());
// update label speed
- lv_label_set_text_fmt(gui->home_label_digit, "%"LV_PRId32, speed);
+ lv_label_set_text_fmt(gui->home_label_digit, "%"LV_PRId32, get_speed());
// update arc value
- lv_arc_set_value(gui->home_arc_shape, speed + 1);
+ lv_arc_set_value(gui->home_arc_shape, get_speed() + 1);
// update trip value
- if(trip < 200.0) trip += 0.001;
+ if(trip < 200.0) trip += 0.1;
lv_label_set_text_fmt(gui->home_label_trip_num, "%4.1f", trip);
// update power value
lv_label_set_text_fmt(gui->home_label_power_num, "%"LV_PRId32, power + speed);
// update ODO value
lv_label_set_text_fmt(gui->home_label_ODO_num, "%"LV_PRId32, ODO + (int)trip);
+ update_frame_0x220_odo(ODO + (int)trip);
```
## 板卡验证
修改上述代码后,将程序更新到板子里,发现CAN 总线已经根据仪表的ODO数据体现到CAN总线0x220上,通过CAN 发送0x110 消息,数字仪表会根据0X110的速度信息进行显示,停发CAN 0x110消息数字仪表将停止显示,恢复消息数字仪表继续进行显示,跟预期的一致说明数字仪表显示已经跟CAN 线保持同步。
!(https://www.nxpic.org.cn/module/forum/data/attachment/forum/202406/30/163422h2988e88j88wwam8.gif)
页:
[1]