freescale飞思卡尔 HCS12 系列单片机bootloader详解(三)

    在完成内存映射的内容后,接下来我们将进入一个简单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
View Code

    这段内存分块中可以看出,全部的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
View Code

   其中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 }
View Code

    此函数以复制的源起始地址,目标空间起始地址,复制地址内容大小为参数,调用格式为:

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记录文件的解析

    (未完待续)

    注: 本系列文章均为原创,如有转载引用请标明来源 

 

posted @ 2018-08-20 23:51  花花世界1202  阅读(3847)  评论(9编辑  收藏  举报