pomin

  • 2024-12-17
  • 上传了资料: 带健康提醒的86盒桌面助手代码

  • 2024-10-31
  • 发表了主题帖: 【2024 DigiKey 创意大赛】带健康提醒的86盒桌面助手

    **一、作品简介** 设计名称:带健康提醒的86盒桌面助手 作者:pomin 项目用到的板卡:使用 ESP32-S3-LCD-Ev-Board开发板,采用的是ESP32-S3这款 MCU,板卡板载了4寸的电容触摸屏,功能强悍,做HMI应用十分合适。 作品功能介绍:借此次得捷大赛的机会,制作了一款带健康提醒的86盒桌面助手,86盒可以摆在桌子上,实时地检测前方的温度,也就是监测使用者的体表温度,并显示在屏幕上面,并可通过网络来获取时间、天气等显示在屏幕上,给使用者提供出行建议等,软件采用LVGL开源GUI界面库,使使用者更加感到可视化、智能化设备带来的便捷。 **二、系统框图** 本项目使用ESP32-S3-LCD-Ev-Board开发板来制作,用LVGL完成了十分美观的界面的绘制,采用欧姆龙D6T-01a传感器来监测体表温度,接入到 HomeAssistant 家庭自动化,搭配 Node-RED 来实现温度监控上传到HomeAssistant,系统框图如下图所示。 **三、各部分功能说明** ESP32-S3-LCD-Ev-Board板卡支持外接的排针接口,所以将欧姆龙的D6T温度传感器连接到开发板,使用I2C接口和板卡通讯,采用MQTT协议与服务器来通讯。 使用ESP-IDF进行开发,在开发板上电的时候自动联网、连接家庭 MQTT 服务器,并订阅指定主题,定时上传D6T采集数据 使用ESP32-S3-LCD-Ev-Board 板卡板载的4寸电容触摸屏来实现HMI,使用LVGL完成了十分美观的界面的绘制,实时的监控当前使用者的体温,并且接入到HomeAssistant。 使用 Node-RED,将开发板上传的温度数据通过Javascript脚本解析然后映射到HomeAssistant。 **六、项目总结** 总结:用LVGL搭配GUI Guider完成了界面的绘制,采用 MQTT 协议与家庭服务器来通讯,通过 MQTT 接入到 HomeAssistant 家庭自动化,搭配 Node-RED 来实现家庭自动化流的创建,操控家中各种智能设备。 帖子分享链接汇总: [【2024 DigiKey 创意大赛】开箱贴(ESP32-S3-LCD、D6T传感器)](https://bbs.eeworld.com.cn/thread-1289930-1-1.html) [【2024 DigiKey 创意大赛】D6T非接触温度传感器调试](https://bbs.eeworld.com.cn/thread-1291214-1-1.html) [【2024 DigiKey 创意大赛】搭建环境、运行86盒demo](https://bbs.eeworld.com.cn/thread-1295847-1-1.html) [【2024 DigiKey 创意大赛】开发板读取D6T传感器值、LVGL显示](https://bbs.eeworld.com.cn/thread-1297965-1-1.html) [localvideo]b54c0d08f5e0d29a2d6894de774dd88f[/localvideo] 源码:https://download.eeworld.com.cn/detail/pomin/635329

  • 发表了主题帖: 【2024 DigiKey 创意大赛】开发板读取D6T传感器值、LVGL显示

    为了把 D6T 传感器连接到开发板,先查看原理图,开发板有一些预留的 IO 接口,但是大多数都不能随意使用 然后查看原理图可以知道 IO47 和 IO48 这两个引脚是作为了 IIC 使用,外接的是屏幕板的电容触摸芯片 FT5406,在代码中也可以看到: 然后查了查文档,FT5406 的 IIC 七位地址是 0x38,而 D6T 的 IIC 七位地址是 0x0A,所以可以把这两个器件都接在 IO47、IO48,然后把 D6T 模块接在开发板的 IO47、IO48上面,代码如下 ```c /* * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */ #include "core/lv_disp.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "esp_check.h" #include "nvs_flash.h" #include "nvs.h" #include "bsp_board_extra.h" #include "bsp/esp-bsp.h" #include #include #include "driver/i2c.h" static char *TAG = "app_main"; #define LOG_MEM_INFO    (0) /* defines */ #define D6T_ADDR 0x0A  // for I2C 7bit address #define D6T_CMD 0x4C   // for D6T-44L-06/06H, D6T-8L-09/09H, for D6T-1A-01/02 #define N_ROW 1 #define N_PIXEL 1 #define N_READ ((N_PIXEL + 1) * 2 + 1) uint8_t rbuf[N_READ]; double ptat; double pix_data[N_PIXEL]; uint8_t calc_crc(uint8_t data) {     int index;     uint8_t temp;     for (index = 0; index < 8; index++) {         temp = data;         data convert a 16bit data from the byte stream. */ int16_t conv8us_s16_le(uint8_t* buf, int n) {     uint16_t ret;     ret = (uint16_t)buf[n];     ret += ((uint16_t)buf[n + 1])

  • 加入了学习《【2024DigiKey创意大赛】基于AIOT的智能家居设备开发演示视频》,观看 【2024DigiKey创意大赛】基于AIOT的智能家居设备开发

  • 2024-10-30
  • 加入了学习《ESP32-S3-LVGL》,观看 自行车智能灯

  • 2024-10-29
  • 加入了学习《 【2024 DigiKey创意大赛】 《智能起居室环境控制台》任务报告汇总》,观看 【2024 DigiKey创意大赛】 《智能起居室环境控制台》任务报告汇总

  • 2024-10-17
  • 加入了学习《Followme-3 XIAO开发板作业视频》,观看 Followme3-作业提交

  • 2024-10-11
  • 发表了主题帖: 【2024 DigiKey 创意大赛】搭建环境、运行86盒demo

    # 搭建环境 Linux下配置 ESP-idf 还是很轻松的,先 clone 并安装一下,我这里用的是 Ubuntu 18.04 的虚拟机 先装点软件包 ```sh sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0 ``` 然后 clone 并运行安装程序 ```bash git clone -b v5.1 --recursive https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh . ./export.sh ``` 此时工具链就安装完成了,可以看到比 Windows 要方便许多 试一下编译 `cd examples/get-started/blink/ && idf.py build` 编译成功 为了方便,再添加一个快捷命令,配置脚本到 bash 或者 zsh ( 我这里用的是 zsh ) ```bash #zsh nano ~/.zshrc #bash nano ~/.bashrc ``` 添加重定向脚本 ```bash alias idf='idf.py' alias iidf='. ~/esp/esp-idf/export.sh' export IDF_TOOLS_PATH=~/.espressif export idf export iidf ``` 更新 bash 或 zsh 配置 ```bash #zsh source ~/.zshrc #bash source ~/.bashrc ``` 然后要在当前终端初始化的话就 `iidf`、idf 重定向到 idf.py,编译直接 `idf build` 就行 # 运行 86 盒 demo 先把这个开发板的官方代码仓库克隆下来,官方的仓库包含有很很多开发板的 demo 程序,这里只留 esp32-s3-lcd-ev-board 这个开发板的 demo 程序 ```sh git clone https://github.com/espressif/esp-dev-kits.git cd esp-dev-kits mv esp32-s3-lcd-ev-board .. rm -rf esp-dev-kits cd esp32-s3-lcd-ev-board ``` 可以看到目录中有许多的 demo 程序,这里编译 86box_demo 这个 demo ```sh cd examples/86box_demo idf build ``` 然后烧录并打开串口监视 ```sh idf flash -p /dev/ttyUSB0 -b 921600 && idf monitor -p /dev/ttyUSB0 -b 115200 ``` 同时可以看到板子上的屏幕已经运行 86 盒的 demo 了

  • 2024-08-22
  • 发表了主题帖: 【2024 DigiKey 创意大赛】D6T非接触温度传感器调试

    本帖最后由 pomin 于 2024-8-22 00:28 编辑 这次活动选的传感器是欧姆龙的 D6T-1A-02,下载了规格书看到是 I2C 接口通讯 接口座子的型号是 GH1.25 4P 带锁扣,开发板的排针都是 2.54 间距的,所以还需要整一根 GH1.25 转 2.54 间距的转接线 打开 D6T 的 user manual 可以看到示例电路图,这模块居然没有板载 I2C 的上拉电阻?? 也可以看到 D6T-1A-02 的 I2C 协议数据包,从机地址是 0x0A,时钟速度 100kHz,写一个 0x4C 的命令后读取 5 个字节的数据 然后查到官方提供的有 arduino 的驱动代码, [https://github.com/omron-devhub/d6t-2jcieev01-arduino/blob/master/examples/d6t-1a/d6t-1a.ino](https://github.com/omron-devhub/d6t-2jcieev01-arduino/blob/master/examples/d6t-1a/d6t-1a.ino) 然后用 USB 转 I2C 调试器连接到 D6T 传感器,同时在板子底部的 I2C 接口飞两个4.7k的上拉电阻,复制官方的arduino代码然后修改一下,使用 I2C 协议读取到模块的温度采集数据。 [localvideo]135775d94635f981c51a88baf858526f[/localvideo]

  • 2024-08-08
  • 回复了主题帖: 【2024 DigiKey 创意大赛】开箱贴(ESP32-S3-LCD、D6T传感器)

    wangerxian 发表于 2024-8-8 13:53 欧姆龙的D6T传感器是做什么检测的? 非接触式温度传感器

  • 发表了主题帖: 【2024 DigiKey 创意大赛】开箱贴(ESP32-S3-LCD、D6T传感器)

    这次报名参加了今年的得捷创意大赛,很荣幸入选了,在得捷商城下单了一个ESP32-S3-LCD和欧姆龙的D6T传感器,没想到这次得捷的物流如此迅速,收到货还是十分的激动的,开始拆箱。 严严实实的包装盒 ESP32-S3-LCD开发板有着非常精致的包装盒,D6T是欧姆龙的非接触式温度传感器 拆开盒子,ESP32-S3-LCD这开发板还蛮大的 插上电,就是官方的一个运行LVGL界面库的86界面

  • 2024-05-17
  • 发表了主题帖: 【2023 DigiKey大赛参与奖】开箱帖 Raspberry Pi 5 4G

    本帖最后由 pomin 于 2024-5-17 02:12 编辑 > 这次参加了2023得捷大赛,虽然没有获得大奖,最后也有了一个参与奖,很开心。下单了一个4GB内存的树莓派5开发板,快递今天到了,板子很不错。 快递袋 树莓派5的盒子,还是这一贯的玫红色包装盒 打开盒子就是树莓派5了,这代虽然没有之前的好看些,不过性能确实提高了不少 感谢EEWorld和得捷举办的本次活动。 希望EEWorld和得捷越办越好!!!

  • 2024-04-24
  • 加入了学习《【DigiKey创意大赛】多通道微型气相色谱采集单元》,观看 多通道微型气相色谱采集单元

  • 2024-04-15
  • 回复了主题帖: 【STM32F411Nucleo测评】驱动 1.3 寸 LCD 屏幕

    deng0713 发表于 2024-4-14 22:26 请问这个扩展版是嘉立创元件库里的吗还是大佬自己画的(☆▽☆) 自己画的

  • 2024-03-11
  • 回复了主题帖: 【STM32F411Nucleo测评】移植 Freemodbus 库

    qiao--- 发表于 2024-3-10 00:07 请问一下,你是用的这个modbus调试工具是什么啊 mthings

  • 2024-03-08
  • 发表了主题帖: 【STM32F411Nucleo测评】modbus&LVGL 屏显从站

    > 在电机应用中,尝尝需要获知电机的运行参数,例如转速、温度等,许多驱动板也会带有屏显,或者通过modbus连接到上位机或者组态屏来监控电机状态。本文使用 LVGL 和 Freemodbus 库制作了一个带屏线从站的 demo ## 往期链接 [【STM32F411Nucleo测评】开箱,搭建开发环境,串口输出](https://bbs.eeworld.com.cn/thread-1272849-1-1.html) [【STM32F411Nucleo测评】驱动 1.3 寸 LCD 屏幕](https://bbs.eeworld.com.cn/thread-1272852-1-1.html) [【STM32F411Nucleo测评】移植多功能按键驱动库](https://bbs.eeworld.com.cn/thread-1273453-1-1.html) [【STM32F411Nucleo测评】移植 Freemodbus 库](https://bbs.eeworld.com.cn/thread-1273454-1-1.html) [【STM32F411Nucleo测评】移植 LVGL 界面库](https://bbs.eeworld.com.cn/thread-1273455-1-1.html) 在前文的介绍中,已经完成了对 LVGL 和 Freemodbus 的移植,下面介绍做一个上层应用——modbus LVGL 屏显从站的 demo 制作。 ## GUI Guider 设计界面 GUI Guider 是 NXP 给 LVGL 开发的可视化界面编辑、模拟器,所见即所得,同时也可以在此之上完成对于时间、定时器、界面切换任务的添加,这里绘制了一个 demo 界面,有两个按键、一个滑条和一个标签文本。 设计的操作逻辑如下: - 按下 Down 按键减小速度、Up 按键增大速度 - 滑条可以百分比放缩显示当前速度 - 标签文本显示当前速度 对于上述的功能写代码当然是比较容易实现的,但是这里是在 GUI Guider 中添加这些事件和任务的响应代码。 按下 Down 按键减小速度、Up 按键增大速度时对应的是 Click 事件,这里添加 Click 的事件代码,当 Down 按下后会设置标签文本并且修改滑条的位置 ```c unsigned int speed = 1000; ``` ```c speed-=10; lv_label_set_text_fmt(guider_ui.screen_label_1, "Speed: %dRPM", speed); lv_slider_set_value(guider_ui.screen_slider_1, speed / 16, LV_ANIM_ON); ``` 类似的, Up 按键的 Click 事件添加代码 ```c speed+=10; lv_label_set_text_fmt(guider_ui.screen_label_1, "Speed: %dRPM", speed); lv_slider_set_value(guider_ui.screen_slider_1, speed / 16, LV_ANIM_ON); ``` 然后在 GUI Guider 中仿真一下,与预期的想法一致,然后生成代码加入到 keil 工程中 添加的文件如下,当然用别的字体的话字体文件跟这个肯定名字不一样 然后就可以把生成的界面代码加载起来了,先在 main.c 中定义一个全局变量 ```c lv_ui guider_ui; ``` 然后在 while(1) 之前加载 GUI Guider 绘制的界面 ```c setup_ui(&guider_ui); ``` 编译烧录到开发板 此时,这个按键是没法操作的,因为在仿真器中加入的是 Windows 的输入设备,在 MCU 端还需要再把按键注册到 LVGL 的输入设备中。 编写 lv_port_indev.c 的代码如下,主要就是在 button_is_pressed 函数中获取按键的电平状态,在初始化时定义按键到 btn_points,按键按下时相当于对应的坐标点被点击。 ```c /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/ #if 1 /********************* *      INCLUDES *********************/ #include "lv_port_indev.h" #include "lvgl/lvgl.h" /********************* *      DEFINES *********************/ /********************** *      TYPEDEFS **********************/ /********************** *  STATIC PROTOTYPES **********************/ static void button_init(void); static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data); static int8_t button_get_pressed_id(void); static bool button_is_pressed(uint8_t id); /********************** *  STATIC VARIABLES **********************/ lv_indev_t * indev_button; static int32_t encoder_diff; static lv_indev_state_t encoder_state; /********************** *      MACROS **********************/ /********************** *   GLOBAL FUNCTIONS **********************/ void lv_port_indev_init(void) {     /**      * Here you will find example implementation of input devices supported by LittelvGL:      *  - Touchpad      *  - Mouse (with cursor support)      *  - Keypad (supports GUI usage only with key)      *  - Encoder (supports GUI usage only with: left, right, push)      *  - Button (external buttons to press points on the screen)      *      *  The `..._read()` function are only examples.      *  You should shape them according to your hardware      */     static lv_indev_drv_t indev_drv;     /*------------------      * Button      * -----------------*/     /*Initialize your button if you have*/     button_init();     /*Register a button input device*/     lv_indev_drv_init(&indev_drv);     indev_drv.type = LV_INDEV_TYPE_BUTTON;     indev_drv.read_cb = button_read;     indev_button = lv_indev_drv_register(&indev_drv);     /*Assign buttons to points on the screen*/     static const lv_point_t btn_points[2] = {         {60, 60},   /*Button 0 -> x:10; y:10*/         {180, 60},  /*Button 1 -> x:40; y:100*/     };     lv_indev_set_button_points(indev_button, btn_points); } /*------------------ * Button * -----------------*/ /*Initialize your buttons*/ static void button_init(void) {     /*Your code comes here*/ } /*Will be called by the library to read the button*/ static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) {     static uint8_t last_btn = 0;     /*Get the pressed button's ID*/     int8_t btn_act = button_get_pressed_id();     if(btn_act >= 0) {         data->state = LV_INDEV_STATE_PR;         last_btn = btn_act;     }     else {         data->state = LV_INDEV_STATE_REL;     }     /*Save the last pressed button's ID*/     data->btn_id = last_btn; } /*Get ID  (0, 1, 2 ..) of the pressed button*/ static int8_t button_get_pressed_id(void) {     uint8_t i;     /*Check to buttons see which is being pressed (assume there are 2 buttons)*/     for(i = 0; i < 2; i++) {         /*Return the pressed button's ID*/         if(button_is_pressed(i)) {             return i;         }     }     /*No button pressed*/     return -1; } #include "main.h" /*Test if `id` button is pressed or not*/ static bool button_is_pressed(uint8_t id) {     /*Your code comes here*/     if (id) return !HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin);     else return !HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin);     return false; } #else /*Enable this file at the top*/ /*This dummy typedef exists purely to silence -Wpedantic.*/ typedef int keep_pedantic_happy; #endif ``` 然后在 main.c 的初始化代码中加入 indev 的初始化 ```c   lv_init();   lv_port_disp_init();   lv_port_indev_init();   setup_ui(&guider_ui); ``` 编译烧录到开发板,可以看到操作按键修改速度值,modbus主机端也可以实时的监控到这个值 [localvideo]1dbe188ba2d29ca0d4708a1f75745f19[/localvideo]

  • 2024-03-05
  • 回复了主题帖: 【STM32F411Nucleo测评】移植 LVGL 界面库

    LitchiCheng 发表于 2024-3-5 09:04 刷新率怎么样,spi通信加上DMA大概可以到多少fps 有动画的时候FPS也可以稳定60,很丝滑

  • 加入了学习《follow me 四期项目提交视频》,观看 follow me 四期项目提交视频

  • 2024-03-04
  • 发表了主题帖: 【STM32F411Nucleo测评】移植 LVGL 界面库

    > STM32F411RE 有 512KB 的 Flash 和 128KB 的 RAM,资源丰富,本文介绍如何移植 LVGL 界面库到 Nucleo-F411 开发板上面 ## 资料获取 LVGL 是比较知名的嵌入式 GUI 库,代码在 GitHub 可以获得,地址 https://github.com/lvgl/lvgl ,这里移植 V8.2 版本,在拉取时使用如下命令,不直接拉取最新版,因为最新版是9.0版本的 ```bash git clone -b release/v8.2 https://github.com/lvgl/lvgl.git ``` 然后将 LVGL 除了别的平台的代码外全部加入 keil 工程中 ## 显示接口适配 LVGL的显示接口主要就是一个刷屏函数disp_flush,这里在之前文章的 LCD 完成刷屏的基础上,修改思路如下: 删除全屏缓存区,因为占用过多 RAM,改为选择显示空间后使用DMA传输LVGL的color参数 LVGL 的 disp_flush 接口代码如下 ```c static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) {     /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/     LCD_FillWindow(area->x1,area->y1,area->x2,area->y2, (uint16_t*)color_p);     /*IMPORTANT!!!      *Inform the graphics library that you are ready with the flushing*/     lv_disp_flush_ready(disp_drv); } void LCD_FillWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t *pixels) {     u16 i,j,width,height;     width = x1 - x0 + 1;     height = y1 - y0 + 1;     uint32_t size = width * height;     LCD_SetFrame(x0, y0, x1, y1);     hspi1.Init.DataSize = SPI_DATASIZE_16BIT;     hspi1.Instance->CR1|=SPI_CR1_DFF;     HAL_GPIO_WritePin(LCD_DC_GPIO_Port, LCD_DC_Pin, 1);     HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, 0);     HAL_SPI_Transmit_DMA(&hspi1,(uint8_t*)pixels, size);     while(__HAL_DMA_GET_COUNTER(&hdma_spi1_tx)!=0);     HAL_GPIO_WritePin(LCD_CS_GPIO_Port, LCD_CS_Pin, 1);     hspi1.Init.DataSize = SPI_DATASIZE_8BIT;     hspi1.Instance->CR1&=~SPI_CR1_DFF; } ``` 至此 LVGL 的刷屏接口就适配好了,其中为了加快执行的速度,部分代码直接操作寄存器来完成 ## 时钟接口适配 在 CubeMX 中初始化 TIM10 配置为1ms周期中断 在中断函数中调用 lv_tick_inc 函数,为LVGL提供时钟 ```c void TIM1_UP_TIM10_IRQHandler(void) {   /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 0 */   /* USER CODE END TIM1_UP_TIM10_IRQn 0 */   HAL_TIM_IRQHandler(&htim10);   /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */   lv_tick_inc(1);   /* USER CODE END TIM1_UP_TIM10_IRQn 1 */ } s ``` ## 主函数代码 最后主函数中调用 lv_init、lv_port_disp_init 和 lv_task_handler,即可完成 LVGL 的移植 ```c   lv_init();   lv_port_disp_init();   /* USER CODE END 2 */   /* Infinite loop */   /* USER CODE BEGIN WHILE */   while (1)   {     /* USER CODE END WHILE */     /* USER CODE BEGIN 3 */     lv_task_handler();   }   /* USER CODE END 3 */ ``` 单单有 LVGL 初始化不够的,还需要有一个界面来交给它来绘制,下面写段验证代码 ## 验证 代码如下,创建一个自定义风格的滑动条控件,百分比为 70%,长度为200,居中显示 ```c void foo(void) {     static lv_style_t style_indic;     lv_style_init(&style_indic);     lv_style_set_bg_color(&style_indic, lv_palette_lighten(LV_PALETTE_RED, 3));     lv_style_set_bg_grad_color(&style_indic, lv_palette_main(LV_PALETTE_RED));     lv_style_set_bg_grad_dir(&style_indic, LV_GRAD_DIR_HOR);     static lv_style_t style_indic_pr;     lv_style_init(&style_indic_pr);     lv_style_set_shadow_color(&style_indic_pr, lv_palette_main(LV_PALETTE_RED));     lv_style_set_shadow_width(&style_indic_pr, 10);     lv_style_set_shadow_spread(&style_indic_pr, 3);     /*Create an object with the new style_pr*/     lv_obj_t * obj = lv_slider_create(lv_scr_act());     lv_obj_add_style(obj, &style_indic, LV_PART_INDICATOR);     lv_obj_add_style(obj, &style_indic_pr, LV_PART_INDICATOR | LV_STATE_PRESSED);     lv_slider_set_value(obj, 70, LV_ANIM_OFF);     lv_obj_center(obj); } ``` 添加到主函数中。 ```c   lv_init();   lv_port_disp_init();   foo();   /* USER CODE END 2 */   /* Infinite loop */   /* USER CODE BEGIN WHILE */   while (1)   {     /* USER CODE END WHILE */     /* USER CODE BEGIN 3 */     lv_task_handler();   }   /* USER CODE END 3 */ ``` 编译烧录到开发板中,可以看到显示出了使用LVGL绘制的滑动条

  • 发表了主题帖: 【STM32F411Nucleo测评】移植 Freemodbus 库

    > 本文介绍如何移植 FreeModbus 库,在 Nucleo-F411 板上实现 Modbus 通信 ## 资料准备 FreeModbus 的代码是存放在 GitHub 的,地址 https://github.com/cwalter-at/freemodbus ,拉取到本地,其中关键的就是demo和modbus两个文件夹,demo存放一些移植好的工程,modbus中存放库代码。 ## 硬件连接 查看原理图的串口是接在STLink 的虚拟串口上面的,管脚为PA2和PA3 ## 接口配置 将拉取到的 FreeModbus 代码添加到 keil 工程中,其中需要添加库代码中的 functions、rtu 文件夹中的代码和 mb.c 此外还需要把demo中BARE文件夹的代码复制到工程中,这里同时将demo.c修改为了data.c添加到工程 添加的代码文件如下 需要的代码文件都添加好了,下面开始对 portxxx.c进行适配,其中 portserial.c 是串口的接口层, porttimer.c 是定时器的接口层。 配置 USART2,参数如下,设置波特率为115200bps 定时器TIM11周期设置为50us 同时将 USART2 和 TIM11 的中断使能 ### 串口接口适配 利用 HAL_UART_TxCpltCallback,HAL_UART_RxCpltCallback 两个HAL库的两个回调函数编写如下代码 ```c void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {     if(huart->Instance == uart->Instance)     {         pxMBFrameCBByteReceived();         HAL_UART_Receive_IT(uart, &singlechar, 1);     } } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {     if(huart->Instance == uart->Instance)     {         pxMBFrameCBTransmitterEmpty();     } } ``` 串口收发的接口代码如下 ```c /* ----------------------- Static variables ---------------------------------*/ UART_HandleTypeDef *uart = &huart2; static uint8_t singlechar; /* ----------------------- Start implementation -----------------------------*/ BOOL            xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,                                    UCHAR ucDataBits, eMBParity eParity ) {     return TRUE; } void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable) {     if(xRxEnable)     {         HAL_UART_Receive_IT(uart, &singlechar, 1);     }        else     {         HAL_UART_AbortReceive_IT(uart);     }     if(xTxEnable)     {         pxMBFrameCBTransmitterEmpty();     }     else     {         HAL_UART_AbortTransmit_IT(uart);     } } void vMBPortClose(void) {     HAL_UART_AbortReceive_IT(uart);     HAL_UART_AbortTransmit_IT(uart); } BOOL xMBPortSerialPutByte(CHAR ucByte) {     HAL_UART_Transmit_IT(uart, (uint8_t*)&ucByte, 1);     return TRUE; } BOOL xMBPortSerialPutBytes(volatile UCHAR *ucByte, USHORT usSize) {     HAL_UART_Transmit_IT(uart, (uint8_t *)ucByte, usSize);     return TRUE; } BOOL xMBPortSerialGetByte(CHAR * pucByte) {     *pucByte = (uint8_t)(singlechar);     return TRUE; } ``` ### 定时器接口适配 定时器使用 HAL_TIM_PeriodElapsedCallback 这个 HAL 提供的定时器周期中断时的回调函数 ```c void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {     if(htim->Instance == tim->Instance)     {         if((++counter) >= timeout)             pxMBPortCBTimerExpired();     } } ``` 其他的代码就是使能、失能 TIM 的一些接口代码 ```c /* ----------------------- User defenitions ---------------------------------*/ TIM_HandleTypeDef *tim = &htim11; static uint16_t timeout = 0; volatile uint16_t counter = 0; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) {     timeout = usTim1Timerout50us;     return TRUE; } inline void vMBPortTimersEnable(  ) {     counter=0;     HAL_TIM_Base_Start_IT(tim); } inline void vMBPortTimersDisable(  ) {     HAL_TIM_Base_Stop_IT(tim); } inline void vMBPortTimersDelay( USHORT usTimeOutMS ) {     HAL_Delay(usTimeOutMS); } ``` 至此接口层的代码就适配好了,代码量不大,还算比较容易,下面开始主函数编写应用代码来验证 ## 验证 将main函数中while前后的代码改为如下代码,先初始化modbus库然后使能,在while循环中轮询。 ```c   eMBInit(MB_RTU, 0x01, 0, 115200, MB_PAR_NONE); // 初始化modbus为RTU方式,波特率9600,奇校验   eMBEnable();                                   // 使能modbus协议栈   /* USER CODE END 2 */   /* Infinite loop */   /* USER CODE BEGIN WHILE */   while (1)   {     /* USER CODE END WHILE */     /* USER CODE BEGIN 3 */     eMBPoll();  // 轮训查询   }   /* USER CODE END 3 */ ``` 光有这些还是不够的,现在已经完成了通信过程的适配,还需要提供保持寄存器、线圈等数据源代码,在data.c中编写代码如下 ```c #include "mb.h" #include "mbport.h" // 十路输入寄存器 #define REG_INPUT_SIZE  10 uint16_t REG_INPUT_BUF[REG_INPUT_SIZE]; // 十路保持寄存器 #define REG_HOLD_SIZE   10 uint16_t REG_HOLD_BUF[REG_HOLD_SIZE]; // 十路线圈 #define REG_COILS_SIZE 10 uint8_t REG_COILS_BUF[REG_COILS_SIZE] = {1, 1, 1, 1, 0, 0, 0, 0, 1, 1}; // 十路离散量 #define REG_DISC_SIZE  10 uint8_t REG_DISC_BUF[REG_DISC_SIZE] = {1,1,1,1,0,0,0,0,1,1}; /// CMD4命令处理回调函数 eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) {     USHORT usRegIndex = usAddress - 1;     // 非法检测     if((usRegIndex + usNRegs) > REG_INPUT_SIZE)     {         return MB_ENOREG;     }     // 循环读取     while( usNRegs > 0 )     {         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );         *pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );         usRegIndex++;         usNRegs--;     }     // 模拟输入寄存器被改变     for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)     {         REG_INPUT_BUF[usRegIndex]++;     }     return MB_ENOERR; } /// CMD6、3、16命令处理回调函数 eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) {     USHORT usRegIndex = usAddress - 1;     // 非法检测     if((usRegIndex + usNRegs) > REG_HOLD_SIZE)     {         return MB_ENOREG;     }     // 写寄存器     if(eMode == MB_REG_WRITE)     {         while( usNRegs > 0 )         {             REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] 0 )         {             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );             *pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );             usRegIndex++;             usNRegs--;         }     }     return MB_ENOERR; } /// CMD1、5、15命令处理回调函数 eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) {     USHORT usRegIndex   = usAddress - 1;     UCHAR  ucBits       = 0;     UCHAR  ucState      = 0;     UCHAR  ucLoops      = 0;     // 非法检测     if((usRegIndex + usNCoils) > REG_COILS_SIZE)     {         return MB_ENOREG;     }     if(eMode == MB_REG_WRITE)     {         ucLoops = (usNCoils - 1) / 8 + 1;         while(ucLoops != 0)         {             ucState = *pucRegBuffer++;             ucBits  = 0;             while(usNCoils != 0 && ucBits < 8)             {                 REG_COILS_BUF[usRegIndex++] = (ucState >> ucBits) & 0X01;                 usNCoils--;                 ucBits++;             }             ucLoops--;         }     }     else     {         ucLoops = (usNCoils - 1) / 8 + 1;         while(ucLoops != 0)         {             ucState = 0;             ucBits  = 0;             while(usNCoils != 0 && ucBits < 8)             {                 if(REG_COILS_BUF[usRegIndex])                 {                     ucState |= (1 REG_DISC_SIZE)     {         return MB_ENOREG;     }     ucLoops = (usNDiscrete - 1) / 8 + 1;     while(ucLoops != 0)     {         ucState = 0;         ucBits  = 0;         while(usNDiscrete != 0 && ucBits < 8)         {             if(REG_DISC_BUF[usRegIndex])             {                 ucState |= (1

统计信息

已有116人来访过

  • 芯积分:144
  • 好友:--
  • 主题:39
  • 回复:24

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言