Funpack3-4利用MCXN947学习CAN通讯
该项目使用了MCXN947,实现了CAN通讯的设计,它的主要功能为:在MCXN947开发板上,使用rtthread系统,实现了IIC读取光度计的值,并通过CAN口上传给上位机;并通过CAN口接收上位机传来的信息。。
标签
嵌入式系统
Funpack活动
测试
开发板
接口
aramy
更新2024-09-09
29

一、硬件介绍:

image.png


1、开发板:FRDM-MCXN947是一款紧凑且可扩展的开发板。该板由一个MCXN947器件和一个64 mbit外部串行闪存(由Winbond公司提供)组成。该板还具有 P3T1755DP I3C 温度传感器、TJA1057GTK/3Z CAN PHY、以太网PHY、SDHC电路(卡槽为DNP)、RGB LED、触摸板、高速USB电路、按钮等 MCU-Link 调试器电路。该板提供 Arduino 扩展、PMOD拓展,MicroBus总线拓展。该板还支持摄像头模块和 NXP 低成本 LCD 模块 PAR-LCD-S035。板载 MCU-Link 调试器基于 LPC55S69 MCU。这次主要是使用该板子的CAN接口。

image.png

2、CAN转串口模块。微控制器的新型CAN总线Grove模块,支持高达5Mbps的CAN FD,并通过串行接口由at命令控制。GD32E103 CAN总线Grove模块规格:MCU–giga device gd32e 103 Arm Cortex-M4F微控制器@最高120 MHz,带64KB至128KB闪存,20KB至32KB SRAM CAN总线–2引脚端子板,带CAN FD,最高5 Mbps主机连接–4引脚Grove连接器,带UART,最高115,200 bps(默认9600 bps)

image.png

3、环境光传感器模块。DLight Unit是一款数字环境光检测传感器, 硬件采用BH1750FVI照度传感器IC(I2C接口), 内置16bit AD转换支持( 1- 65535 lx)照度值检测。具有体积小, 功耗低等特点。 适用于各种照度检测,光控调节场景。

二、任务选择:

一直没有接触过CAN总线,手头也没有CAN总线设备。总是想去学习了解一下基于CAN协议通讯,正好借此机会学习一下CAN通讯。所以这里我选择了任务1:实现USB多协议HUB。板卡上集成了HS-USB接口,要求使用CAN口和UART口向MCU发送信息,通过USB显示出数据;从USB端可以指定向CAN和UART发送数据,并从对应端口发送出去。

三、任务实现:

有个开源的操作系统“RT-Thread”,支持这块板卡。有操作系统加持,开发起来会变得容易很多。所以选定“RT-Thread”操作系统作为基础,在此基础上开发CAN通讯功能。

image.png

1、编译RT-Thread

源码获取:https://github.com/RT-Thread/rt-thread/ 源码比较大,需要耐心下载。

进入到 rt-thread\bsp\nxp\mcx\mcxn\frdm-mcxn947 文件夹中,双击 project.uvprojx 文件,打开 MDK5工程。先进行一次编译,会报错。提示缺少了 completion.c 文件;原因是 rt-thread版本更新之后,文件名称变更了,但是例程里面的源文件没有同步进行调整,导致实际工程编译报错

image.png

解决方案:在工程文件中删除completion.c 文件;在DeviceDrivers组里添加 completion_comm.c completion_up.c condvar.c 3个文件(三个文件都在rt-thread\components\drivers\ipc\)

image.png

image.png

再在Kernel组里添加rt-thread\src\cpu_up.c文件。此时再编译就编译成功了。

2、添加CAN协议支持。这里要使用rt-thread提供的工具ENV做配置(ENV工具安装参考网上的步骤)

image.png

修改文件 rt-thread\bsp\nxp\mcx\mcxn\frdm-mcxn947\board\Kconfig 在190行添加,然后在menuconfig中配置。


menuconfig BSP_USING_CAN
bool "Enable CAN"
select RT_USING_CAN
default y
if BSP_USING_CAN
config BSP_USING_CAN0
bool "Enable CAN0"
default n
config BSP_USING_CAN1
bool "Enable CAN1"
default n
endif

image.png

image.png

配置完成后 执行 scons --target=mdk5 更新项目。

做了以上设置,就能开启CAN接口了。安装MCUXpressoIDE,官方提供的例程中有“frdmmcxn947_flexcan_ping_pong_buffer_transfer”和“frdmmcxn947_flexcan_loopback”。编译通过后,烧写到开发板上,就能够通过搭配的CAN通信模块通过串口和PC机交换信息了。参考着这两个个例程,在rt-thread中添加CAN自测试的代码:

int flexcan_loopback(void)
{
flexcan_config_t flexcanConfig = {0};
flexcan_rx_mb_config_t mbConfig = {0};

mode = LOOP_BACK;

LOG_INFO("\r\n==FlexCAN loopback example -- Start.==\r\n\r\n");
/* Init FlexCAN module. */
/*
* 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);

#if defined(EXAMPLE_CAN_CLK_SOURCE)
flexcanConfig.clkSrc = EXAMPLE_CAN_CLK_SOURCE;
#endif

flexcanConfig.enableLoopBack = true;

#if (defined(USE_IMPROVED_TIMING_CONFIG) && USE_IMPROVED_TIMING_CONFIG)
flexcan_timing_config_t timing_config;
memset(&timing_config, 0, sizeof(flexcan_timing_config_t));
#if (defined(USE_CANFD) && USE_CANFD)
if (FLEXCAN_FDCalculateImprovedTimingValues(EXAMPLE_CAN, flexcanConfig.bitRate, flexcanConfig.bitRateFD,
EXAMPLE_CAN_CLK_FREQ, &timing_config))
{
/* Update the improved timing configuration*/
memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
}
else
{
LOG_INFO("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
}
#else
if (FLEXCAN_CalculateImprovedTimingValues(EXAMPLE_CAN, flexcanConfig.bitRate, EXAMPLE_CAN_CLK_FREQ, &timing_config))
{
/* Update the improved timing configuration*/
memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
}
else
{
LOG_INFO("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
}
#endif
#endif

#if (defined(USE_CANFD) && USE_CANFD)
FLEXCAN_FDInit(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ, BYTES_IN_MB, true);
#else
FLEXCAN_Init(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ);
#endif

/* Setup Rx Message Buffer. */
mbConfig.format = kFLEXCAN_FrameFormatStandard;
mbConfig.type = kFLEXCAN_FrameTypeData;
mbConfig.id = FLEXCAN_ID_STD(0x123);
#if (defined(USE_CANFD) && USE_CANFD)
FLEXCAN_SetFDRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &mbConfig, true);
#else
FLEXCAN_SetRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_NUM, &mbConfig, true);
#endif

/* Setup Tx Message Buffer. */
#if (defined(USE_CANFD) && USE_CANFD)
FLEXCAN_SetFDTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
#else
FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
#endif

/* Create FlexCAN handle structure and set call back function. */
FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);

/* Start receive data through Rx Message Buffer. */
rxXfer.mbIdx = (uint8_t)RX_MESSAGE_BUFFER_NUM;
#if (defined(USE_CANFD) && USE_CANFD)
rxXfer.framefd = &rxFrame;
(void)FLEXCAN_TransferFDReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
#else
rxXfer.frame = &rxFrame;
(void)FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
#endif

/* Prepare Tx Frame for sending. */
txFrame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
txFrame.type = (uint8_t)kFLEXCAN_FrameTypeData;
txFrame.id = FLEXCAN_ID_STD(0x123);
txFrame.length = (uint8_t)DLC;
#if (defined(USE_CANFD) && USE_CANFD)
txFrame.brs = 1U;
txFrame.edl = 1U;
#endif
#if (defined(USE_CANFD) && USE_CANFD)
uint8_t i = 0;
for (i = 0; i < DWORD_IN_MB; i++)
{
txFrame.dataWord[i] = i;
}
#else
txFrame.dataWord0 = CAN_WORD0_DATA_BYTE_0(0xAA) | CAN_WORD0_DATA_BYTE_1(0x22) | CAN_WORD0_DATA_BYTE_2(0x33) |
CAN_WORD0_DATA_BYTE_3(0x44);
txFrame.dataWord1 = CAN_WORD1_DATA_BYTE_4(0xBB) | CAN_WORD1_DATA_BYTE_5(0x66) | CAN_WORD1_DATA_BYTE_6(0x77) |
CAN_WORD1_DATA_BYTE_7(0x88);
#endif

LOG_INFO("Send message from MB%d to MB%d\r\n", TX_MESSAGE_BUFFER_NUM, RX_MESSAGE_BUFFER_NUM);
#if (defined(USE_CANFD) && USE_CANFD)
for (i = 0; i < DWORD_IN_MB; i++)
{
LOG_INFO("tx word%d = 0x%x\r\n", i, txFrame.dataWord[i]);
}
#else
LOG_INFO("tx word0 = 0x%x\r\n", txFrame.dataWord0);
LOG_INFO("tx word1 = 0x%x\r\n", txFrame.dataWord1);
#endif

/* Send data through Tx Message Buffer. */
txXfer.mbIdx = (uint8_t)TX_MESSAGE_BUFFER_NUM;
#if (defined(USE_CANFD) && USE_CANFD)
txXfer.framefd = &txFrame;
(void)FLEXCAN_TransferFDSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
#else
txXfer.frame = &txFrame;
(void)FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
#endif

/* Waiting for Rx Message finish. */
while ((!rxComplete) || (!txComplete))
{
};

LOG_INFO("\r\nReceived message from MB%d\r\n", RX_MESSAGE_BUFFER_NUM);
#if (defined(USE_CANFD) && USE_CANFD)
for (i = 0; i < DWORD_IN_MB; i++)
{
LOG_INFO("rx word%d = 0x%x\r\n", i, rxFrame.dataWord[i]);
}
#else
LOG_INFO("rx word0 = 0x%x\r\n", rxFrame.dataWord0);
LOG_INFO("rx word1 = 0x%x\r\n", rxFrame.dataWord1);
#endif

mode = NOT_IN_FLEXCAN;

LOG_INFO("\r\n==FlexCAN loopback example -- Finish.==\r\n");

return 0;
}

编译通过后,可以对CAN口进行回环测试。

3、添加IIC协议支持,并添加bh1750外设的支持包。

image.png

添加传感器框架的支持。

image.png

使用模拟IIC,管脚使用电源旁边的两个管脚P1_22,P1_23作为SCL和SDA。光线传感器使用的芯片是bh1750。添加传感器的支持包

image.png

image.png

image.png

配置完成后执行 pkgs --update 更新传感器包文件,再执行 scons --target=mdk5 生成新的工程文件。参考着rt-thread\bsp\nxp\mcx\mcxn\frdm-mcxn947\packages\bh1750-latest\README.md 文件 读取传感器,写了个can发送光照流明强度的方法:

//通过can发送光线强度数据
void cansend_light(int argc, char *argv[]){
float light;
char *pbuf;
flexcan_config_t flexcanConfig = {0};
flexcan_rx_mb_config_t mbConfig = {0};
bh1750_device_t dev=bh1750_init("i2c1");
int send_num = 0;
int can_id = 0;

if (argc > 2)
{
LOG_INFO("Too many arguments !\r\n");
return ;
}
else if (argc < 2)
{
LOG_INFO("Too less arguments !\r\n");
return ;
}

send_num = atoi(argv[argc - 1]);

FLEXCAN_GetDefaultConfig(&flexcanConfig);
flexcanConfig.bitRate = 500000U;

/* Enable Rx Individual Mask and Queue feature. */
flexcanConfig.enableIndividMask = true;

#if (defined(USE_IMPROVED_TIMING_CONFIG) && USE_IMPROVED_TIMING_CONFIG)
flexcan_timing_config_t timing_config;
memset(&timing_config, 0, sizeof(flexcan_timing_config_t));
#if (defined(USE_CANFD) && USE_CANFD)
if (FLEXCAN_FDCalculateImprovedTimingValues(EXAMPLE_CAN, flexcanConfig.bitRate, flexcanConfig.bitRateFD,
EXAMPLE_CAN_CLK_FREQ, &timing_config))
{
/* Update the improved timing configuration*/
memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
}
else
{
LOG_INFO("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
}
#else
if (FLEXCAN_CalculateImprovedTimingValues(EXAMPLE_CAN, flexcanConfig.bitRate, EXAMPLE_CAN_CLK_FREQ, &timing_config))
{
/* Update the improved timing configuration*/
memcpy(&(flexcanConfig.timingConfig), &timing_config, sizeof(flexcan_timing_config_t));
}
else
{
LOG_INFO("No found Improved Timing Configuration. Just used default configuration\r\n\r\n");
}
#endif
#endif

#if (defined(USE_CANFD) && USE_CANFD)
FLEXCAN_FDInit(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ, BYTES_IN_MB, true);
#else
FLEXCAN_Init(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ);
#endif

/* Setup Tx Message Buffer. */
#if (defined(USE_CANFD) && USE_CANFD)
FLEXCAN_SetFDTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
#else
FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);
#endif

/* Create FlexCAN handle structure and set call back function. */
FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);

/* Prepare Tx Frame for sending. */
txFrame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
txFrame.type = (uint8_t)kFLEXCAN_FrameTypeData;
txFrame.id = FLEXCAN_ID_STD(0x0021);
txFrame.length = (uint8_t)DLC;
#if (defined(USE_CANFD) && USE_CANFD)
txFrame.brs = 1U;
txFrame.edl = 1U;
#endif

while (send_num > 0)
{
light = bh1750_read_light(dev);
rt_kprintf("read bh1750 sensor intensity : %d\n",(int)light);
pbuf=(char*)(&light);
txFrame.dataByte0 = pbuf[0];
txFrame.dataByte1 = pbuf[1];
txFrame.dataByte2 = pbuf[2];
txFrame.dataByte3 = pbuf[3];
txFrame.dataByte4 =0;
txFrame.dataByte5 =0;
txFrame.dataByte6 =0;
txFrame.dataByte7 =0;
/* Send data through Tx Message Buffer. */
txXfer.mbIdx = (uint8_t)TX_MESSAGE_BUFFER_NUM;
#if (defined(USE_CANFD) && USE_CANFD)
txXfer.framefd = &txFrame;
(void)FLEXCAN_TransferFDSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
#else
txXfer.frame = &txFrame;
(void)FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
#endif

txComplete = false;
// FLEXCAN_TransferSendBlocking(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, &txFrame);
/* Waiting for Rx Message finish. */
while ((!txComplete))
{
};
send_num--;

rt_thread_delay(200);
}

mode = NOT_IN_FLEXCAN;

}

4、使用PYQT制作一个简单的上位机。上位机与下位机通过CAN转换模块通讯。该模块能够将CAN口信息转发到串口,同时将串口信息转发到CAN口上。

image.png

上位机通过一个lab展示下位机上送来的光线强度信息。有一个DIAL控件,旋转它就能将控件值送到下位机上。CAN通讯固定ID:0X0021;串口速率固定为9600波特率。

四、效果展示:

image.png

将开发板通过CAN口与CAN转串口模块相连接,CAN_L连接CAN_L;CAN_H连接CAN_H。CAN转串口模块通过TTL转USB模块与电脑连接:RX连接TX;TX连接RX。环境光传感器模块用杜邦线与板子相连接,P1_22连接SCL;P1_23连接SDA。

image.png

使用了RT_Thread操作系统,开启了IIC 、CAN的支持。

image.png

下位机读取环境光传感器的值,传给上位机展示。

image.png

上位机将控件“dial”的值,传给下位机,下位机解析,并展示。


五、心得体会:非常开心能参加这次活动,MCXN947板子非常优秀,很多功能待探索。不过希望以后活动中的直播课能添加更加深入的内容讲解,毕竟对参加活动的自己仅仅是个爱好者,不是专业的,视频教学仅仅做了最简单的讲解,完成任务太难啦!

附件下载
mcxn.zip
上位机.zip
团队介绍
单片机业余爱好者,瞎捣鼓小能手。
团队成员
aramy
单片机业余爱好者,瞎捣鼓小能手。
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号