C8051F130的远程在线升级程序设计
时间:06-03
来源:互联网
点击:
3. 需要注意的问题
3.1 项目管理问题
下位机固件程序中实际包含两个独立的部分,Bootloader 程序和主程序。我们在KEIL 中分别为这两部分的建立独立的项目文件,分别编译。烧写编译后产生的HEX 文件时,应该先擦除FLASH 后,烧写Bootloader 程序,然后在不擦除之前内容的情况下烧写主程序。
3.2 Bootloader 的存储位置
Bootloader 程序必须保证在上电过程后立即运行,而51 单片机的中断向量存放在低地址处。所以Bootloader 程序不能存储在低地址处,必须存放到高地址处,本例中,留出0xF000~0xFFFF 4KB 的FLASH 空间作为Bootloader 程序存储区。在KEIL 开发环境中,默认会为项目文件提供初始化文件STARTUP.A51 来清空RAM 空间,以及调用初始化全局变量代码段。其默认起始地址安排在0x0000处。为保证上电过程后立即运行Bootloader,我们在起始处还必须要手动修改汇编指令如下:
以上代码编译时强制在0x0000 处放置一条跳转到 0xF000 处的指令,这就保证了保证在上电过程后立即跳转到Bootloader 程序运行。为了将Bootloader 程序的所有代码定位在0xF000~0xFFFF 范围内,要对KEIL 的BL51 定位选项设置如下:
主程序同样有代码定位的问题。为保证程序升级后能正常工作,修改主程序的STARTUP.A51 文件如下:
这里保证执行主程序也是先跳转到Bootloader 程序,而将0x0006 设为了主程序的起始地址,避免了对中断向量表的占用。
另外还要将主程序的编译代码进行定位。设置和图4 中类似,只是将Code Ranger 设为主程序的代码空间:0x0000~0xEFFF。
3.3 程序跳转时的PLL 设置
C8051F130 内部带有PLL,最高主频可达100MHz。在本系统设计中,外部晶振频率为11.0592MHz,
在Bootloader 程序和主程序中都通过使能PLL,倍频至99.5328MHz 作为系统时钟。在上电后,C8051F130 默认是以内部时钟作为系统时钟的,通过执行初始化程序,系统再切换到以PLL 输出为系统时钟的工作环境上来。如果通过Silicon Labs 公司提供的初始化软件CONFIG2 来配置初始化C8051F130 的代码的话,在初始化PLL 的过程中,会关闭PLL 模块。
// 一个由 CONFIG2 生成的系统时钟初始化程序,
// 注意:在调用此函数时,系统时钟是由内部振荡器产生。
void Oscillator_Init()
{
int i = 0;
SFRPAGE = CONFIG_PAGE; // 切换到对应的寄存器页
OSCXCN = 0x67; // 选择外部晶振,频率11.0592MHz.
for (i = 0; i < 3000; i++); // Wait 1ms for initialization
while ((OSCXCN & 0x80) == 0); // 检测晶振是否已稳定,如果稳定,继续执行下面的程序。
PLL0CN = 0x04; // 选择PLL 源时钟为外部晶振,PLL 保持在复位状态,偏置发生器被禁止
CCH0CN &= ~0x20; // 禁止预取引擎。
SFRPAGE = LEGACY_PAGE; // 切换到对应的寄存器页
FLSCL = 0xB0; // FLASH 读时间,SYSCLK <= 100 MHz
SFRPAGE = CONFIG_PAGE;
CCH0CN |= 0x20; // 允许预取引擎。
PLL0CN |= 0x01; // PLL 偏置发生器被使能
PLL0div = 0x01; // PLL 预分频值
PLL0FLT = 0x07; // PLL 滤波器参数
PLL0MUL = 0x09; // PLL 时钟倍频寄存器
for (i = 0; i < 15; i++); // 等待PLL 初始化。
PLL0CN |= 0x02; // PLL 被使能
while ((PLL0CN & 0x10) == 0); // 等待PLL 输出频率已经锁定。
CLKSEL = 0x02; // 选择PLL 输出为系统时钟
OSCICN &= ~0x80; // 关闭内部振荡器。
}
如果使用这段代码初始化PLL 模块,在主程序跳转到Bootloader 程序,或者由Bootloader 程序跳转到主程序时,必须考虑PLL 的设置问题。因为原工作时钟是PLL 的输出,而直接切换到另外一个程序中时,执行以上代码会关闭PLL 系统时钟,导致系统时钟丢失,工作不正常。一个更安全的做法是在程序间互相跳转之前,将系统时钟切换到上电后默认的内部时钟上。代码如下所示:
// 跳转到Bootloader 程序处开始执行,
// 因为在此Bootloader 程序中采用PLL 作为系统时钟,
// 所以跳转之前,应该将时钟切换到内部振荡器或外部时钟。
// 本函数切换到内部时钟
void GotoBootLoader(void)
{
#define BOOTLOADER_ADDR 0xF000
SFRPAGE = CONFIG_PAGE;
OSCICN = 0xC0; // 使能内部振荡器,且8 分频,其起振时间短,无需延时。
CLKSEL = 0x00; // 选择内部时钟作为系统时钟。
PLL0CN = 0x00; // 关闭PLL。
((void (code *)(void)) BOOTLOADER_ADDR)();
// 程序跳转到Loaded 的代码运行,正常情况下永远不会返回
}
3.4 寄存器页的切换
C8051F130 内部功能模块多,其控制寄存器也多,为了合理安排控制寄存器位置,采用了寄存器页控制寄存器SFRPAGE 来将相同的地址切换到不同的控制寄存器。除了中断函数自动切换到对应的寄存器页外,当在程序中对某个寄存器操作前,需要设置SFRPAGE 切换到对应的寄存器页。因此,在C51 语言编写的程序中,涉及到寄存器操作的模块函数中,需要在执行之前保存SFRPAGE 寄存器值,处理完毕后再恢复先前的SFRPAGE 寄存器值。相当于对SFRPAGE 寄存器值做一次出入栈。示例代码如下:
void PutUnchar(uchar ch) //显示字符
{
uchar SFRPAGE_SAVE = SFRPAGE; // 保存 SFRPAGE
// 其他局部变量定义
// 函数执行代码部分,包含对指定寄存器的操作
SFRPAGE = SFRPAGE_SAVE; // 恢复SFRPAGE
}
3.1 项目管理问题
下位机固件程序中实际包含两个独立的部分,Bootloader 程序和主程序。我们在KEIL 中分别为这两部分的建立独立的项目文件,分别编译。烧写编译后产生的HEX 文件时,应该先擦除FLASH 后,烧写Bootloader 程序,然后在不擦除之前内容的情况下烧写主程序。
3.2 Bootloader 的存储位置
Bootloader 程序必须保证在上电过程后立即运行,而51 单片机的中断向量存放在低地址处。所以Bootloader 程序不能存储在低地址处,必须存放到高地址处,本例中,留出0xF000~0xFFFF 4KB 的FLASH 空间作为Bootloader 程序存储区。在KEIL 开发环境中,默认会为项目文件提供初始化文件STARTUP.A51 来清空RAM 空间,以及调用初始化全局变量代码段。其默认起始地址安排在0x0000处。为保证上电过程后立即运行Bootloader,我们在起始处还必须要手动修改汇编指令如下:
以上代码编译时强制在0x0000 处放置一条跳转到 0xF000 处的指令,这就保证了保证在上电过程后立即跳转到Bootloader 程序运行。为了将Bootloader 程序的所有代码定位在0xF000~0xFFFF 范围内,要对KEIL 的BL51 定位选项设置如下:
主程序同样有代码定位的问题。为保证程序升级后能正常工作,修改主程序的STARTUP.A51 文件如下:
这里保证执行主程序也是先跳转到Bootloader 程序,而将0x0006 设为了主程序的起始地址,避免了对中断向量表的占用。
另外还要将主程序的编译代码进行定位。设置和图4 中类似,只是将Code Ranger 设为主程序的代码空间:0x0000~0xEFFF。
3.3 程序跳转时的PLL 设置
C8051F130 内部带有PLL,最高主频可达100MHz。在本系统设计中,外部晶振频率为11.0592MHz,
在Bootloader 程序和主程序中都通过使能PLL,倍频至99.5328MHz 作为系统时钟。在上电后,C8051F130 默认是以内部时钟作为系统时钟的,通过执行初始化程序,系统再切换到以PLL 输出为系统时钟的工作环境上来。如果通过Silicon Labs 公司提供的初始化软件CONFIG2 来配置初始化C8051F130 的代码的话,在初始化PLL 的过程中,会关闭PLL 模块。
// 一个由 CONFIG2 生成的系统时钟初始化程序,
// 注意:在调用此函数时,系统时钟是由内部振荡器产生。
void Oscillator_Init()
{
int i = 0;
SFRPAGE = CONFIG_PAGE; // 切换到对应的寄存器页
OSCXCN = 0x67; // 选择外部晶振,频率11.0592MHz.
for (i = 0; i < 3000; i++); // Wait 1ms for initialization
while ((OSCXCN & 0x80) == 0); // 检测晶振是否已稳定,如果稳定,继续执行下面的程序。
PLL0CN = 0x04; // 选择PLL 源时钟为外部晶振,PLL 保持在复位状态,偏置发生器被禁止
CCH0CN &= ~0x20; // 禁止预取引擎。
SFRPAGE = LEGACY_PAGE; // 切换到对应的寄存器页
FLSCL = 0xB0; // FLASH 读时间,SYSCLK <= 100 MHz
SFRPAGE = CONFIG_PAGE;
CCH0CN |= 0x20; // 允许预取引擎。
PLL0CN |= 0x01; // PLL 偏置发生器被使能
PLL0div = 0x01; // PLL 预分频值
PLL0FLT = 0x07; // PLL 滤波器参数
PLL0MUL = 0x09; // PLL 时钟倍频寄存器
for (i = 0; i < 15; i++); // 等待PLL 初始化。
PLL0CN |= 0x02; // PLL 被使能
while ((PLL0CN & 0x10) == 0); // 等待PLL 输出频率已经锁定。
CLKSEL = 0x02; // 选择PLL 输出为系统时钟
OSCICN &= ~0x80; // 关闭内部振荡器。
}
如果使用这段代码初始化PLL 模块,在主程序跳转到Bootloader 程序,或者由Bootloader 程序跳转到主程序时,必须考虑PLL 的设置问题。因为原工作时钟是PLL 的输出,而直接切换到另外一个程序中时,执行以上代码会关闭PLL 系统时钟,导致系统时钟丢失,工作不正常。一个更安全的做法是在程序间互相跳转之前,将系统时钟切换到上电后默认的内部时钟上。代码如下所示:
// 跳转到Bootloader 程序处开始执行,
// 因为在此Bootloader 程序中采用PLL 作为系统时钟,
// 所以跳转之前,应该将时钟切换到内部振荡器或外部时钟。
// 本函数切换到内部时钟
void GotoBootLoader(void)
{
#define BOOTLOADER_ADDR 0xF000
SFRPAGE = CONFIG_PAGE;
OSCICN = 0xC0; // 使能内部振荡器,且8 分频,其起振时间短,无需延时。
CLKSEL = 0x00; // 选择内部时钟作为系统时钟。
PLL0CN = 0x00; // 关闭PLL。
((void (code *)(void)) BOOTLOADER_ADDR)();
// 程序跳转到Loaded 的代码运行,正常情况下永远不会返回
}
3.4 寄存器页的切换
C8051F130 内部功能模块多,其控制寄存器也多,为了合理安排控制寄存器位置,采用了寄存器页控制寄存器SFRPAGE 来将相同的地址切换到不同的控制寄存器。除了中断函数自动切换到对应的寄存器页外,当在程序中对某个寄存器操作前,需要设置SFRPAGE 切换到对应的寄存器页。因此,在C51 语言编写的程序中,涉及到寄存器操作的模块函数中,需要在执行之前保存SFRPAGE 寄存器值,处理完毕后再恢复先前的SFRPAGE 寄存器值。相当于对SFRPAGE 寄存器值做一次出入栈。示例代码如下:
void PutUnchar(uchar ch) //显示字符
{
uchar SFRPAGE_SAVE = SFRPAGE; // 保存 SFRPAGE
// 其他局部变量定义
// 函数执行代码部分,包含对指定寄存器的操作
SFRPAGE = SFRPAGE_SAVE; // 恢复SFRPAGE
}
单片机 电压 MCU MIPS 电路 总线 振荡器 滤波器 Keil 电子 嵌入式 相关文章:
- 单片机智能频率信号装置(11-25)
- 单片机在医学信号检测仪中的应用(02-07)
- 单片机应用编程技巧(02-25)
- DSP与单片机通信的多种方案设计(03-08)
- 单片机与PC机串行通信的实现方法 (02-25)
- 单片机与PC通信的简化接口 (05-11)