[PIC®/AVR®/dsPIC®产品] 使用MPLAB X IDE开发ATmega4809中断的“坑”

[复制链接]
2407|12
 楼主| JackTang1994 发表于 2021-12-29 16:04 | 显示全部楼层 |阅读模式
本帖最后由 JackTang1994 于 2022-1-12 16:09 编辑

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

软件环境:MPLAB X IDE v5.5,XC8编译器
硬件环境:ATmega4809 Curiosity Nano开发板

测试代码:
问题描述:
最近在使用ATmega4809的芯片的串口时,发现一个奇怪的问题。使用MCC配置串口时,只要使能串口中断时就会出现串口数据发送不出来的情况(没有使用中断发送)。
配置如下:

主程序中代码如下:
  1. int main(void)
  2. {
  3.     /* Initializes MCU, drivers and middleware */
  4.     SYSTEM_Initialize();
  5.    
  6.     USART0_Write('1');

  7.     /* Replace with your application code */
  8.     while (1)
  9.     {  
  10.         _delay_ms(1000);
  11.         LED_Toggle();
  12.         USART0_Write('2');
  13.     }
  14. }
由于使用板载的调试器,进行调试时经常无缘无故的调试异常(程序无法正常停止,且停止后无法找到程序运行位置)。所以只能通过现象来确认问题,目前现象是LED会闪烁4次,然后就不再闪烁。结合代码可以分析:因为串口发送缓冲区正好是8字节,当连续调用USART0_Write('2')填写满USART3_txbuf缓冲区时就停止在while处不再执行了。
  1. void USART3_Write(const uint8_t data)
  2. {
  3.     uint8_t tmphead;

  4.     /* Calculate buffer index */
  5.     tmphead = (USART3_tx_head + 1) & USART3_TX_BUFFER_MASK;
  6.     /* Wait for free space in buffer */
  7. while (USART3_tx_elements == USART3_TX_BUFFER_SIZE);
  8.     /* Store data in buffer */
  9.     USART3_txbuf[tmphead] = data;
  10.     /* Store new index */
  11.     USART3_tx_head = tmphead;
  12.     ENTER_CRITICAL(W);
  13.     USART3_tx_elements++;
  14.     EXIT_CRITICAL(W);
  15.     /* Enable Tx interrupt */
  16.     USART3.CTRLA |= (1 << USART_DREIE_bp);
  17. }
原因:没有进入中断。但是我已经在MCC中配置好了中断且相关寄存器也已经设置好了。
为了进一步验证是否为中断服务函数进入不了原因?
从网上下载了一份ATmega4809的中断使用代码,使用Wake_Up_On_Button_Press工程
https://github.com/microchip-pic-avr-examples/atmega4809-getting-started-with-gpio-mplab
通过简单修改将原先PB2——SW0修改为PF6,PB5——LED修改为PF5。发现此代码是可以正常进行中断的。从而说明此芯片的中断是没有问题的。

解决:
将MCC的中断配置界面中的Compact Vector Table Enable:选项的勾去除,MCC软件在我们设置串口中断时自动帮我们将此选项勾选好了。“Compact Vector Table Enable”选项:使能它,我们可以将没有使用到的中断从中断向量表中移除,从而节省我们FLASH空间以便将其预留给保存程序代码用。取消Compact Vector Table Enable:选项的勾选


如果需要使用“精简向量表”模式的中断,需要特殊处理。新建一个startup.c文件将以下代码复制到此文件中,然后将此文件添加到工程目录中。
  1. /*
  2. * Please disable standard startup files in Toolchain->AVR/GNU Linker->General.
  3. * In this example startup.c replaces the standard startup files.
  4. */
  5. #include <avr/io.h>

  6. /* Letting the compiler know there will be something called main */
  7. extern int main(void);

  8. /* Being nice to the compiler by predeclaring the ISRs */
  9. void __vector_cvt_nmi(void) __attribute__((weak, alias("dummy_handler")));
  10. void __vector_cvt_lvl1(void) __attribute__((weak, alias("dummy_handler")));
  11. void __vector_cvt_lvl0(void) __attribute__((weak, alias("dummy_handler")));

  12. /* Setting up the vector section
  13. * The rjump instruction can handle 8k code space, so this is used for
  14. * vector tables on devices with 8k flash or smaller. Other devices
  15. * use the jmp instruction.
  16. */
  17. __attribute__((section(".vectors"), naked)) void vectors(void)
  18. {
  19. #if (PROGMEM_SIZE <= 0x2000)
  20.         asm("rjmp __ctors_end");
  21.         asm("rjmp __vector_cvt_nmi");
  22.         asm("rjmp __vector_cvt_lvl1");
  23.         asm("rjmp __vector_cvt_lvl0");
  24. #else
  25.         asm("jmp __ctors_end");
  26.         asm("jmp __vector_cvt_nmi");
  27.         asm("jmp __vector_cvt_lvl1");
  28.         asm("jmp __vector_cvt_lvl0");
  29. #endif
  30. }

  31. /* Initialize the AVR core */
  32. __attribute__((section(".init2"), naked)) void init2(void)
  33. {
  34.         asm("clr r1");                   /* GCC expects this CPU register to be zero */
  35.         SREG = 0;                        /* Make sure the status register has a known state */
  36.         SPL  = INTERNAL_SRAM_END & 0xff; /* Point the stack pointer to the end of stack (low byte) */
  37.         SPH  = (INTERNAL_SRAM_END >> 8); /* Point the stack pointer to the end of stack (high byte) */
  38. }

  39. /* Handling main */
  40. __attribute__((section(".init9"), naked)) void init9(void)
  41. {
  42.         main();

  43.         /* Jump to avr libc defined exit handler
  44.          * (Disables interrupts and runs an empty loop forever)
  45.          */
  46.         asm("jmp _exit");
  47. }

  48. /* Dummy handler alias for unused ISRs */
  49. void dummy_handler(void)
  50. {
  51.         while (1)
  52.                 ;
  53. }
注意:startup.c文件需要放置在工程文件的上一层路径,否则工程不会将其识别为可用文件。


请按以下步骤添加:
1. 将startup.c文件工程文件的上一层路径

2. 选中当前工程中的Source files文件夹,点击鼠标右键选择“Add Exsiting Item...”

然后选择startup.c文件



3. 确认文件已经被添加。选中添加后的文件,点击鼠标右键选择“Compile File”,查看是否有编译信息输出。如果有则说明此文件已经被包含到工程中了。


4. 设置工程属性,使工程不添加默认的启动文件。
说明:为什么我们没有看到系统自动生成的启动文件?这是因为自动生成的启动文件在编译完成后被删除了,如果想要保留需要使用keep  --RUNTIME编译选项。
获取相关链接选项
查看MPLAB X IDE安装目录下的XC8 for AVR编译的使用说明文档(MPLAB_XC8_C_Compiler_User_Guide_for_AVR.pdf),在此文档中搜索"startup"关键字找到相关的编译选项。找到了链接选项"-nostartfiles"


设置链接选项
选中工程,打开工程属性面板。在Additional Options中添加“-nostartfiles”选项。保存修改

编译工程

因为这里按钮使用了外部中断,所以在pin_manager.c文件中添加以下中断函数,其中__vector_cvt_lvl0就是使用的“精简中断向量表”(Compact Vector Table
  1. ISR(__vector_cvt_lvl0) {
  2.     // Call the interrupt handler for the callback registered at runtime
  3.     if(VPORTF.INTFLAGS & PORT_INT6_bm)
  4.     {
  5.        PORTF_BUTTON_InterruptHandler();
  6.     }
  7.     if(VPORTF.INTFLAGS & PORT_INT5_bm)
  8.     {
  9.        PORTF_LED_InterruptHandler();
  10.     }
  11.     /* Clear interrupt flags */
  12.     VPORTF.INTFLAGS = 0xff;
  13. }

编译运行代码,发现可以进入中断函数了。





本帖子中包含更多资源

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

×
lcczg 发表于 2021-12-29 17:41 | 显示全部楼层
发现问题解决问题,点赞楼主。精简中断可以节约空间, bootloader有时会考虑。
aoyi 发表于 2022-1-10 14:46 | 显示全部楼层
非常不错的填坑文
coshi 发表于 2022-1-10 14:53 | 显示全部楼层
“-nostartfiles”选项是做什么用的呢
drer 发表于 2022-1-10 15:05 | 显示全部楼层
什么时候可以勾选该选项呢
gwsan 发表于 2022-1-10 15:46 | 显示全部楼层
这个解决的办法可不好找
 楼主| JackTang1994 发表于 2022-1-10 17:37 | 显示全部楼层
coshi 发表于 2022-1-10 14:53
“-nostartfiles”选项是做什么用的呢

不使用系统自动生成的启动文件。这个编译选项可以从AVR编译器使用手册中找到
 楼主| JackTang1994 发表于 2022-1-10 17:39 | 显示全部楼层
drer 发表于 2022-1-10 15:05
什么时候可以勾选该选项呢

想要节省Flash空间时使用。具体看芯片手册中CPUINT中断章节
daichaodai 发表于 2022-1-11 08:34 来自手机 | 显示全部楼层
谢谢分享避坑经验
kxsi 发表于 2022-1-11 08:57 | 显示全部楼层
保留系统自动生成的启动文件有什么用处吗
 楼主| JackTang1994 发表于 2022-1-11 11:06 | 显示全部楼层
kxsi 发表于 2022-1-11 08:57
保留系统自动生成的启动文件有什么用处吗

没有启动文件,芯片是运行不起来的。
 楼主| JackTang1994 发表于 2022-1-15 11:55 | 显示全部楼层
kxsi 发表于 2022-1-11 08:57
保留系统自动生成的启动文件有什么用处吗

就不用你写启动代码了,直接用C代码操作MCU就行
huquanz711 发表于 2022-1-15 18:45 来自手机 | 显示全部楼层
经验都是这样积累的啊
您需要登录后才可以回帖 登录 | 注册

本版积分规则

29

主题

64

帖子

0

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