LED闪烁灯
交流QQ: 1048272975 QQ交流群: 636564526
控制LED灯的亮灭是MCU开发中一个最简单的应用功能,实现这个应用功能包含了MCU开发中工程的构建、编译的过程、下载烧录的方式、开机运行的流程等等内容。
1. 开发工具链
针对ESP32开发,乐鑫官方提供了ESP-IDF框架以及对应的开发工具链,开发环境的搭建可以参考上一章节的内容。作为一个LED灯控制的简单程序,只需要基于Xtensa架构的GNU交叉编译工具的支持。
2. LED闪烁灯程序
以通用的main函数作为程序的入口,通过控制ESP32 GPIO输出高低电平来控制LED灯的亮灭。
2.1. 关闭看门狗
参考ESP-IDF目录components/bootloader裸机引导程序的实现流程以及ESP32芯片数据手册,可以知道Flash启动时使能了主系统看门狗定时器MWDT和RTC看门狗定时器RWDT,程序应加入关闭看门狗的代码。
设置TIMGn_Tx_WDTCONFIG0_REG寄存器的TIMGn_Tx_WDT_FLASHBOOT_MOD_EN(位14)为0即可关闭主系统看门狗定时器MWDT。
主系统看门狗MWDT配置寄存器
设置RTC_CNTL_WDTCONFIG0_REG寄存器的RTC_CNTL_WDT_FLASHBOOT_MOD_EN (位10)为0即可关闭RTC看门狗定时器RWDT。
RTC看门狗RWDT配置寄存器
2.2. 初始化GPIO
通过设置GPIO交换矩阵配置寄存器GPIO_FUNCn_OUT_SEL_CFG_REG的输出选择为256,使对应的GPIO n引脚为GPIO输出功能。通过设置GPIO输出使能置位寄存器PIO_ENABLE_W1TS_REG对应n位为1,使能GPIO n的输出。通过设置GPIO输出置位寄存器GPIO_OUT_W1TS_REG对应n位为1, GPIO n输出高电平,通过设置GPIO输出清零寄存器GPIO_OUT_W1TC_REG对应n位为1, GPIO n输出低电平。
GPIO交换矩阵输出信
2.3. 循环闪烁
在while循环里面控制GPIO输出低电平,然后软件延时,再输出高电平,软件延时,如此循环,实现LED灯的闪烁。
2.4. c代码
LED闪烁灯项目路径为D:esp32led,完整的LED闪烁灯代码led.c如下:
3. 编译运行
ESP32使用Xtensa架构的GNU交叉编译工具,可以使用通用的GUN工具命令编译代码,链接生成可执行代码,也可以生成相应的输出文件,如map文件、反汇编等等用于分析调试。
3.1. 编译
用gcc命令编译led.c,输出led.o文件。
xtensa-esp32-elf-gcc.exe -Os -c led.c -o led.o
代码编译
3.2. 链接
编译生成目标文件后,需要通过链接器链接为一个可执行文件,可以通过链接脚本控制链接过程。链接脚本可以指定程序的入口、各个输入段的内存布局等等,其有特定的语法格式。可以参考ESP-IDF目录componentsbootloadersubprojectmainldesp32下bootloader.ld文件,实现ESP32裸机程序的链接脚本。
LED闪烁灯项目的链接脚本led.ld实现如下:
链接脚本指定了程序的入口为main,代码段.text加载到0x40078000地址的内存区域执行。由于LED闪烁灯程序比较简单,只有代码段.text以及字面量段.literal,没有全局变量静态变量.bss段和.data段、常数.rodata段,可以不指定未使用段的内存布局。
用ld命令链接目标文件,生成可执行文件led.elf。用-T指定led.ld链接脚本,用-Map指定输出map文件,可以了解程序、数据、IO空间等等的映射关系。
xtensa-esp32-elf-ld.exe -Map led.map -T led.ld led.o -o led.elf
led.map文件
3.3. 反汇编
链接生成可执行elf文件后,可以提取可执行段并生成反汇编代码,用于代码优化、调试等分析。
用objdump命令反汇编led.elf,输出led.dis反汇编文件。
xtensa-esp32-elf-objdump.exe -d led.elf > led.dis
led.dis反汇编代码
ESP32为Xtensa架构,该架构具有很强的可重构性和可拓展性,允许自定义全新指令,用于硬件加速,可提升处理器的运算性能同时又便于软件实现控制。详细的汇编指令可参考Xtensa指令集体系结构(ISA)数据手册。
从反汇编代码可以看出,函数总是以entry指令开头,该指令主要实现两个功能:
1、为函数分配堆栈帧,计算并设置函数的栈指针。
2、根据函数调用指令calln/callxn,将寄存器窗口移动/旋转n个物理寄存器。
例如entry a1, 32,表示为函数分配32个字长的堆栈帧,并计算设置函数栈指针a1的值。当使用call8调用该函数时,寄存器窗口移动/旋转8个物理寄存器。
针对函数的调用,Xtensa架构设计了一种窗口旋转方式的寄存器管理机制,将逻辑寄存器和物理寄存器分开。对于ESP32,有16个逻辑寄存器(指令中的a0~a15),有64个环形物理寄存器,通过WindowBase寄存器,使逻辑寄存器可滑动对应实际的物理寄存器。从而避免寄存器覆盖,减少入栈和出栈的操作。
这一设计机制可让嵌入式软件性能得到明显提高。这是因为使用函数时,往往需要入栈,保存函数使用前的寄存器现场,函数结束时,需要出栈,恢复之前的寄存器现场后返回。入栈和出栈将对外部内存进行读写,而外部内存对于高性能CPU来说是慢速设备,需打断流水线等待访问完成,因此频繁访问外部内存将极大地影响性能。通常高性能的CPU,如Cortex-A系列ARM核,通过增加一级或二级Cache来降低CPU读写等待内存的几率。而Xtensa架构直接减少了函数调用时的入栈和出栈,降低了内存的访问,更大限度地提升了性能。
环形物理寄存器
3.4. 烧录文件
链接生成的可执行文件为elf格式,该文件保存了二进制可执行代码,适用于Unix类系统环境。ESP32无法直接运行elf代
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!