基于FRDM-MCXN947的LwIP通信与外设控制(MCUXpresso+LwIP+TSI)

2024-08-24 09:20

本文主要是介绍基于FRDM-MCXN947的LwIP通信与外设控制(MCUXpresso+LwIP+TSI),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 项目介绍
  • 硬件介绍
  • 项目设计
    • 开发环境
    • 总体流程图
    • 功能实现
      • LED控制、按键读取
      • 温度传感器测量与读取
      • 触摸按键读取
      • LwIP通信
      • 编译配置
  • 功能展示
  • 项目总结

👉 【Funpack3-3】
👉 Github: EmbeddedCamerata/FRDM-MCXN947_lwip_peripherals_control

项目介绍

基于 NXP FRDM-MCXN947 开发板,通过板卡上的 RJ45 以太网口与 PC 连接,使用 LwIP 协议与主机进行数据传输。开发板集成有温度传感器、按键、触摸按键等外设,板卡可将温度,触摸与按键信息发送至主机;主机可通过 LwIP 控制板卡 RGB LED。

👉 MCUXpresso
👉 MCUXpresso VSCode 扩展

硬件介绍

FRDM-MCXN947 是一款紧凑且可扩展的开发板,可让您快速基于 MCX N94x MCU 开展原型设计。板卡上集成行业标准的接口,可轻松使用 MCU 的I/O、集成的开放标准串行接口、外部闪存和板载 MCU-Link 调试器,支持各种附加版的扩展,为用户提供简单高效的评估和开发体验。

该板由一个 MCXN947 器件和一个64 mbit外部串行闪存组成。该板还具有 P3T1755DP I3C 温度传感器、TJA1057GTK/3Z CAN PHY、以太网 PHY、SDHC电路、RGB LED、触摸板、高速 USB 电路、按钮等 MCU-Link 调试器电路。该板提供 Arduino 扩展、PMOD 拓展,MicroBus 总线拓展。该板还支持摄像头模块和 NXP 低成本 LCD 模块 PAR-LCD-S035。板载 MCU-Link 调试器基于 LPC55S69 MCU。

板卡顶部视图

项目设计

开发环境

本项目使用 MCUXpresso VSCode 扩展开发。项目所用到的工具链、库如下:

  1. 编译器:arm-none-eabi-gcc v13.3.1
  2. 调试器:arm-none-eabi-gdb v14.2
  3. MCUXpresso SDK:SDK_2.x_FRDM-MCXN947 v2.16
  4. Linkserver:v1.6.121,启动和管理恩智浦硬件调试器的GDB服务器,并提供命令行目标闪存编程功能

总体流程图

工程总体流程图
首先初始化时钟与外设,而后创建三个外设相关的读取任务,用于周期性读取并记录它们的状态。之后进行 LwIP 初始化,进行以太网初始化、IPv4 地址设置,再创建主任务,socket 创建、绑定、监听,并在死循环中等待 PC 连接。当 PC 连接后,创建数据发送任务,每秒发送外设的状态信息;且接收 PC 发送的命令,进行字符串匹配,根据匹配结果控制对应 REB LED 。当 PC 断连后,删除数据发送任务,并等待下次连接。

功能实现

由于使用的是 MCUXpresso VSCode 扩展,因此本项目基于多个示例工程进行开发。本项目将参考官方提供的多个相关模块的示例代码进行移植与修改。

当安装好对应开发板的 SDK 后,可通过扩展内的“Import Example from Repository”选项导入示例程序。

LED控制、按键读取

参考示例工程 frdmmcxn947_gpio_input_interrupt 中的对 GPIO 的配置、读写功能。

/* Define the init structure for the input switch pin */
gpio_pin_config_t sw_config = {kGPIO_DigitalInput,0,
};
GPIO_PinInit(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN, &sw_config);
/* LED init */
LED_RED_INIT(LOGIC_LED_OFF);
LED_GREEN_INIT(LOGIC_LED_OFF);
LED_BLUE_INIT(LOGIC_LED_OFF);

针对 RGB LED 的操作可通过宏 LED_xxx_INITLED_xxx_ONLED_xxx_OFFLED_xxx_TOGGLE 完成,xxx 为对应颜色。针对 SW3,将其配置成输入,通过 GPIO_PinRead() 可读取其值,0表示按下。将按键读取功能单独写成函数,后续将作为一个单独的任务在 FreeRTOS 中创建。

/* Whether the SW button is pressed */
volatile uint32_t g_ButtonStatus = 0;
static void sw_scan_task(void *arg)
{while (1){if ((g_ButtonStatus = GPIO_PinRead(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN)) == 0){PRINTF("%s is pressed\n", BOARD_SW3_NAME);}vTaskDelay(200 / portTICK_PERIOD_MS);}vTaskDelete(NULL);
}

温度传感器测量与读取

参考示例工程 frdmmcxn947_lpadc_temperature_measurement 中初始化温度传感器、读取温度的代码,该工程通过设置 ADC 软件中断进行温度测量, g_LpadcConversionCompletedFlag 用于表示温度测量是否完成,测得的温度保存至 g_CurrentTemperature 中。读取温度数值的代码单独写成函数,后续将作为一个单独的任务在 FreeRTOS 中创建。

volatile bool g_LpadcConversionCompletedFlag = false;
float g_CurrentTemperature = 0.0f;
static void temp_measurement_task(void *arg)
{while (1){g_LpadcConversionCompletedFlag = false;LPADC_DoSoftwareTrigger(LPADC_BASE, 1U); /* 1U is trigger0 mask. */while (false == g_LpadcConversionCompletedFlag);vTaskDelay(500 / portTICK_PERIOD_MS);}vTaskDelete(NULL);
}

触摸按键读取

参考示例工程 frdmmcxn947_tsi_v6_self_cap 中提供的软件中断触发扫描(示例还提供了硬件触发扫描、软件轮询触发扫描的方法)的方法实现对触摸按键的读取检测。除了打开必要的时钟外,三种扫描方法对TSI初始化的配置是相同的:

tsi_calibration_data_t buffer;
void TSI0_IRQHandler(void)
{...
}int main(void)
{/* Configure LPTMR */LPTMR_GetDefaultConfig(&lptmrConfig);/* TSI default hardware configuration for self-cap mode */TSI_GetSelfCapModeDefaultConfig(&tsiConfig_selfCap);/* Initialize the LPTMR */LPTMR_Init(LPTMR0, &lptmrConfig);/* Initialize the TSI */TSI_InitSelfCapMode(APP_TSI, &tsiConfig_selfCap);/* Enable noise cancellation function */TSI_EnableNoiseCancellation(APP_TSI, true);/* Set timer period */LPTMR_SetTimerPeriod(LPTMR0, USEC_TO_COUNT(LPTMR_USEC_COUNT, LPTMR_SOURCE_CLOCK));NVIC_EnableIRQ(TSI0_IRQn);TSI_EnableModule(APP_TSI, true); /* Enable module *//*********  CALIBRATION PROCESS ************/memset((void *)&buffer, 0, sizeof(buffer));TSI_SelfCapCalibrate(APP_TSI, &buffer);
}

当触摸按键未被按下时,s_tsiInProgress 为 true ,当按下超过一定时长后,触发软件中断,在回调函数 TSI0_IRQHandler 中,s_tsiInProgress 置 false 。按下过程中,将触发多次软件中断。当松手后,不会触发软件中断,s_tsiInProgress 将置 true,将检测触摸按键的代码单独写成函数,后续将作为一个单独的任务在 FreeRTOS 中创建。

static void tsi_scan_task(void *arg)
{while (1){while (s_tsiInProgress){TSI_StartSoftwareTrigger(APP_TSI);}s_tsiInProgress = true;vTaskDelay(100 / portTICK_PERIOD_MS);}vTaskDelete(NULL);
}

LwIP通信

本项目主体代码参考示例工程 frdmmcxn947_lwip_ping_freertos ,该示例提供了完整的IPv4地址初始化、网口初始化、FreeRTOS配置等,后续只需要将示例中所使用的 ping 功能替换为我们的业务代码即可。

LwIP参考:[野火]LwIP应用开发实战指南—基于STM32

首先配置开发板的IPv4地址 configIP_ADDR0/1/2/3 与所连接的网关的地址 configGW_ADDR0/1/2/3

#define TCP_PORT 5001static void lwip_tcp_task(void *arg)
{LWIP_UNUSED_ARG(arg);int err, sock;struct timeval timeout = {.tv_sec = 1, .tv_usec = 0};struct sockaddr_in server_addr, client_addr;int connected = -1;socklen_t sin_size = sizeof(struct sockaddr_in);char *recv_cmd;ssize_t recv_cmd_len;char unknown_cmd_str[] = "Unknown command\n";recv_cmd = (char *)pvPortMalloc(LWIP_RECV_DATA_SIZE);LWIP_ERROR("no memory.", recv_cmd != NULL, goto __exit;);sock = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);LWIP_ERROR("socket build failed.", sock >= 0, return;);err = lwip_setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));LWIP_ERROR("setting receive timeout failed.", err == 0, goto __exit;);server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = INADDR_ANY;server_addr.sin_port = lwip_htons(TCP_PORT);memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));/* Socket bind */err = lwip_bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));LWIP_ERROR("bind failed.", err == 0, goto __exit;);/* Listen */err = lwip_listen(sock, 5);LWIP_ERROR("listen failed.", err == 0, goto __exit;);while (1){...}vTaskDelete(NULL);__exit:if (recv_cmd)vPortFree(recv_cmd);if (sock >= 0)lwip_close(sock);vTaskDelete(NULL);

通过 lwip_socketlwip_setsockopt 创建一个socket接口并配置,lwip_bind 将申请成功的docket与网卡信息进行绑定,绑定到500端口。recv_cmd 后续将接收从PC发送来的数据。

后续,在 while(1) 循环中,通过 lwip_accept 判断 PC 是否发出连接请求,若请求则返回描述符。若连接,则创建一个发送任务,用于定时向PC发送传感器、按键信息。

while (1)
{connected = lwip_accept(sock, (struct sockaddr *)&client_addr, &sin_size);if (connected > 0){PRINTF("new client connected from (%s, %d)\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));if (send_data_task_handle == NULL){if (xTaskCreate(send_data_task, "send_data_task", 512,(void *)&connected, 4, &send_data_task_handle) != pdPASS){PRINTF("lwip_tcp_task(): Task creation failed!\n");while (1);}}while (1){...}else{if (send_data_task_handle != NULL){vTaskDelete(send_data_task_handle);send_data_task_handle = NULL;}}vTaskDelay(500 / portTICK_PERIOD_MS);
}

同时,当 PC 连接后,阻塞接收从 PC 传输的数据,并进行字符串匹配,根据匹配的结果“blue”/“green”/"red"控制对应的 LED 状态翻转;匹配到“close”则关闭连接;未匹配则发送字符串至 PC 进行提示。

#define LWIP_RECV_DATA_SIZE 128
while (1)
{memset(recv_cmd, 0, LWIP_RECV_DATA_SIZE);recv_cmd_len = lwip_recv(connected, recv_cmd, LWIP_RECV_DATA_SIZE, 0);if (recv_cmd_len <= 0)break;/* Command match */if (strcmp(recv_cmd, "close") == 0){err = lwip_close(connected);LWIP_ERROR("close failed", err == 0, goto __exit;);connected = -1;break;}else if (strcmp(recv_cmd, "blue") == 0){LED_BLUE_TOGGLE();}else if (strcmp(recv_cmd, "green") == 0){LED_GREEN_TOGGLE();}else if (strcmp(recv_cmd, "red") == 0){LED_RED_TOGGLE();}else // Send unknown command message{lwip_write(connected, (void *)unknown_cmd_str, strlen(unknown_cmd_str));}
}

该任务实现每秒通过 lwip_write 向 PC 发送温度传感器读数、按键、触摸按键信息。它在 LwIP 连接到 PC 后创建(若不存在则创建),若连接关闭则任务将被删除。

#define LWIP_SEND_DATA_SIZE 128static void send_data_task(void *arg)
{int connected = *(int *)arg;int temp_int, temp_deci;char *send_data;send_data = (char *)pvPortMalloc(LWIP_SEND_DATA_SIZE);LWIP_ERROR("no memory", send_data != NULL, goto __exit;);LWIP_UNUSED_ARG(arg);while (1){temp_int = (int)g_CurrentTemperature;temp_deci = (int)(100 * (g_CurrentTemperature - temp_int));sprintf(send_data, "temp: %02d.%02d, touch: %d, sw3: %lu\n",temp_int, temp_deci, 1 - s_tsiInProgress, 1 - g_ButtonStatus);lwip_write(connected, (void *)send_data, strlen(send_data));vTaskDelay(1000 / portTICK_PERIOD_MS);}vTaskDelete(NULL);__exit:if (send_data)vPortFree(send_data);vTaskDelete(NULL);
}

编译配置

需要根据前述所参考的工程,将它们的 pin_mux.cboard.h 综合起来,这里面主要是针对 GPIO 或板卡硬件相关参数的定义。

同时,还需要为 armgcc/config.cmake 添加额外用到的模块,具体可通过前述示例工程内的 armgcc/config.cmake 进行比较可得。将它们汇总至本工程下 armgcc/config.cmake 即可。

set(CONFIG_COMPILER gcc)
set(CONFIG_TOOLCHAIN armgcc)
set(CONFIG_USE_COMPONENT_CONFIGURATION false)
set(CONFIG_USE_driver_phy-device-lan8741 true)
set(CONFIG_USE_driver_vref_1 true)
set(CONFIG_USE_driver_crc true)
set(CONFIG_USE_utility_debug_console_lite true)
set(CONFIG_USE_utility_assert_lite true)
set(CONFIG_USE_driver_lpadc true)
set(CONFIG_USE_driver_flashiap true)
set(CONFIG_USE_component_silicon_id true)
set(CONFIG_USE_middleware_lwip_mcx_ethernetif true)
set(CONFIG_USE_middleware_lwip true)
set(CONFIG_USE_driver_mcx_enet true)
set(CONFIG_USE_middleware_freertos-kernel_cm33_non_trustzone true)
set(CONFIG_USE_middleware_lwip_contrib_ping true)
set(CONFIG_USE_driver_clock true)
set(CONFIG_USE_middleware_freertos-kernel_heap_3 true)
set(CONFIG_USE_driver_common true)
set(CONFIG_USE_driver_inputmux true)
set(CONFIG_USE_driver_power true)
set(CONFIG_USE_driver_reset true)
set(CONFIG_USE_device_MCXN947_CMSIS true)
set(CONFIG_USE_device_MCXN947_startup true)
set(CONFIG_USE_driver_lptmr true)
set(CONFIG_USE_driver_tsi_v6 true)
set(CONFIG_USE_driver_lpuart true)
set(CONFIG_USE_driver_gpio true)
set(CONFIG_USE_driver_port true)
set(CONFIG_USE_component_lpuart_adapter true)
set(CONFIG_USE_component_lists true)
set(CONFIG_USE_driver_reset true)
set(CONFIG_USE_utilities_misc_utilities true)
set(CONFIG_USE_utility_str true)
set(CONFIG_USE_driver_phy-common true)
set(CONFIG_USE_middleware_lwip_template true)
set(CONFIG_USE_component_gpio_adapter true)
set(CONFIG_USE_middleware_freertos-kernel true)
set(CONFIG_USE_middleware_freertos-kernel_template true)
set(CONFIG_USE_middleware_freertos-kernel_extension true)
set(CONFIG_USE_driver_mcx_spc true)
set(CONFIG_USE_driver_inputmux_connections true)
set(CONFIG_USE_CMSIS_Include_core_cm true)
set(CONFIG_USE_device_MCXN947_system true)
set(CONFIG_USE_driver_lpflexcomm true)
set(CONFIG_CORE cm33)
set(CONFIG_DEVICE MCXN947)
set(CONFIG_BOARD frdmmcxn947)
set(CONFIG_KIT frdmmcxn947)
set(CONFIG_DEVICE_ID MCXN947)
set(CONFIG_FPU SP_FPU)
set(CONFIG_DSP DSP)
set(CONFIG_CORE_ID cm33_core0)

由于直接使用的 MCUX VSCode 扩展,因此未安装 newlib 库,头一次编译将报错。这时,将 armgcc/CMakePresets.json 中有关 NEWLIB 的配置删除即可。

同时,还有可能遇到成功编译、下载,但是程序卡死的情况。修改 armgcc/flags.cmake 中 linker 链接标志,位于 CMAKE_EXE_LINKER_FLAGS_DEBUGCMAKE_EXE_LINKER_FLAGS_RELEASE,修改 --defsym=__stack_size__=2000--defsym=__heap_size__=38400 应该可以解决问题。

功能展示

首先通过网线将板卡与 PC 连接,在 PC 上连接该有线网。此时,串口将打印 IP 等信息。

LwIP连接
通过 TCP 连接板卡,连接后,板卡每秒钟将通过 LwIP 发送温度、按键等信息。如下图所示,按下触摸按键,“touch btn”状态为1。

板卡通过LwIP发送数据
PC 也可通过 LwIP 发送数据至板卡以控制 LED。如下图所示,输入“red”,板卡红灯亮。
在这里插入图片描述

👉 详细展示参见:B站:基于FRDM-MCXN947的LwIP通信与外设控制

项目总结

本次项目基于 FRDM-MCXN947 与 LwIP 实现了与 PC 的通信与外设控制,了解了 LwIP 的基本使用方法、触摸按键等外设的读取。MCUX 扩展功能集成度高,项目的编译、下载均可通过扩展完成,调试也可以使用 gdb 在 VSCode 中实现。

这篇关于基于FRDM-MCXN947的LwIP通信与外设控制(MCUXpresso+LwIP+TSI)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1102078

相关文章

用js控制视频播放进度基本示例代码

《用js控制视频播放进度基本示例代码》写前端的时候,很多的时候是需要支持要网页视频播放的功能,下面这篇文章主要给大家介绍了关于用js控制视频播放进度的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言html部分:JavaScript部分:注意:总结前言在javascript中控制视频播放

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

使用DrissionPage控制360浏览器的完美解决方案

《使用DrissionPage控制360浏览器的完美解决方案》在网页自动化领域,经常遇到需要保持登录状态、保留Cookie等场景,今天要分享的方案可以完美解决这个问题:使用DrissionPage直接... 目录完整代码引言为什么要使用已有用户数据?核心代码实现1. 导入必要模块2. 关键配置(重点!)3.

SpringSecurity 认证、注销、权限控制功能(注销、记住密码、自定义登入页)

《SpringSecurity认证、注销、权限控制功能(注销、记住密码、自定义登入页)》SpringSecurity是一个强大的Java框架,用于保护应用程序的安全性,它提供了一套全面的安全解决方案... 目录简介认识Spring Security“认证”(Authentication)“授权” (Auth

python之流程控制语句match-case详解

《python之流程控制语句match-case详解》:本文主要介绍python之流程控制语句match-case使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录match-case 语法详解与实战一、基础值匹配(类似 switch-case)二、数据结构解构匹

Spring Security注解方式权限控制过程

《SpringSecurity注解方式权限控制过程》:本文主要介绍SpringSecurity注解方式权限控制过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、摘要二、实现步骤2.1 在配置类中添加权限注解的支持2.2 创建Controller类2.3 Us

Python中如何控制小数点精度与对齐方式

《Python中如何控制小数点精度与对齐方式》在Python编程中,数据输出格式化是一个常见的需求,尤其是在涉及到小数点精度和对齐方式时,下面小编就来为大家介绍一下如何在Python中实现这些功能吧... 目录一、控制小数点精度1. 使用 round() 函数2. 使用字符串格式化二、控制对齐方式1. 使用

Springboot控制反转与Bean对象的方法

《Springboot控制反转与Bean对象的方法》文章介绍了SpringBoot中的控制反转(IoC)概念,描述了IoC容器如何管理Bean的生命周期和依赖关系,它详细讲解了Bean的注册过程,包括... 目录1 控制反转1.1 什么是控制反转1.2 SpringBoot中的控制反转2 Ioc容器对Bea

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

Spring IOC控制反转的实现解析

《SpringIOC控制反转的实现解析》:本文主要介绍SpringIOC控制反转的实现,IOC是Spring的核心思想之一,它通过将对象的创建、依赖注入和生命周期管理交给容器来实现解耦,使开发者... 目录1. IOC的基本概念1.1 什么是IOC1.2 IOC与DI的关系2. IOC的设计目标3. IOC