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

这段内存分块中可以看出,全部的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记录文件的解析

(未完待续)

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

原文地址:https://www.cnblogs.com/15821216114sw/p/9507919.html

时间: 2024-08-20 11:54:19

freescale飞思卡尔 HCS12 系列单片机bootloader详解(三)的相关文章

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

在上一篇文章中我们做了对HC9S12系列单片机存储空间的划分,但是相比于整个bootloader的工作而言,这只是非常基础的一步,有很多基础的概念在上一篇的操作中我们并没有涉及到.比如较复杂单片机中单片机的存储空间到底是什么样的,具体怎么用C语言对它进行寻址.因为我们单片机的存储空间一般由如下的构成,RAM,ROM以及EEPROM等等.比如在我在本篇中举例介绍的9S12G128单片机,它的RAM空间有8KB,EEPROM有4KB,FLASH有128KB.如何将这几个存储空间合理的组织起来想必单片

freescale飞思卡尔 HC9S12 系列单片机 Flash擦写详解(一)之时钟设置

Flash擦写的内容,个人做HC9S12系列单片机时觉得应该是各模块内容中最难而且是最麻烦的一步了.只有能够对Flash进行擦写以后,所做的Bootloader才有真正手段将串口或者其他通讯手段接收到的数据或者程序写入Flash中进行程序或者数据的更新.当初做Flash的擦写也遇到了很多问题,网上搜罗下来也没有合适的帖子能够详细的将整个过程或者学习的思路以及最后实现的程序详细的加以解析,而本博文就是基于这个出发点来写的.技术本身应当是共享的才能真正让社会进步,不是么? 关于单片机Flash的基本

NXP 飞思卡尔 S12Z 系列单片机 SCI 设置(解决SCI已经设置完成,但却无法通信的问题)

近日在使用了飞思卡尔S12Z设置SCI后总是不出数据,明明按照前代单片机S12Z的方法设置好了,为什么又不能用了呢? 下面是我简单对SCI1的设置,仅仅这样设置是出不来数据的: 1 SCI1BD = GLOBAL_BUS_CLOCK / SCI1_BAUD_RATE; 2 SCI1CR2 = SCI1CR2_RE_MASK + SCI1CR2_TE_MASK + SCI1CR2_RIE_MASK; 在上面的设置中我仅仅设置了SCI1的设置,假设了S12Z与S12一样,不存在同一口多个功能复用的情

飞思卡尔MC9S12系列单片机地址影射以及分页问题

对于用MCU的人来说,不一定要明白HCS12(x) memory map的机制和联系.因为如果没有系统地学习操作系统和编译原理之类的课程,确实有些难度.并且,对于DG128 XS128这样的MCU,默认的emory分配方式已经够用了.从这个意义上讲,搞清楚memory map似乎不必要. 但是,你有没有RAM不够用的情况?有没有想定义变量到FLASH ROM的情况?有没有因为欲提高寻址效率而定义变量到非分页区的情况?有没有写EEPROM但没写成功的情况? 飞思的memory非常灵活,通过地址映射

Android组件系列----Activity组件详解

[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3924567.html 联系方式:[email protected] [正文] 注:四大组件指的是应用组件:Activity.Service.BroadcastReceiver.ContentProvider:之前的控件指的是UI组件. 博文目录: 一.Activity简介 二.Activity的状

Android自定义控件系列八:详解onMeasure()(二)--利用onMeasure测量来实现图片拉伸永不变形,解决屏幕适配问题

上一篇文章详细讲解了一下onMeasure/measure方法在Android自定义控件时的原理和作用,参看博文:Android自定义控件系列七:详解onMeasure()方法中如何测量一个控件尺寸(一),今天就来真正实践一下,让这两个方法大显神威来帮我们搞定图片的屏幕适配问题. 请尊重原创劳动成果,转载请注明出处:http://blog.csdn.net/cyp331203/article/details/45038329,非允许请勿用于商业或盈利用途,违者必究. 使用ImageView会遇到

mongo 3.4分片集群系列之六:详解配置数据库

这个系列大致想跟大家分享以下篇章(我会持续更新的(^ω^)): 1.mongo 3.4分片集群系列之一:浅谈分片集群 2.mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3.mongo 3.4分片集群系列之三:搭建分片集群--哈希分片 + 安全 4.mongo 3.4分片集群系列之四:搭建分片集群--哈希分片 + 安全 + 区域 5.mongo 3.4分片集群系列之五:详解平衡器 6.mongo 3.4分片集群系列之六:详解配置数据库 7.mongo 3.4分片集群系列之七:配置数

spark2.x由浅入深深到底系列六之RDD java api详解三

学习任何spark知识点之前请先正确理解spark,可以参考:正确理解spark 本文详细介绍了spark key-value类型的rdd java api 一.key-value类型的RDD的创建方式 1.sparkContext.parallelizePairs JavaPairRDD<String, Integer> javaPairRDD =         sc.parallelizePairs(Arrays.asList(new Tuple2("test", 3

php学习之道:WSDL详解(三)

通过声明方式定义绑定(binding)属性 如果你在服务中采用SOAP binding,你可以使用JAX-WS来指定一定数量的属性binding.这些属性指定对应你在WSDL中指定的属性.某些设置,比如参数类型,可以约束你实现的方法,这些设置也影响声明的效用. @SOAPBinding声明,定义在javax.jws.soap.SOAPBinding接口中.它提供发布时的SOAP Binding细节.如果@SOAPBinding没有被指定,则用缺省的doc/literal SOAPBinding.