打印
[PIC32/SAM]

PIC单片机生成HEX文件中代码地址问题分析

[复制链接]
63|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2025-1-15 08:22 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
以前使用STM32单片机的时候,生成的HEX文件,代码存储都是从0地址开始依次递增存储的,但是最近再使用PIC单片机的时候却发现这个单片机有点不一样。搞了好久才搞清楚了PIC单片机代码存储的原理。
下面就使用PIC18F46K80单片机来演示一下这个单片机HEX文件中代码存储的规律。

使用MPLAB X IDE新建一个工程,编写一个简单的LED程序

/*@  配置字部分,可通过菜单栏Window/PIC存储器视图/Configuration Bits来配置并生成配置字代码*********************************/
//CONFIG1L
#pragma config RETEN = OFF           // VREG Sleep Enable bit (Ultra low-power regulator is Disabled (Controlled by REGSLP bit)),禁止超低功耗稳压器
#pragma config INTOSCSEL = HIGH      // LF-INTOSC Low-power Enable bit (LF-INTOSC in High-power mode during Sleep),休眠期间 LF-INTOSC 处于高功耗模式
#pragma config SOSCSEL = DIG         // SOSC Power Selection and mode Configuration bits (Digital (SCLKI) mode),SOSC 功耗选择和模式配置位,数字 ( SCLKI)模式;使能 RC0 和 RC1 的 I/O 端口功能
#pragma config XINST = OFF           // Extended Instruction Set (Disabled),扩展指令集使能位,禁止指令集扩展和变址寻址模式 (传统模式)

// CONFIG1H
#pragma config FOSC = HS1            // Oscillator (HS oscillator (Medium power, 4 MHz - 16 MHz)),HS振荡器,中等功耗
#pragma config PLLCFG = ON           // PLL x4 Enable bit (Enabled),打开PLL功能,系统时钟4倍频,变为32MHz
#pragma config FCMEN = OFF           // Fail-Safe Clock Monitor (Disabled),禁止故障保护时钟监视器
#pragma config IESO = OFF            // Internal External Oscillator Switch Over Mode (Disabled),禁止双速启动

// CONFIG2L
#pragma config PWRTEN = OFF         // Power Up Timer (Disabled),禁止上电延时定时器
#pragma config BOREN = SBORDIS      // Brown Out Detect (Enabled in hardware, SBOREN disabled),只能由硬件使能欠压复位
#pragma config BORV = 3             // Brown-out Reset Voltage bits (1.8V),欠压复位电压选择为1.8v
#pragma config BORPWR = ZPBORMV     // BORMV Power level (ZPBORMV instead of BORMV is selected),选择ZPBORMV而不是BORMV

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer (WDT disabled in hardware; SWDTEN bit disabled),关闭看门狗功能
//#pragma config WDTEN = SWDTDIS      // Watchdog Timer (WDT enabled in hardware; SWDTEN bit disabled),由硬件使能WDT,禁止SWDTEN位
#pragma config WDTPS = 256          // Watchdog Postscaler (1:256),看门狗后分频1:256,溢出时间1.024s

// CONFIG3H
#pragma config CANMX = PORTB    // ECAN Mux bit (ECAN TX and RX pins are located on RB2 and RB3, respectively),1 = CANTX 和 CANRX 引脚分别位于 RB2 和 RB3 上
#pragma config MSSPMSK = MSK7   // MSSP address masking (7 Bit address masking mode),1 = 使能 7 位地址掩码模式
#pragma config MCLRE = ON      // Master Clear Enable (MCLR Enabled, RE3 Disabled),使能MCLR复位功能

// CONFIG4L
#pragma config STVREN = ON      // Stack Overflow Reset (Enabled),堆栈满/下溢导致复位
#pragma config BBSIZ = BB2K     // Boot Block Size (2K word Boot Block size),引导区大小选择2K

// CONFIG5L
#pragma config CP0 = OFF        // Code Protect 00800-01FFF (Disabled),代码保护位
#pragma config CP1 = OFF        // Code Protect 02000-03FFF (Disabled),代码保护位
#pragma config CP2 = OFF        // Code Protect 04000-05FFF (Disabled),代码保护位
#pragma config CP3 = OFF        // Code Protect 06000-07FFF (Disabled),代码保护位

// CONFIG5H
#pragma config CPB = OFF        // Code Protect Boot (Disabled),引导区不受代码保护
#pragma config CPD = OFF        // Data EE Read Protect (Disabled),数据EEPROM不受代码保护

// CONFIG6L
#pragma config WRT0 = OFF       // Table Write Protect 00800-01FFF (Disabled),写保护位
#pragma config WRT1 = OFF       // Table Write Protect 02000-03FFF (Disabled),写保护位
#pragma config WRT2 = OFF       // Table Write Protect 04000-05FFF (Disabled),写保护位
#pragma config WRT3 = OFF       // Table Write Protect 06000-07FFF (Disabled),写保护位

// CONFIG6H
#pragma config WRTC = OFF       // Config. Write Protect (Disabled),配置寄存器不受写保护
#pragma config WRTB = OFF       // Table Write Protect Boot (Disabled),引导区不受写保护
#pragma config WRTD = OFF       // Data EE Write Protect (Disabled),数据EEPROM不受写保护

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protect 00800-01FFF (Disabled),表读保护位
#pragma config EBTR1 = OFF      // Table Read Protect 02000-03FFF (Disabled),表读保护位
#pragma config EBTR2 = OFF      // Table Read Protect 04000-05FFF (Disabled),表读保护位
#pragma config EBTR3 = OFF      // Table Read Protect 06000-07FFF (Disabled),表读保护位
// CONFIG7H
#pragma config EBTRB = OFF      // Table Read Protect Boot (Disabled),引导区不受写保护

#include <xc.h>
#define uchar unsigned char
#define uint unsigned int
#define led_blue    LATBbits.LATB1

void delay_ms(uint m)
{
    unsigned int a,b;
    for(b=m;b>0;b--)
    {
            for(a=796;a>0;a--);
    }
}
int main(void)
{
    ANCON1=0x00;            //PORTB带模拟输入功能的引脚全部配置为数字IO
    TRISBbits.TRISB1=0;     //RB1配置为输出   
    led_blue=1;  
    while(1)
    {
        led_blue=0;         //蓝灯点亮
        delay_ms(100);
        led_blue=1;         //蓝灯熄灭
        delay_ms(100);
    }
    return 0;
}





接下来编译代码,生成hex文件。



使用notepad++ 打开hex文件



可以看到默认生成的代码存放地址都是从0x1000开始的,如果要修改存储地址的话需要再编译器项目属性里面设置ROM的地址范围。
打开项目属性由两种方法
第一种直接再File菜单中选择



第二种直接点击项目属性旁边的齿轮图标



打开项目属性设置



左边选择XC8 linker,右边操作类型选择Memory model,然后再ROM ranges 后面输入代码存放的地址范围,这个地址根据自己的项目情况设置。这里设置的范围为0到7FF之间。点击应用,再点击确定按钮。重新编译代码,查看hex文件。



这时候代码的存储位置变了,但是不是从0地址开始存储,而是从7FF地址前面了。没有存储到最前面,而是存储到了最后面一段。这个原因在网上找了好久才搞明白。当程序中没有中断的时候,代码存储的位置是编译器自动存放的,不一定是按照顺序从0开始存放的。要想代码从0开始存放,就需要在代码中添加中断。

下来给代码中添加一个串口中断函数

#include <xc.h>
#define uchar unsigned char
#define uint unsigned int
#define led_blue    LATBbits.LATB1

uchar USART_Rdata = 0x00; // 为接收串口数据流而定义的变量
void usart1_init(void)
{
    TXSTA1bits.SYNC = 0; // 异步模式
    TXSTA1bits.BRGH = 0; // 选择低速波特率模式
    TXSTA1bits.TX9 = 0;  // 选择8位发送模式
    TXSTA1bits.TXEN = 1; // 使能发送

    RCSTA1bits.SPEN = 1; // 使能串口,1 = 使能串口 (将 RXx/DTx 和 TXx/CKx 引脚配置为串口引脚)
    RCSTA1bits.CREN = 1; // 连续接收使能
    RCSTA1bits.RX9 = 0;  // 选择8位接收模式

    BAUDCON1bits.BRG16 = 1; // 选择16位波特率发生器,SYNC=0,BRGH=0,BRG16=1,波特率寄存器值=fosc/(16*19200))-1=32000000hz/(16*19200)-1=103.16,取值103
    // SPBRG1=103;                 //配置波特率为19200(器件工作于32MHz条件下)
    SPBRG1 = 207; // 波特率9600  32000000/(16*9600)-1=207.3333333333
    SPBRGH1 = 0;

    PIE1bits.RC1IE = 1; // 开usart1接收中断
    PIR1bits.RC1IF = 0; // 清零usart1接收中断标志位
    PIE1bits.TX1IE = 0; // 关闭发送中断,中断配置,根据需要来设置
    PIR1bits.TX1IF = 0; // 清零发送中断标志位

    INTCONbits.PEIE = 1; // 打开外设中断
    INTCONbits.GIE = 1;  // 打开总中断
}
void __interrupt() ISR(void)
{
    if (PIE1bits.RC1IE && PIR1bits.RC1IF)
    {
        USART_Rdata = RCREG1;
        PIR1bits.RC1IF = 0;   
    }
}

void delay_ms(uint m)
{
    unsigned int a,b;
    for(b=m;b>0;b--)
    {
            for(a=796;a>0;a--);
    }
}
int main(void)
{
    ANCON1=0x00;            //PORTB带模拟输入功能的引脚全部配置为数字IO
    TRISBbits.TRISB1=0;     //RB1配置为输出   
    led_blue=1;  
    usart1_init();
    while(1)
    {
        led_blue=0;         //蓝灯点亮
        delay_ms(100);
        led_blue=1;         //蓝灯熄灭
        delay_ms(100);
    }
    return 0;
}



重新编译代码,查看hex文件。

此时代码的存储就从0地址开始依次递增了。



如果要设置代码的偏移地址的话,也可以在项目属性中设置。



左边选择XC8 Linker,右边选择Additional options,在代码偏移Codeoffset处设置偏移地址,这里设置为0x100,点击应用,在点确定。
重新编译在查看HEX文件



可以看出代码的存放地址就改到了从0x100地址开始存放了。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_20222919/article/details/145069924

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2071

主题

16025

帖子

15

粉丝