y369369 发表于 2024-12-2 13:01:45

【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]
查看完整版本: 【Avnet | NXP FRDM-MCXN947试用活动】N947的CAN 总线实现仪表