[PIC®/AVR®/dsPIC®产品] ATmega4809汇编——启动代码编写

[复制链接]
 楼主| JackTang1994 发表于 2022-1-19 18:01 | 显示全部楼层 |阅读模式
本帖最后由 JackTang1994 于 2022-2-8 10:43 编辑

#申请原创# #技术资源#

软件环境:MPLAB X IDE v5.5,XC8编译器
硬件环境:ATmega4809 Curiosity Nano开发板
参考资料
AVR指令集文档:https://www.microchip.com/DS40002198
ATmega4809 Curiosity Nano开发板:https://ww1.microchip.com/downloads/en/DeviceDoc/ATmega4809_Curiosity_Nano_Schematics.pdf
AVR编译器用户手册:http://ww1.microchip.com/downloads/en/DeviceDoc/50002750D.pdf

示例代码工程:

存储器介绍
存储器类型介绍RAM:随机存储器,速度快。可以按字节访问,掉电后数据丢失。
常用类型:SRAM、SDRAM、DDR等。

ROM:只读存储器,速度相对于RAM来说更慢。一般不能按照字节访问,基本访问单位一般为page(页)而且需要先擦除后才能写,掉电后数据依然保持,不会丢失。
常用类型:Flash、EEPROM、NandFlash、NorFlash等

总结:根据存储器特性,程序代码会被设计存储到ROM中,在MCU中ROM一般叫做Flash;而变量数据则会被设计存储在RAM中。

编译与链接是什么?
编译就是将源代码(C代码或者汇编代码)翻译成机器码(只包含01的二进制数)。根据不同的架构位数的MCU相应的编译器生成后的机器码长度也不一样。一般有16位、32位的机器码。
链接就是将生成的机器码进行组装,将其他源代码中的数据、代码放置在不同的内存区域。然后下载器再根据工程中设置下载地址,将生成的机器码镜像下载到相应的存储地址处。
MCU源代码编译链接及操作流程

说明:编译器和链接器以及代码下载软件工具都集成在了开发工具MPLAB X IDE中了

什么是启动代码?
启动代码就是芯片复位后最开始运行的代码。功能如下:
  • 设置栈(给C语言代码运行用的,用于程序的调用及局部变量)
  • 设置堆(需要使用动态内存时设置)
  • 中断向量表设计
  • 调试器相关配置(有些调试器需要MCU处于相应调试状态时才能进行调试,比如:Trace,在进行trace时需要设置MCU中控制trace的相关寄存器)
  • 对存储器的操作(代码搬运、RAM清零、以及将非0全局变量从ROM中搬移到RAM中)等操作。
启动代码的基本功能:设置栈、设置中断向量表

为什么MCU开发中,我们没有写过启动代码
因为这些启动代码,一般芯片公司已经写好了,已经放置在了下载的MCU pack包中了,我们创建工程后会自动复制到我们工程中。或者有些启动代码是自动生成的,不需要我们再写了。

编译选项设置
默认情况下,MPLAB中会使用默认的启动代码(MicroChip写好的)。如果想要在工程中使用我们编写的启动代码,则需要设置下编译选项:


查看预定义的section
目的:查看我们代码应该放置在哪个预定的区,因为我们没有修改默认的链接脚本。
查看MPLAB安装目录下的MPLAB_XC8_C_Compiler_User_Guide_for_AVR.pdf文档,从4.9章节可以知道,MPLAB链接脚本中已经预定义了10个在main调用之前运行代码的section(.init0-.init9)、10个在main调用之后运行代码的section(.fini0-.fini9)。

注:默认链接脚本路径:XXX\Microchip\xc8\v2.31\avr\avr\lib\ldscripts

MCU中断函数处理
在没有跳转语句时,代码是从头开始一条一条运行的。当发生中断时程序会自动跳转到相应的中断入口地址处而中断入口地址是连续的地址,一般我们把这连续的中断入口地址也叫做中断向量表。因为中断入口地址对应的内存空间只有16bit(2字节),这个空间根本无法保存我们的中断服务函数代码。所以我们会在中断入口地址处放置一条跳转语句,跳转到我们中断服务函数入口地址,从而来执行我们的中断服务函数。

启动代码编写——汇编版
  1. ; 说明:此文件不是用的AVRASM2.exe汇编器。此文件用的是gnu汇编器as - the GNU assembler.
  2. ; 版权申明:此代码仅供学习使用,没有经过严格测试,不便用于商业产品中。
  3. ; QQ:275424510
  4. ; Author:jack.tang
  5.     ;修改说明:将rjmp指令更换成jmp
  6.     #include <xc.h>

  7.     .extern main    ; 引用Main.c文件中的main函数
  8.    
  9.    
  10.    
  11.     //中断向量表.放置跳转指令,当中断发生时跳转到相应的中断服务函数中执行中断函数
  12.     .section .vectors
  13.     jmp __ctors_end        ;
  14.     jmp CRCSCAN_NMI
  15.     jmp BOD_VLM
  16.     jmp RTC_CNT_MEGA4809
  17.     jmp RTC_PIT
  18.     jmp CCL_CCL
  19.     jmp PORTA_PORT
  20.     jmp TCA0_OVF_vect

  21.      
  22.     //在main函数运行前运行的代码存储区
  23.     .section .init2
  24.     clr r1  ; 清零r1寄存器。有些编译器要求这样的做
  25.     clr r16 ; 清零SREG状态寄存器
  26.     out SREG, r16
  27.     ; Init the STACK Pointer. 初始化栈.用于子函数的调用
  28.     ldi        r16,(RAMEND & 0xff)        ; initialize
  29.     out        CPU_SPL,r16                ; stack pointer.
  30.     ldi        r16,(RAMEND >> 8)        ; to RAMEND
  31.     out        CPU_SPH,r16
  32.    
  33.     .section .init9
  34.     rcall main        ; 跳转到main函数中
  35.     jmp 0x00        ; main函数运行结束后执行的函数.直接跳转到0x00处即复位MCU
  36.       


  37. // 代码空间
  38. .section .text
  39.   
  40. //中断服务函数
  41. .weak CRCSCAN_NMI   ;使用.weak伪指令来定义函数.当有外部定义此函数时,使用外部定义的函数
  42. .global CRCSCAN_NMI
  43. CRCSCAN_NMI:
  44.     reti
  45.    
  46. .weak BOD_VLM
  47. .global BOD_VLM
  48. BOD_VLM:
  49.     reti   
  50.    
  51. .weak RTC_CNT_MEGA4809
  52. .global RTC_CNT_MEGA4809
  53. RTC_CNT_MEGA4809:
  54.     reti
  55.    
  56. .weak RTC_PIT
  57. .global RTC_PIT
  58.     RTC_PIT:
  59.     reti
  60.    
  61. .weak CCL_CCL
  62. .global CCL_CCL
  63.     CCL_CCL:
  64.     reti
  65.    
  66. .weak PORTA_PORT
  67. .global PORTA_PORT
  68.     PORTA_PORT:
  69.     reti

  70. .end
  71.    

测试代码

在main函数中编译此启动文件的测试代码,检测我们编写的启动是否可以进入TCA0溢出中断
  1. #include "mcc_generated_files/mcc.h"


  2. volatile uint8_t flag = 0;

  3. int main(void)
  4. {
  5.     /* Initializes MCU, drivers and middleware */
  6.     SYSTEM_Initialize();

  7.     /* Replace with your application code */
  8. //    protected_write_io(&(CLKCTRL.MCLKCTRLB),IO_KEY,0x01);
  9.     PORTF.DIR = 0x20;
  10.     TCA0.SINGLE.PERH = 0x4C;
  11.     TCA0.SINGLE.PERL = 0x4B;
  12.     TCA0.SINGLE.CTRLA = 0x0D;
  13.     TCA0.SINGLE.INTCTRL = 0x01;
  14.     ENABLE_INTERRUPTS();
  15.    
  16.     while(1)
  17.     {
  18.         if(flag == 1)
  19.         {
  20.             flag = 0;
  21.             PORTF.OUTTGL = 0x20;
  22.         }
  23.     }
  24. }

  25. ISR(TCA0_OVF_vect)
  26. {
  27.     flag = 1;
  28.     TCA0.SINGLE.INTFLAGS = 0x01;//??????
  29. }

启动代码编写——C语言版
C语言编写启动代码,可以参考我前面写的**:https://bbs.21ic.com/icview-3189168-1-1.html按照此**的启动代码仿写即可。

注:不同的编译器启动代码所使用的语言不同,汇编语言编写是常用的形式。








本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
天意无罪 发表于 2022-1-21 19:12 来自手机 | 显示全部楼层
考虑到执行效率问题,启动代码段一般都是通过汇编来实现的。
pzsh 发表于 2022-2-25 19:24 | 显示全部楼层
真正深入到低层了
sanxingnote7 发表于 2022-3-3 23:07 | 显示全部楼层
启动代码使用官网的不好吗  
touser 发表于 2022-3-3 23:18 | 显示全部楼层
这个厉害了,直接是需改boot了   
biechedan 发表于 2022-3-3 23:36 | 显示全部楼层
能够修改usb加载程序吗
 楼主| JackTang1994 发表于 2022-3-4 13:36 | 显示全部楼层
pzsh 发表于 2022-2-25 19:24
真正深入到低层了

是的。华东的
 楼主| JackTang1994 发表于 2022-3-4 13:36 | 显示全部楼层
biechedan 发表于 2022-3-3 23:36
能够修改usb加载程序吗

是指Bootloader吗?
yangxiaor520 发表于 2022-3-4 19:11 来自手机 | 显示全部楼层
现在用汇编的好少
wengh2016 发表于 2022-3-4 20:00 | 显示全部楼层
ATmega4809有哪些汇编指令呢
tabmone 发表于 2022-3-4 20:38 | 显示全部楼层
看着好复杂呢   
sanfuzi 发表于 2022-3-5 22:46 | 显示全部楼层
没有写过启动代码
updownq 发表于 2022-3-5 23:26 | 显示全部楼层
汇编写起来好麻烦的。   
mattlincoln 发表于 2022-3-6 14:02 | 显示全部楼层
汇编和C语言如何同时开发呢   
zerorobert 发表于 2022-3-6 15:14 | 显示全部楼层
这就是BootLoader吗?
adolphcocker 发表于 2022-3-6 16:06 | 显示全部楼层
启动代码还需要自己编写吗
 楼主| JackTang1994 发表于 2022-3-7 09:44 | 显示全部楼层
sanxingnote7 发表于 2022-3-3 23:07
启动代码使用官网的不好吗

官方有,这里只是学习如何写
 楼主| JackTang1994 发表于 2022-3-7 09:45 | 显示全部楼层
adolphcocker 发表于 2022-3-6 16:06
启动代码还需要自己编写吗

大部分情况下,不用
 楼主| JackTang1994 发表于 2022-3-7 09:46 | 显示全部楼层
mattlincoln 发表于 2022-3-6 14:02
汇编和C语言如何同时开发呢

新建工程,添加汇编文件和C文件。编译器会自动完成编译汇编和C代码
 楼主| JackTang1994 发表于 2022-3-7 09:46 | 显示全部楼层
zerorobert 发表于 2022-3-6 15:14
这就是BootLoader吗?

不是。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

64

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部