标签:使用 函数指针 play test constant 区域 closed 0ms sort
在完成内存映射的内容后,接下来我们将进入一个简单Bootloader的实际设计中来。在第一节内容中,我们已经简单介绍了bootlaoder的作用,它实际上就是在单片机重启过程中的一个步骤:如果有bootloader的启动信号,则进入bootloader模式开始新程序的接收与flash的擦写,若没有bootloader的启动信号,则直接进入用户程序执行用户程序内容。
bootloader的启动信号一般有如下两种:
1)外部引脚接地或者拉高电平;每次启动时先监测某一已经设定的引脚是否已经被操作到了bootloader启动电平位,如果是希望bootloader启动的电平,则需要跳转入bootloader程序,否则直接进入用户程序。
2)重启后先开启通讯,通过串口,LIN或者CAN,网络等方式先于外部设备交互,若能够完成已经设计好的握手内容,则进入bootloader继续后续操作,否则等待一段时间比如10ms后直接跳转至用户程序。
读取引脚信息跳转bootloader与单片机的类型紧密相关,因而这里仅介绍开启通讯后的bootloader实现(实际上实现的过程大同小异,可以举一反三)。
这里讲解的内容即为上述(2)中方式,这个方式的开机后处理逻辑为:
1. 初始化总线时钟
2. 初始化通讯方式(初始化串口,CAN通讯,网络通讯或其他)
3. 初始化Flash擦写内容(这一步也可以在确认要进入bootloader后进行)
4. 将Flash擦写必须的程序从ROM中的复制到RAM中
5. 进入大循环中从通讯方式中发出握手信号判断是否进入Bootloader,握手成功则进入Bootloader否则进入用户程序
在了解整个开机后处理逻辑后,首先就需要考虑划分存储空间的事。下面是我的一个例程的prm文件的设置内容(本例程使用飞思卡尔HC9S12G128单片机为例):
1 /* This is a linker parameter file for the MC9S12G128 */ 2 NAMES END /* CodeWarrior will pass all the needed files to the linker by command line. But here you may add your own files too. */ 3 4 SEGMENTS /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */ 5 6 /* Register space */ 7 /* IO_SEG = PAGED 0x0000 TO 0x03FF; intentionally not defined */ 8 9 /* RAM */ 10 RAM = READ_WRITE 0x2000 TO 0x3BFF; 11 CODE_RAM = READ_WRITE 0x3C00 TO 0x3FFF; /*1 kB for flash read and write*/ 12 13 /* D-Flash */ 14 DFLASH = READ_ONLY 0x000400 TO 0x0013FF; 15 16 /* non-paged FLASHs */ 17 ROM_1400 = READ_ONLY 0x1400 TO 0x1FFF; 18 19 ROM_BOOT = READ_ONLY 0x4000 TO 0x43FF; // 1KB for boot loader 20 ROM_FLASH = READ_ONLY 0X4400 TO 0x47FF RELOCATE_TO 0x3C00; // 1KB for necessary flash operation 21 22 ROM_C000 = READ_ONLY 0xC000 TO 0xFEFF; 23 /* VECTORS = READ_ONLY 0xFF00 TO 0xFFFF; intentionally not defined: used for VECTOR commands below */ 24 //OSVECTORS = READ_ONLY 0xFF80 TO 0xFFFF; /* OSEK interrupt vectors (use your vector.o) */ 25 26 /* paged FLASH: 0x8000 TO 0xBFFF; addressed through PPAGE */ 27 PAGE_08 = READ_ONLY 0x088000 TO 0x08BFFF; 28 PAGE_09 = READ_ONLY 0x098000 TO 0x09BFFF; 29 PAGE_0A = READ_ONLY 0x0A8000 TO 0x0ABFFF; 30 PAGE_0B = READ_ONLY 0x0B8000 TO 0x0BBFFF; 31 PAGE_0C = READ_ONLY 0x0C8000 TO 0x0C93FF; 32 PAGE_0C_A000 = READ_ONLY 0x0CA000 TO 0x0CBFFF; 33 PAGE_0E = READ_ONLY 0x0E8000 TO 0x0EBFFF; 34 /* PAGE_0D = READ_ONLY 0x0D8000 TO 0x0DBFFF; not used: equivalent to ROM_4000 */ 35 /* PAGE_0F = READ_ONLY 0x0F8000 TO 0x0FBEFF; not used: equivalent to ROM_C000 */ 36 END 37 38 PLACEMENT /* here all predefined and user segments are placed into the SEGMENTS defined above. */ 39 _PRESTART, /* Used in HIWARE format: jump to _Startup at the code start */ 40 STARTUP, /* startup data structures */ 41 ROM_VAR, /* constant variables */ 42 STRINGS, /* string literals */ 43 VIRTUAL_TABLE_SEGMENT, /* C++ virtual table segment */ 44 //.ostext, /* OSEK */ 45 NON_BANKED, /* runtime routines which must not be banked */ 46 COPY /* copy down information: how to initialize variables */ 47 /* in case you want to use ROM_4000 here as well, make sure 48 that all files (incl. library files) are compiled with the 49 option: -OnB=b */ 50 INTO ROM_C000/*, ROM_1400, ROM_4000*/; 51 52 BOOTLOADER INTO ROM_BOOT; 53 FLASH_CODE INTO ROM_FLASH; 54 55 56 USER_APP INTO PAGE_08; 57 TEST_AREA INTO PAGE_09; /* physical address from 0x2_4000 */ 58 DEFAULT_ROM INTO PAGE_0A, PAGE_0B, PAGE_0C, PAGE_0C_A000, PAGE_0E ; 59 60 //.stackstart, /* eventually used for OSEK kernel awareness: Main-Stack Start */ 61 SSTACK, /* allocate stack first to avoid overwriting variables on overflow */ 62 //.stackend, /* eventually used for OSEK kernel awareness: Main-Stack End */ 63 DEFAULT_RAM INTO RAM; 64 65 //.vectors INTO OSVECTORS; /* OSEK */ 66 END 67 68 ENTRIES /* keep the following unreferenced variables */ 69 /* OSEK: always allocate the vector table and all dependent objects */ 70 //_vectab OsBuildNumber _OsOrtiStackStart _OsOrtiStart 71 END 72 73 STACKSIZE 0x100 74 75 VECTOR 0 _Startup /* reset vector: this is the default entry point for a C/C++ application. */ 76 //VECTOR 0 Entry /* reset vector: this is the default entry point for an Assembly application. */ 77 //INIT Entry /* for assembly applications: that this is as well the initialization entry point */ 78 79 VECTOR ADDRESS 0xFFD6 SCI0_INT_receive
这段内存分块中可以看出,全部的RAM空间地址从0x2000-0x3FFF。其中最高位的1KB (地址从 0x3C00到0x3FFF)内容用于运行Flash读写时的程序(注意,Flash的读写不能同步进行,仅当Flash不在写入时才能从中读取程序)。我们将ROM逻辑地址中未分页的区域0x4400-0x47FF定义为Flash写入必不可少的程序存储区域,并使用 RELOCATE_TO 语句将其映射至0x3C00。这样的话,存储在ROM_FLASH区域中的内容将在运行时使用CODE_RAM中的地址(当然程序需要在运行以前从ROM中先复制到RAM里,单片机不会自动帮你完成)。除了以上的操作外,我们分别定义了bootloader的存储区域ROM_BOOT并定义了相关存储区域的名称。
我们通过如下代码的方式定义了Flash擦写的库函数,通过#pragma关键词将其定位至不同的存储空间
1 #ifndef _FLASH_LIB_H 2 #define _FLASH_LIB_H 3 4 #include <mc9s12g128.h> 5 6 typedef enum 7 { 8 NoError = 0, 9 FlashProgramError = 1, 10 FlashEraseError = 2 11 } FlashMsg; 12 13 #pragma CODE_SEG BOOTLOASER 14 15 void Init_Flash(void); 16 17 #pragma CODE_SEG DEFAULT 18 19 #pragma CODE_SEG FLASH_CODE 20 21 FlashMsg Flash_Program(unsigned long address, unsigned int *ptr); 22 23 FlashMsg Flash_EraseSector(unsigned long address); 24 25 26 #pragma CODE_SEG DEFAULT 27 28 #endif
其中Flash区域的擦除与写入函数我们将其放在ROM的Flash区域,这两个函数都将会被复制到RAM中运行,复制ROM中的函数到RAM中我们只需要将每个字节都对应的复制过去即可。代码复制函数及其调用方式如下:
1 void MoveCodeIntoRam(byte *source, byte *dest, unsigned int size) 2 { 3 while (size --) 4 { 5 *dest ++ = *source ++; 6 } 7 }
此函数以复制的源起始地址,目标空间起始地址,复制地址内容大小为参数,调用格式为:
1 MoveCodeIntoRam((byte *)0x4400, (byte *)0x3C00, 0x400);
在main()函数中添加如下的内容:
1 Init_PLL(); // 初始化时钟 2 3 Init_SCI0(); // 初始化串口 4 5 Init_Flash(); // 初始化Flash擦写 6 7 MoveCodeIntoRam((byte *)0x4400, (byte *)0x3C00, 0x400); // 从ROM中复制程序至RAM 8 9 for (;;) 10 { 11 // 串口发送握手信息判断是否进入bootloader,若握手失败,进入用户程序 12 // 若进入bootloader在此处擦写Flash 13 }
主要的操作思路为上述所示,再有就是关于用户程序的跳转,可以直接通过指针函数的形式将用户程序调用。
如下所示在分页区域中定义了用户程序:
1 #pragma CODE_SEG USER_APP 2 void application(void) 3 { 4 /*Do application works here*/ 5 6 for (;;) 7 { 8 // 用户大循环 9 } 10 11 } 12 #pragma CODE_SEG DEFAULT
跳转时可以先定义函数指针,然后调用即可:
1 void (*__far ifunction)(void) = application;
总结一下本篇的内容:
1. 简要的给出了具体的HC9S12的Bootloader设计思路并提供了具体实现的方法
2. 给出了从ROM中复制地址到RAM中运行的方法及其简单解释
3. 给出了bootloader程序跳转至用户程序的函数指针方法
后续我们会再介绍HCS12系列单片机Bootloader中的重中之重:Flash擦写思路,S19记录文件的解析
(未完待续)
注: 本系列文章均为原创,如有转载引用请标明来源
freescale飞思卡尔 HCS12 系列单片机bootloader详解(三)
标签:使用 函数指针 play test constant 区域 closed 0ms sort
原文地址:https://www.cnblogs.com/15821216114sw/p/9507919.html