20200928会议要点总结
遇到困难,解决困难
每遇到一个问题,就放弃一个问题,就永远不会成长,遇到一个问题,解决一个问题,能力就会提升一步。
编写程序不能有警告
有些警告可能会影响程序的运行,编写程序要做到警告尽可能的少,最好是没有警告,在实验室IDE和官方IDE下都没有警告。
每遇到一个问题,就放弃一个问题,就永远不会成长,遇到一个问题,解决一个问题,能力就会提升一步。
有些警告可能会影响程序的运行,编写程序要做到警告尽可能的少,最好是没有警告,在实验室IDE和官方IDE下都没有警告。
出现问题的原因:串口接收事件或串口失效,需要重新打开一次
已采取的方法:程序烧录之后对串口显示区时间重新绑定一次
结果:未完全解决
后续采取的方法:烧录之后重新绑定一次串口
1.设备连接流程
① 一些变量的声明:
(1) 返回值:用于标识寻找串口的结果(ret)
0-成功找到设备
1-未找到串口
2-找到串口,但是未找到设备
(2) 串口信息:用于标识设备的串口信息(com)
找到设备—返回目标设备信息
未找到设备—返回计算机的所有可用com
C#–out关键字:通常一个方法只能返回一个值,但是如果在某些时候,我们想要返回多个值, 例如某个方法将一个浮点数分割成一个整数和一个小数返回去。这个时候我们就要用到out关键 字。如果用ref也可以解决,但是用ref需要在初始化的时候虚设一个值,并且还要给虚设值赋初始 值。
(3)
(4) 保存串口接收信息
(5) 与终端握手帧数据
2.清除一些可能的余留信息
(1) 右上角显示区
(2) 底部提示
(3) 左侧代码显示区
(4) 右侧更新提示区
(5) 刷新显示
(6) 更改显示文字
3.重新遍历串口,寻找终端
4.根据寻找串口的返回值确定执行下列分支
(1) 连接终端的emuart失败(不存在可用串口):
禁用相关按钮
修改相关显示信息
关闭串口
返回
(2) 存在串口,但不存在emuart设备:
让终端设备情况数据缓存区
修改相关显示信息
关闭串口
返回
(3) 找到串口与UE:
向设备发送握手帧(若未收到数据,转向(4))
修改相关显示信息
允许相关按钮使用
清空接收数据缓冲区
获得设备的信息放在recv中
解析握手帧数据(有新/旧版本握手帧)
设置设备信息
修改相关显示信息
有串口信息,则增加接收串口事件
(4) 未收到返回信息(与(2)类似):
让终端设备情况数据缓存区
修改相关显示信息
关闭串口
返回
1.1 指令概念
指令是指指令系统的各条指令,每一条指令在源程序汇编时都要产生可供计算机执行的指令代码(即目标代码),每一条指令语句是表示计算机具有的一个基本能力。
1.2 伪指令概念
伪指令是用于对汇编过程进行控制的指令,该类指令并不是可执行指令,没有机器代码,只用于汇编过程中为汇编程序提供汇编信息。例如,提供如下信息:哪些是指令、哪些是数据及数据的字长、程序的起始地址和结束地址等。伪指令有2个特点:
(1)由于是伪“指令”,因而它只存在于汇编语言中。高级语言中不叫指令,叫语句;
(2)由于是“伪”指令,也即“假”指令,因而不是可执行指令,不会产生机器代码,不会占用ROM空间,只用于汇编过程中为汇编程序提供汇编信息。
1.3 常用指令总结
在嵌入式开发中,汇编程序常常用于非常关键的地方,比如系统启动时初始化,进出中断时的环境保护,恢复等对性能有要求的地方。
ARM指令集可分为六大类,分别是数据处理指令、Load/Store指令、跳转指令、程序状态寄存器处理指令、协处理器指令和异常产生指令。
ARM的寻址方式可分为立即寻址、寄存器寻址、寄存器间接寻址、基址加偏址寻址、堆栈寻址、块拷贝寻址、相对寻址。
1.3.1 相对跳转指令:B、BL
(1)B与BL指令的作用是什么?
B与BL指令的作用: 实现程序跳转,也就是调用子程序。
(2)B与BL指令的区别是什么?
B指令: 简单的程序跳转,跳转到目标号处执行。
BL指令:带链接程序跳转,也就是要带返回地址。在发生跳转前,将当前PC-4保存到R14中。
也就是将返回地址存在R14中,所以可以在子程序返回时只要MOV PC,LR即可
(3)B{条件} <地址>
B—Branch
1 | b funa |
(4)BL{条件}<地址>
BL—Branch withLink
(5)BX{条件}<地址>
BX—Branch and eXchange_Instructions(计算机中一般不用E,例如执行权限EXE,用X代表)
BX用法:
BX Rn
BX是跳转指令,Rn是寄存器,如果Rn的位0为1,则进入Thumb状态;如果Rn的位为0,这进入ARM状态。(原因:ARM指令的后两位始终为0,而Thumb指令的后一位始终为0,因此采用位0来表示ARM指令与Thumb指令的切换标志位。)
1、ARM状态:32位,ARM状态执行字对齐的32位ARM指令。
2、Thumb状态,16位,执行半字对齐的16位指令。
3、用BX Rn指令来进行两种状态的切换:
注:1、ARM和Thumb两种状态之间的切换不影响处理器的工作模式和寄存器的内容。
2、ARM处理器在处理异常时,不管处理器处于什么状态,则都将切换到ARM状态。
1.3.2 数据传送指令mov,地址读取伪指令ldr
mov指令可以把一个寄存器的值赋给另外一个寄存器,或者把一个常数赋给寄存器。
mov r1, r2
mov r1, #1024
mov传送的常数必须能用立即数表示。当不能用立即数表示时,可以用ldr命令来赋值。Tips:ldr时伪命令,不是真实存在的指令,编译器会把他扩展成真正的指令;如果该常数能用“立即数”来表示,则使用mov指令,否则编译时将该常数保存在某个位置,使用内存读取指令把它读出来。
1.3.3 内存访问指令ldr、str、ldm、stm
1.3.4 加减指令
add r1, r2, #1
sub r1, r2, #1
1.3.5 程序状态寄存器的访问指令msr,mrs
1.3.6 异常中断指令
1.3.7 其他伪指令
(1).extern main
‘.extern’定义一个外部符号(可以是变量也可以是函数),上面的代码表示文本文件中引用的main是一个外部函数。
(2).text
‘.text’表示下面的语句都属于代码段
(3).global _start
_start:
‘.global’将本文件中的某个程序标号定义为全局的,如’_start’就是个全局函数
当我们编写了多个C文件时,我们需要将它们编译链接成一个可执行的文件xxx.exe,此时就需要用到链接文件(.ld)。链接文件的主要功能就是:将多个目标文件(.o)和库文件(.a)链接成一个可执行文件。
链接脚本文件主要有什么内容?
链接配置(可不配置)
如一些符号变量的定义、入口地址、输出格式等
1 | STACK_SIZE = 0X200 |
内存布局定义
链接文件中以MEMORY命令定义了存储空间,其中以ORIGIN定义地址空间的起始地址,LENGTH定义地址空间的长度
1 | MEMORY |
段链接定义
链接文件中以SECTIONS命令定义一些段(text、data、bss段等)链接分布
1 | SECTIONS |
.text段即代码段, *(.text*)指示将工程中所有目标文件的.text段链接到FLASH中
MEMORY命令
1.1 使用MEMORY来定义内存如下:
1 | MEMORY |
1.2 参数介绍
NAME : 存储区域的名字 。(命名无要求)
ATTR :定义改存储区域的属性(权限)。ATTR属性内可以出现以下7个字符(可出现多个):
· R 只读section
· W 读/写section
· X 可执行section
· A 可分配的section
· I 初始化了的section
· L 与I相同
· ! 除该字符以外的任何一个属性的section
ORIGIN(关键字) : 区域的开始地址,可简写成org或o
LENGTH(关键字) : 区域的大小,可简写成len或l
1.3 KL36——BIOS示例:
1 | /* 定义内存区域的起始地址和长度 */ |
SECTIONS命令
2.1 SECTIONS基本的命令语法如下
1 | SECTIONS |
2.2 参数解析
链接脚本本质就是描述输入和输出。
secname表示输出文件的段,即输出文件中有那些段。
contents描述输出文件的这个段从哪些文件里抽取出来,即输入文件,一般就是目标文件。
2.3 示例
如下,将目标文件的数据段链接到输出文件的数据段(段名可以自己定义,段名前后必须要有空格)
1 | SECTIONS |
其中 *(.data) 表示将所有的目标的.data段链接到输出文件.data段中, 特别注意的是,之前链接的就不会再链接,这样做的目的是可以将某些特殊的目标文件链接到地址前面。
2.4 其他参数的介绍
· start: 表示将某个段强制链接到的地址
· AT(addr) : 实现存放地址和加载地址不一致的功能,AT表示在文件中存放的位置,而在内存里呢,按照普通方式存储。
· region:这个region就是前面说的MEMORY命令定义的位置信息。
2.5 KL36——BIOS示例
1 | /* Define output sections */ |
各关键字
3.1 定位符号’.’的使用
‘.’表示当前地址,它可以被赋值也可以赋值给某个变量。
如下为将当前地址赋值给某个变量(链接器链接是按照SECTIONS里的段顺序排列的,前面的排列完之后就能计算出当前地址)—我们可以看到上述SECTIONS中频繁出现 . = ALIGN(x)或者 xxx = .;那么这个.的地址是会不断更新的。比如上一段.的地址 为 0x80,上一段大小有20,那么下一段的时候.的地址则为0x100
1 | /*将.赋值给RAM_START*/ |
“. = 0×10000;”该语句表示将当前地址设置为0x10000。如上代码中,意思是将所有目标文件的text段从0x10000地址开始存放。
3.2 PROVIDE关键字
该关键字定义一个(目标文件内被引用但没定义)符号。相当于定义一个全局变量的符号表,其他C文件可以通过该符号来操作对应的存储内存。
1 | SECTIONS |
如上,在链接脚本中声明了_etext 符号。特别注意的是_etext 只是一个符号,没有存储内存,并不是一个变量,该符对应(映射)的是一个地址,,其地址为.text section之后的第一个字节的地址。C文件中引用用法如下。
1 | int main() |
若在链接脚本中 “ _etext = 0x100; “,即表示符号_etext对应的地址为0X100, 此时 & _etext的值为 0x100, char a= *p;表示为 从0X100地址取值存储的值赋值给变量a。
3.3 KEEP关键字
在连接命令行内使用了选项–gc-sections后,连接器可能将某些它认为没用的section过滤掉,此时就有必要强制连接器保留一些特定的 section,可用KEEP()关键字达此目的。如KEEP(* (.text))或KEEP(SORT(*)(.text))。说的通俗易懂就是:防止被优化。
3.4 ALIGN关键字
表示字节对齐, 如 “ . = ALIGN(4);”表示从该地址开始后面的存储进行4字节对齐。
EMW307x 系列模组主要应用于物联网数据通讯。通过丰富的外设接口实现数据采集和控制,并可以通过 Wi-Fi 网络连接,将数据传输到物联网云服务平台上,实现万物互联。本系列模组通过各种不同的外形尺寸,接口形式,天线接口和温度范围,应用于广泛的物联网应用中。
EMW3072-WiFi芯片的工作温度在-40°C到+105°C之间,它的典型应用有:智能电工、照明,特别适合球灯泡。
EMW3072-WiFi芯片使用了AT2.0指令,通过串口发送AT指令与EMW3072-WiFi芯片通信来达到控制WiFi的功能。
EMW3072-WiFi芯片不止提供了局域网内的信息通信,同时提供了高效的开发环境、各大物联网云服务的接入协议栈、丰富的示例程序和各种典型应用。
开发EMW3072 模块主要需要用到以下三种芯片:EMW3072应用板、MXKit-Core板、MXKit-Base板。
EMW3072应用板主要是用于嵌入到其他芯片上用于增加芯片的远程通信功能,本身不提供引脚,采用的是与ESP8266相同的邮票孔设计。嵌入到芯片上后,芯片通过AT指令与EMW3072进行交互即可使得芯片具有WiFi功能。
MXKit-Core板是包含MXCHIP无线通讯模组的IOT接入核心板,通过MXPort接口与MXKit-Base板或MXKit-Arduino板连接,主要包括:
⚫MXPort 接口,通过板对板连接器与 MXKit-Base 板或 MXKit-Arduino 板连接。
⚫ MXCHIP 无线通讯模块,包括 Wi-Fi 模块,BLE 模块,Wi-Fi + BT 模块,LoRa 模块,GPRS 模块以及 SigFox 模块等。
MXKit-Core板上可放置不同种类的可拆卸的无线模组芯片,用户可以仅使用一个底板使用不同的MXKit-Core板
上一章介绍了芯片启动后的过程,芯片上电后开始启动执行到无操作系统主函数main(07_NosPrg\main.cpp)前。那么针对于BIOS最小系统和mbedOS的跳转过程如下:
BIOS的main函数如下:
1 | int main(void) |
那么,在上述代码中,有两处关键代码 pin_reset_judge(); 和 user_reset(GEC_USERBASE);
pin_reset_judge();
代码进行的是复位引脚被触碰了几次,本例子中以6次为限,触碰6次则程序返回BIOS中。
那么上一章所提的BSS段被注释掉就是希望芯片启动后不清楚某些全局变量的状态,如果芯片复位标志被置位,说明已触碰6次,那么就不会跳转到GEC_USERBASE用户区。反之,芯片启动后会直接跳转到用户区。具体流程见后续章节。
user_reset(GEC_USERBASE);
这段代码会让芯片直接跳转到用户区,GEC_USERBASE是用户区的首地址。
main 函数开始 mbedOS 的启动,我们把 mbedOS 启动过程梳理到一个函数中运行,由 main 函数来调用,这个函数原型为:
1 | mbedOS_start(osThreadId_t &thd, void (*func)(void))。 |
mbedOS 进入 main 函数之后,调用 mbedOS 的启动函数,mbedOS 就开始启动,并无休无止地开始运行,进行任务调度。
mbedOS_start 函数是 mbedOS 启动过程函数
其主要任务是:
1.设置 mbedOS 堆栈区
2.重定向中断向量表
3.完成内核初始化
4.建立互斥信号
5.创建主线程和启动内核
类似于BIOS,在main函数中会直接调用mbedOS_start这个函数跳转到mbedOS的首地址处。
当内核启动成功后,函数 svcRtxKernelStart 执行完成返回到 SVC 中断中,进行上下文切换,返回到定时器线程中执行。定时器线程 osRtxInfo.timer.thread 运行后会进入阻塞状态,此时 mbedOS 会选择就绪队列中优先级最高的线程,即主线程 main_thread,使其进入激活态。
主线程被调度运行时,其执行函数 app_init主要负责完成初始化外设模块、初始化全局变量、使能中断模块、创建并启动其他用户线程、阻塞主线程的功能,之后的线程运行和切换都由 mbedOS 调度完成