在线时间1369 小时
UID3338155
注册时间2017-7-26
NXP金币1767
TA的每日心情 | 无聊 6 小时前 |
---|
签到天数: 598 天 [LV.9]以坛为家II
金牌会员
- 积分
- 7113
- 最后登录
- 2024-5-6
|
本帖最后由 andeyqi 于 2023-4-5 14:59 编辑
Shell 在开发过程中经常会被使用到,本次移植适配的Shell 是基于Uart 方式实现,Shell 程序的移植适配需要依赖底层的接口位串口的手法函数,上一篇的(【LPC845-BRK板卡试用申请】(一)开发板体验之上电体验) 工程中已经可以通过串口输出和输入串口数据,shell 的移植依赖也就不需要额外适配了,串口的移植适已经完成S08平台完成过,移植说明参考此贴(【S08P闯关赛经验分享】+添加shell控制开发板)只要将源文件加入编译即可完成,因之前是基于IAR工具链本次使用的gcc的工具链实现上会有些差距,本篇主要介绍移植过程中遇到的问题进行整理。
1.MCUXpresso IDE(v11.7.1_9221) 如何将文件夹源文件加入编译
MCUXpresso 同IAR/MDK 常用的开发工具加入文件的方法稍微有些不同,IAR/MDK 将文件夹加入编译在workspace 下创建一个gpoup 将源文件加入编译列表即可参与编译,在MCUXpresso 工具下将文件夹放到工程目录下MCUXpresso 会自动刷新目录结构,如下将shell 文件夹放入工程目录下后workspace 下会多出shell 对应的文件夹,这时进行build 会发现shell 文件夹并未参与编译,仔细查看也会发现参与编译的未参与编译的显示上也会有差别,图中方框的部分是参与编译的文件夹,椭圆形的是未参与编译的目录。
将源码文件夹加入编译路径后在对比下文件夹的显示情况会发现和之前已经不同了,此时进行build 该文件夹已经参与编译了。
2.MCUXpresso IDE(v11.7.1_9221)链接脚本自定义的段不进行覆盖
shell 的实现是在flash 中定义一个FSymTab 的section,代码中在 FSymTab 起始地址和结束地址范围内进行检索查找输入的命令是否吻合,如果一致的话就执行绑定的函数,在工程的lpc845breakout_hello_world_Debug.ld 文件中加入如下 FSymTab section 区间。
/* MAIN TEXT SECTION */
.text : ALIGN(4)
{
. = ALIGN(4);
/* section information for shell */
__fsymtab_start = .;
KEEP(*(FSymTab))
__fsymtab_end = .;
} > PROGRAM_FLASH
加入上述的section 后,重新build 发现本地修改的链接脚本文件被覆盖了恢复成改动之前的样子了,从MCUXpresso 的帮助文档中可知默认是开启自动生成link file 的属性的
- Disabling Managed Linker Scripts
- It is possible to disable the managed linker script mechanism if required and provide your own linker scripts, but this is not recommended for most users. In most circumstance, the facilities provided by the managed linker script mechanism, and its underlying FreeMarker template mechanism should allow you to avoid the need for writing your own linker scripts. But if you do wish to do this, then untick the appropriate option at:
- Properties -> C/C++ Build -> Settings -> MCU Linker -> Managed Linker Script
复制代码
对于我们的使用场景需要关闭此特性,加入自定义的section,通过如下path 即可设置关闭。
Properties -> C/C++ Build -> Settings -> MCU Linker -> Managed Linker Script
去掉上述勾选后发现我们修改的内容不会被MCUXpresso overwrite了。
3.GCC 编译器对link file 定义的symbol 的引用
因之前的代码是基于IAR 环境下的,IAR 通过 __section_begin("FSymTab"),__section_end("FSymTab") 可以引用symbol 的定义此语法在gcc 编译器下是无法编译通过的,一下是摘自“GNU链接器ld_v2.34”对代码引用link file 的symbol的描述。
- 3.5.5 Source Code Reference
- Accessing a linker script defined variable from source code is not intuitive. In particular a linker script symbol is not equivalent to a variable declaration in a high level language, it is instead a symbol that does not have a value.
- Before going further, it is important to note that compilers often transform names in the source code into different names when they are stored in the symbol table. For example, Fortran compilers commonly prepend or append an underscore, and C++ performs extensive ‘name mangling’. Therefore there might be a discrepancy between the name of a variable as it is used in source code and the name of the same variable as it is defined in a linker script. For example in C a linker script variable might be referred to as:
- extern int foo;
- But in the linker script it might be defined as:
- _foo = 1000;
- In the remaining examples however it is assumed that no name transformation has taken place.
- When a symbol is declared in a high level language such as C, two things happen. The first is that the compiler reserves enough space in the program’s memory to hold the value of the symbol. The second is that the compiler creates an entry in the program’s symbol table which holds the symbol’s address. ie the symbol table contains the address of the block of memory holding the symbol’s value. So for example the following C declaration, at file scope:
- int foo = 1000;
- creates an entry called ‘foo’ in the symbol table. This entry holds the address of an ‘int’ sized block of memory where the number 1000 is initially stored.
- When a program references a symbol the compiler generates code that first accesses the symbol table to find the address of the symbol’s memory block and then code to read the value from that memory block. So:
- foo = 1;
- looks up the symbol ‘foo’ in the symbol table, gets the address associated with this symbol and then writes the value 1 into that address. Whereas:
- int * a = & foo;
- looks up the symbol ‘foo’ in the symbol table, gets its address and then copies this address into the block of memory associated with the variable ‘a’.
- Linker scripts symbol declarations, by contrast, create an entry in the symbol table but do not assign any memory to them. Thus they are an address without a value. So for example the linker script definition:
- foo = 1000;
- creates an entry in the symbol table called ‘foo’ which holds the address of memory location 1000, but nothing special is stored at address 1000. This means that you cannot access the value of a linker script defined symbol - it has no value - all you can do is access the address of a linker script defined symbol.
- Hence when you are using a linker script defined symbol in source code you should always take the address of the symbol, and never attempt to use its value. For example suppose you want to copy the contents of a section of memory called .ROM into a section called .FLASH and the linker script contains these declarations:
- start_of_ROM = .ROM;
- end_of_ROM = .ROM + sizeof (.ROM);
- start_of_FLASH = .FLASH;
- Then the C source code to perform the copy would be:
- extern char start_of_ROM, end_of_ROM, start_of_FLASH;
- memcpy (& start_of_FLASH, & start_of_ROM, & end_of_ROM - & start_of_ROM);
- Note the use of the ‘&’ operators. These are correct. Alternatively the symbols can be treated as the names of vectors or arrays and then the code will again work as expected:
- extern char start_of_ROM[], end_of_ROM[], start_of_FLASH[];
- memcpy (start_of_FLASH, start_of_ROM, end_of_ROM - start_of_ROM);
- Note how using this method does not require the use of ‘&’ operators.
复制代码 从这段描述中可知C代码引用了link file 的符号并不会未其分配地址,只是一个数值,c 代码中可以根据该符号是数值或者地址空间进行使用,shell中是作为地址空间进行引用的,所以在符号前加& 编译器将该符号作为指针变量处理。
#if defined (__ICCARM__)
littleshell_system_function_init(__section_begin("FSymTab"),
__section_end("FSymTab"));
#elif defined (__GNUC__)
extern const int __fsymtab_start;
extern const int __fsymtab_end;
littleshell_system_function_init(&__fsymtab_start, &__fsymtab_end);
#endif
至此shell 的移植已经完成了,我们可以在代码中添加命令打印 __fsymtab_start 和 & __fsymtab_start 来验证这两种写法的区别,添加如下测试代码打印信息:
- unsigned int symbol(char argc,char ** argv)
- {
- extern const int __fsymtab_start;
- extern const int __fsymtab_end;
- PRINTF("&__fsymtab_start = %p &__fsymtab_end = %p.\r\n",&__fsymtab_start,&__fsymtab_end);
- PRINTF("__fsymtab_start = %x __fsymtab_end = %x.\r\n",__fsymtab_start,__fsymtab_end);
- return 1;
- }
- LTSH_FUNCTION_EXPORT(symbol,"show symbol");
复制代码 打印结果如下:
- #symbol
- &__fsymtab_start = 47C0 &__fsymtab_end = 47E4.
- __fsymtab_start = 4120 __fsymtab_end = 10000024.
复制代码
根据编译的map 的FSymTab 下述信息可知&__fsymtab_start ,&__fsymtab_end 的地址信息和map 一致。
- 0x000047c0 __fsymtab_start = .
- *(SORT_BY_ALIGNMENT(FSymTab))
- FSymTab 0x000047c0 0xc ./source/hello_world.o
- 0x000047c0 __fsym_led
- FSymTab 0x000047cc 0x18 ./littleshell/littleshell.o
- 0x000047cc __fsym_hello
- 0x000047d8 __fsym_symbol
- 0x000047e4 __fsymtab_end = .
复制代码
__fsymtab_start = 4120 __fsymtab_end = 10000024 这个打印的内容是什么呢,从debug memory 窗口可知该内容是0x000047c0 和 0x000047e4 对应的地址空间的内容:
==========代码路径==========
==========资料分割线========== |
|