基于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

相关文章

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

vue2 组件通信

props + emits props:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新) props检查属性 属性名类型描述默认值typeFunction指定 prop 应该是什么类型,如 String, Number, Boolean,

控制反转 的种类

之前对控制反转的定义和解释都不是很清晰。最近翻书发现在《Pro Spring 5》(免费电子版在文章最后)有一段非常不错的解释。记录一下,有道翻译贴出来方便查看。如有请直接跳过中文,看后面的原文。 控制反转的类型 控制反转的类型您可能想知道为什么有两种类型的IoC,以及为什么这些类型被进一步划分为不同的实现。这个问题似乎没有明确的答案;当然,不同的类型提供了一定程度的灵活性,但

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理

深入解析秒杀业务中的核心问题 —— 从并发控制到事务管理 秒杀系统是应对高并发、高压力下的典型业务场景,涉及到并发控制、库存管理、事务管理等多个关键技术点。本文将深入剖析秒杀商品业务中常见的几个核心问题,包括 AOP 事务管理、同步锁机制、乐观锁、CAS 操作,以及用户限购策略。通过这些技术的结合,确保秒杀系统在高并发场景下的稳定性和一致性。 1. AOP 代理对象与事务管理 在秒杀商品

PostgreSQL中的多版本并发控制(MVCC)深入解析

引言 PostgreSQL作为一款强大的开源关系数据库管理系统,以其高性能、高可靠性和丰富的功能特性而广受欢迎。在并发控制方面,PostgreSQL采用了多版本并发控制(MVCC)机制,该机制为数据库提供了高效的数据访问和更新能力,同时保证了数据的一致性和隔离性。本文将深入解析PostgreSQL中的MVCC功能,探讨其工作原理、使用场景,并通过具体SQL示例来展示其在实际应用中的表现。 一、

vue2实践:el-table实现由用户自己控制行数的动态表格

需求 项目中需要提供一个动态表单,如图: 当我点击添加时,便添加一行;点击右边的删除时,便删除这一行。 至少要有一行数据,但是没有上限。 思路 这种每一行的数据固定,但是不定行数的,很容易想到使用el-table来实现,它可以循环读取:data所绑定的数组,来生成行数据,不同的是: 1、table里面的每一个cell,需要放置一个input来支持用户编辑。 2、最后一列放置两个b

【电机控制】数字滤波算法(持续更新)

文章目录 前言1. 数字低通滤波 前言 各种数字滤波原理,离散化公式及代码。 1. 数字低通滤波 滤波器公式 一阶低通滤波器的输出 y [ n ] y[n] y[n] 可以通过以下公式计算得到: y [ n ] = α x [ n ] + ( 1 − α ) y [ n − 1 ] y[n] = \alpha x[n] + (1 - \alpha) y[n-1]