zght

  • 2024-03-24
  • 发表了主题帖: 【极海APM32F407 Tiny Board】 5.基于LWIP的IAP升级

    # 1        LWIP ## 1.1        LWIP简介 LwIP 是一款轻量化的 TCP/IP 协议,是瑞典计算机科学院(SICS)的 AdamDunkels 开发的一个小型开源的 TCP/IP 协议栈。在保持 TCP 协议主要功能的基础上减少对资源的占用。此外 LwIP 既可以移植到操作系统上运行,也可以在无操作系统的情况下独立运行。 可以在网页http://savannah.nongnu.org/projects/lwip/, 下载获取到 LwIP 的各个版本的源代码包和对应的contrib 包。 本次计划基于上一篇LWIP移植的基础上实现LWIP网络传输固件进行升级。 ## 1.2        硬件设计 开发板使用 APM32F407 控制器通过 RMII 接口和 SMI 接口与 LAN8720A 以太网 PHY 进行连接。通过接下拉电阻把 nINTSEL 引脚设置为低电平,从而使能 nINT/REFCLKO 引脚的输出功能为RMII 接口中 REF_CLK 信号线提供时钟信号, 硬件上 XTAL1 与 XTAL2 之间接入提供 25MHz时钟,经过 LAN8720A 内部 PLL 电路陪频后使得 nINT/REFCLKO 引脚的输出的时钟信号为50MHz 时钟。 # 2        移植步骤 ## 2.1        Bootloader配置 本次由于在bootloader程序中添加了lwip的代码,需要将bootloader的大小调整一下,初步编译了一下,大概占用30k左右的样子,这里进行试验就调整大一点,200k 应用程序配置,只需要调整APP的起始地址即可。 同时应用APP程序中只需要一个简单的例子即可,2s闪烁一次LED 接下来在bootloader建立TcpServer服务器,端口设置为80,代码基于lwip移植和串口IAP升级的工程合并而成。 Lwip接收数据回调函数,连接收向服务器发送消息证明建立通讯 接下来就是接收数据后的回调函数,先将接收的数据返回发送到服务器 实现Tcpserver这个功能后在实现代码升级的部分,然后初始化函数放在主函数中初始化。 打开网络测试工具,先ping一下,ping正常 接下来点击打开TCP,可以发现lwip Tcpserver connect  这串字符上传,这是代码里面写的,说明成功,接下来向服务器发送数据11,返回11。说明功能已正常使用 ## 2.2        网络固件传输调整 由于最开始使用的串口升级的的方式,现在需要使用lwip实现,从串口IAP升级中将串口接收的数据固定存放在一个区域就可以,lwip升级同样也是。通过对lwip接收到的数据存放在我们的app段地址,通过按键去启动。 可以同样使用串口的缓冲,将lwip接收的数据存放在串口升级的缓冲区,首先找到lwip接收数据的接口tatic err_t tcp1_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err)。其lwip接收到的数据存放在tcp_recv_pbuf这个结构体中。 将接收到数据指针指向我们的串口接收缓冲区,同时拿出接收的长度 串口缓冲区数据存放地址 在主循环中通过检测按键进行代码升级和运行APP。检测到lwip接收的数据不为0,将接收的数据写入到指定区域。 # 3        验证 打开网络工具,本次使用野火的上位机,可以发文件,点击网络助手,配置协议类型为TCP Client,以及开发板的地址,端口,点击建立连接 接下来点击右下角的加载文件,选择APP代码生成的bin文件,如下所示 点击发送数据,接下来就可以通过串口看到输出消息,按下按键,执行用户程序。 正确执行升级后的代码,LED灯按APP的代码执行。

  • 2024-03-17
  • 发表了主题帖: 【极海APM32F407 Tiny Board】 4.LWIP移植

    # 1        LWIP ## 1.1        LWIP简介 LwIP 是一款轻量化的 TCP/IP 协议,是瑞典计算机科学院(SICS)的 AdamDunkels 开发的一个小型开源的 TCP/IP 协议栈。在保持 TCP 协议主要功能的基础上减少对资源的占用。此外 LwIP 既可以移植到操作系统上运行,也可以在无操作系统的情况下独立运行。 可以在网页http://savannah.nongnu.org/projects/lwip/, 下载获取到 LwIP 的各个版本的源代码包和对应的contrib 包。 本次的移植目的是为了实现LWIP进行代码升级,因此本篇文章先实现底层功能,方便后续操作。 ## 1.2        硬件设计 开发板使用 APM32F407 控制器通过 RMII 接口和 SMI 接口与 LAN8720A 以太网 PHY 进行连接。通过接下拉电阻把 nINTSEL 引脚设置为低电平,从而使能 nINT/REFCLKO 引脚的输出功能为RMII 接口中 REF_CLK 信号线提供时钟信号, 硬件上 XTAL1 与 XTAL2 之间接入提供 25MHz时钟,经过 LAN8720A 内部 PLL 电路陪频后使得 nINT/REFCLKO 引脚的输出的时钟信号为50MHz 时钟。 # 2        移植步骤 ## 2.1        底层驱动添加 这是官方提供的ETH驱动库,标准库中没有添加 添加完如下所示: 添加LAN8720外设驱动,完成以太网的GPIO,时钟等配置,也相当于一个外设驱动文件。 ## 2.2        添加Lwip源码 找到lwip-1.4.1\src\api文件,全部添加 lwip-1.4.1\src\core\ipv4下文件全部添加: lwip-1.4.1\src\core下除了ipv6以及snmp文件夹,其他源码全部添加: lwip-1.4.1\src\netif下添加,etharp.c文件: 添加头文件路径,当然中间步骤太多,因此部分省略 中间配置网卡等步骤省略,主要说明部分配置文件及实现逻辑,网络配置: Lwip初始化,主要设置设置的IP地址,网关等 主程序,网络配置初始化,通过串口打印网络配置信息: 程序验证lwip是否移植成功,主要通过电脑进行ping 接下来配置电脑网络的相关配置: # 3        验证 接下来打开电脑的CMD,ping一下开发板的ip,如下所示,lwip移植成功,文章是移植完后才写的,中间很多步骤省略。

  • 2024-03-16
  • 发表了主题帖: 【极海APM32F407 Tiny Board】 3.IAP升级

    # 1        IAP ## 1.1        IAP简介 IAP(In Application Programming)即在应用编程。是用户自己的程序在运行过程中对User Flash的部分区域进行烧写。实现代码升级,可通过UART、SPI、IIC、USB等等传递需要升级的固件。实现即将flash或者其他存储器分成多个段,存储升级的代码,一般设计分为两个部分,第一部分用于实现通讯升级代码,叫bootloader,第二部分为应用程序,即正常运行的代码。 ## 1.2        启动流程 程序启动后,先从 0x08000004 处取出复位中断向量地址,执行完复位中断函数后跳转到IAP 程序 main 函数中执行[①]。 当发生中断请求后,程序跳转到中断向量表中取出中断函数入口地址,再跳转到中断服务函数中执行[②],执行完中断函数后返回 main 函数中[③],然后执行 IAP 过程,成功后跳转到 APP程序[④]。 从偏移后的中断向量表得到相应中断函数地址,执行相应新的中断服务函数后,回到 APP的 main 函数中[⑤]。后面[⑥⑦⑧]的过程和前述一致。 ## 1.3        中断向量表设置 APM32F4xxSDK 的 system_apm32f4xx.c 文件中可以找到 VECT_TAB_OFFSET 这个向量表偏移量宏定义来重新设置中断向量表的地址,也即是修改 SCB->VTOR 向量表偏移量寄存器。 # 2        移植步骤 ## 2.1        Bootlaoder设计 Flash IAP BootLoader的起始地址为0x08000000,通过串口1接收升级固件,使用64k的空间用于存储bootloader,因此应用程序的地址为0x0801000。 首先是bootloader程序,配置按键,led,串口等相关外设 设置工程文件的ROM的起始地址为0x8000000,大小为64k 代码中初始化串口1用来进行应用程序的接收,同时打印状态信息。 ## 2.2        App程序设计 APP 是主用户程序,在完成 IAP 程序的设计后,就要把 APP 的更新文件生成,然后通过一定的协议传输给 IAP 程序来进行 APP 固件的更新。 一般来说 APP 更新文件的文件类型为.bin 文件,该文件可以直接拷贝到 flash 中运行。 设置工程文件的起始地址为0X8010000 在 Keil MDK Option 配置的 User 选项卡中配置以下命令, 即可使用 fromelf.exe 生成 bin 文件,默认生成在工程目录。 准备好APP应用程序,这里使用之前的工程文件,把其他外设都关了,就留一个LED灯进行验证,2s闪烁进行区分。 # 3        验证 接下来打开终端,使用串口工具选择APP程序的bin文件 加载然后进行发送: 应用程序开始执行,LED灯2s的周期开始闪烁。

  • 2024-03-03
  • 发表了主题帖: 【极海APM32F407 Tiny Board】 2.LVGL8.2移植体验

    一、移植准备 下载LVGL源码:https://github.com/lvgl/lvgl/,解压后,文档如下所示: 将lv_conf_template.h改成lv_conf.h,命令可以随意更改,一个配置文件,同时打开文件,将#if0调整为# if 1,不然编译的回收会出很多错。 接下来删除一下文件,也可以不删除,删除了方便管理 二、移植过程 在之前的基础上添加lvgl的源码,文件比较多,不一一说明,同时这里添加的时候可以进行分类添加,这样就没有这么乱,查找也方便 添加头文件路径: 添加定时器初始化,定时1ms,,添加lvgl头文件 添加LVGL与LCD的接口处理: /** * [url=home.php?mod=space&uid=1307177]@File[/url] lv_port_disp_templ.c * */ /*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/ #if 1 /********************* * INCLUDES *********************/ #include "lv_port_disp_template.h" #include "../../lvgl.h" #include "./BSP/LCD/lcd.h" #include "gui.h" #include "lv_demo_keypad_encoder.h" #define MY_DISP_HOR_RES (320) #define MY_DISP_VER_RES (240) /********************* * DEFINES *********************/ /********************** * TYPEDEFS **********************/ /********************** * STATIC PROTOTYPES **********************/ static void disp_init(void); static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p); //static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, // const lv_area_t * fill_area, lv_color_t color); /********************** * STATIC VARIABLES **********************/ /********************** * MACROS **********************/ /********************** * GLOBAL FUNCTIONS **********************/ void lv_port_disp_init(void) { /*------------------------- * Initialize your display * -----------------------*/ disp_init(); /*----------------------------- * Create a buffer for drawing *----------------------------*/ /** * LVGL requires a buffer where it internally draws the widgets. * Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display. * The buffer has to be greater than 1 display row * * There are 3 buffering configurations: * 1. Create ONE buffer: * LVGL will draw the display's content here and writes it to your display * * 2. Create TWO buffer: * LVGL will draw the display's content to a buffer and writes it your display. * You should use DMA to write the buffer's content to the display. * It will enable LVGL to draw the next part of the screen to the other buffer while * the data is being sent form the first buffer. It makes rendering and flushing parallel. * * 3. Double buffering * Set 2 screens sized buffers and set disp_drv.full_refresh = 1. * This way LVGL will always provide the whole rendered screen in `flush_cb` * and you only need to change the frame buffer's address. */ /* Example for 1) */ static lv_disp_draw_buf_t draw_buf_dsc_1; static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ /* Example for 2) */ // static lv_disp_draw_buf_t draw_buf_dsc_2; // static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/ // static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/ // lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/ /* Example for 3) also set disp_drv.full_refresh = 1 below*/ // static lv_disp_draw_buf_t draw_buf_dsc_3; // static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/ // static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/ // lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX); /*Initialize the display buffer*/ /*----------------------------------- * Register the display in LVGL *----------------------------------*/ static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/ lv_disp_drv_init(&disp_drv); /*Basic initialization*/ /*Set up the functions to access to your display*/ /*Set the resolution of the display*/ disp_drv.hor_res = lcddev.width; disp_drv.ver_res = lcddev.height; /*Used to copy the buffer's content to the display*/ disp_drv.flush_cb = disp_flush; /*Set a display buffer*/ disp_drv.draw_buf = &draw_buf_dsc_1; /*Required for Example 3)*/ //disp_drv.full_refresh = 1 /* Fill a memory array with a color if you have GPU. * Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL. * But if you have a different GPU you can use with this callback.*/ //disp_drv.gpu_fill_cb = gpu_fill; /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); } /********************** * STATIC FUNCTIONS **********************/ /*Initialize your display and the required peripherals.*/ static void disp_init(void) { /*You code here*/ LCD_Init(); LCD_direction(1); } /*Flush the content of the internal buffer the specific area on the display *You can use DMA or any hardware acceleration to do this operation in the background but *'lv_disp_flush_ready()' has to be called when finished.*/ 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_color_fill(area->x1,area->y1,area->x2,area->y2,color_p); // LCD_Fill(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); } /*OPTIONAL: GPU INTERFACE*/ /*If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color*/ //static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width, // const lv_area_t * fill_area, lv_color_t color) //{ // /*It's an example code which should be done by your GPU*/ // int32_t x, y; // dest_buf += dest_width * fill_area->y1; /*Go to the first line*/ // // for(y = fill_area->y1; y <= fill_area->y2; y++) { // for(x = fill_area->x1; x <= fill_area->x2; x++) { // dest_buf[x] = color; // } // dest_buf+=dest_width; /*Go to the next line*/ // } //} #else /*Enable this file at the top*/ /*This dummy typedef exists purely to silence -Wpedantic.*/ typedef int keep_pedantic_happy; #endif 修改LCD的颜色填充函数: void lcd_color_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, lv_color_t *color) { uint32_t y=0; u16 height, width; width = ex - sx + 1; height = ey - sy + 1; LCD_SetWindows(sx,sy,ex,ey); for(y = 0; y <width*height; y++) { Lcd_WriteData_16Bit(color->full); color++; } }   添加测试代码,如下所示: int main(void) { NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3); sys_apm32_clock_init(336, 8, 2, 7); delay_init(168); usart_init(115200); btmr_tmrx_int_init(10-1, 8400-1); led_init(); lv_init(); lv_port_disp_init(); lv_obj_t *label = lv_label_create(lv_scr_act()); lv_label_set_text(label,"APM32F407 LVGL8.2"); lv_obj_center(label); #if 0 nes_main(); #endif while (1) { lv_timer_handler(); LED0_TOGGLE(); delay_ms(500); } }   三、结果 显示效果如下,有兴趣的可以继续跑一下官方例程,或者自己写一个

  • 2024-02-19
  • 发表了主题帖: 【极海APM32F407 Tiny Board】 1.NES模拟器

    NES模拟器 一、NES简介 NES是“Nintendo Entertainment System”的缩写,它是任天堂公司在1980年代推出的一款家用游戏机。在日本,它被称为Famicom(Family Computer)。NES于1983年在日本首次推出,随后于1985年在北美地区发行,成为当时家庭游戏市场的主导力量。 这款游戏机因其丰富的游戏库、经典的游戏系列以及在家庭游戏市场的影响力而备受推崇。它引入了许多经典游戏,如《马里奥兄弟》(Super Mario Bros.)、《塞尔达传说》(The Legend of Zelda)、《魂斗罗》(Contra)和《魔界村》(Castlevania)等,这些游戏至今仍然被认为是游戏史上的经典之作。 二、移植准备 2.1 资料下载 首先下载相关资料,函数库,以及一些示例,电路原理图等,如下所示 本次使用的是RTT进行开发,需要下载板级支持包 同时将PYOCD更新到最新版本,不然可能存在下载失败的情况 接下来新建RTT工程,如下所示,主函数1s打印一次 使用外接串口与开发板的串口1连接,打开终端,可以看到1s打印1次 由于打印信息不方便观察,且有时候需要用终端,这里将打印信息调整为板载的LED灯,类似于心跳灯。查看板载的LED灯原理图,如下所示: 添加心跳灯代码,使用GET_PIN函数时需要包含drv_common.h头文件,不然编译会报错。 在使用RTT开发过程中遇到很多坑,于是就放弃了使用RTT开发,果断入手标准库进行开发,时间小于用RTT开发。本次使用一个SPI的例程进行修改调整,添加spi,LCD,以及NES模拟器代码,由于使用RTT花了太多时间,过程就不在详细介绍,步骤也比较多。 2.2 SPI驱动 本次使用的是SPI1与ili9341进行连接,配置如下: 读写函数: 2.3 LCD显示实现 添加LCD源码,添加LCD路径,替换头文件等 LCD引脚配置: 接下来就是写数据与写命令函数的替换,将内部函数调整为SPI的读写函数: 三、移植过程 NES模拟器移植这里不详细进行介绍,添加源码,头文件路径 调整NES模拟器显示内容: 主函数,添加nes头文件,以及执行函数: 四、效果 [localvideo]ed24c744f62b7b3cb4cd60c43e8c97cc[/localvideo]  

  • 2024-02-04
  • 回复了主题帖: 测评入围名单(最后一批):年终回炉,FPGA、AI、高性能MCU、书籍等42个测品

    信息确认无误,感谢管理员

最近访客

< 1/1 >

统计信息

已有12人来访过

  • 芯积分:72
  • 好友:--
  • 主题:5
  • 回复:2

留言

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


现在还没有留言