打印
[PIC®/AVR®/dsPIC®产品]

使用MPLAB X IDE开发ATmega4809中断的“坑”

[复制链接]
1824|12
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 JackTang1994 于 2022-1-12 16:09 编辑

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

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

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

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

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

    /* Calculate buffer index */
    tmphead = (USART3_tx_head + 1) & USART3_TX_BUFFER_MASK;
    /* Wait for free space in buffer */
while (USART3_tx_elements == USART3_TX_BUFFER_SIZE);
    /* Store data in buffer */
    USART3_txbuf[tmphead] = data;
    /* Store new index */
    USART3_tx_head = tmphead;
    ENTER_CRITICAL(W);
    USART3_tx_elements++;
    EXIT_CRITICAL(W);
    /* Enable Tx interrupt */
    USART3.CTRLA |= (1 << USART_DREIE_bp);
}
原因:没有进入中断。但是我已经在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文件将以下代码复制到此文件中,然后将此文件添加到工程目录中。
/*
* Please disable standard startup files in Toolchain->AVR/GNU Linker->General.
* In this example startup.c replaces the standard startup files.
*/
#include <avr/io.h>

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

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

/* Setting up the vector section
* The rjump instruction can handle 8k code space, so this is used for
* vector tables on devices with 8k flash or smaller. Other devices
* use the jmp instruction.
*/
__attribute__((section(".vectors"), naked)) void vectors(void)
{
#if (PROGMEM_SIZE <= 0x2000)
        asm("rjmp __ctors_end");
        asm("rjmp __vector_cvt_nmi");
        asm("rjmp __vector_cvt_lvl1");
        asm("rjmp __vector_cvt_lvl0");
#else
        asm("jmp __ctors_end");
        asm("jmp __vector_cvt_nmi");
        asm("jmp __vector_cvt_lvl1");
        asm("jmp __vector_cvt_lvl0");
#endif
}

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

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

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

/* Dummy handler alias for unused ISRs */
void dummy_handler(void)
{
        while (1)
                ;
}
注意: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
ISR(__vector_cvt_lvl0) {
    // Call the interrupt handler for the callback registered at runtime
    if(VPORTF.INTFLAGS & PORT_INT6_bm)
    {
       PORTF_BUTTON_InterruptHandler();
    }
    if(VPORTF.INTFLAGS & PORT_INT5_bm)
    {
       PORTF_LED_InterruptHandler();
    }
    /* Clear interrupt flags */
    VPORTF.INTFLAGS = 0xff;
}

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





使用特权

评论回复
沙发
lcczg| | 2021-12-29 17:41 | 只看该作者
发现问题解决问题,点赞楼主。精简中断可以节约空间, bootloader有时会考虑。

使用特权

评论回复
板凳
aoyi| | 2022-1-10 14:46 | 只看该作者
非常不错的填坑文

使用特权

评论回复
地板
coshi| | 2022-1-10 14:53 | 只看该作者
“-nostartfiles”选项是做什么用的呢

使用特权

评论回复
5
drer| | 2022-1-10 15:05 | 只看该作者
什么时候可以勾选该选项呢

使用特权

评论回复
6
gwsan| | 2022-1-10 15:46 | 只看该作者
这个解决的办法可不好找

使用特权

评论回复
7
JackTang1994|  楼主 | 2022-1-10 17:37 | 只看该作者
coshi 发表于 2022-1-10 14:53
“-nostartfiles”选项是做什么用的呢

不使用系统自动生成的启动文件。这个编译选项可以从AVR编译器使用手册中找到

使用特权

评论回复
8
JackTang1994|  楼主 | 2022-1-10 17:39 | 只看该作者
drer 发表于 2022-1-10 15:05
什么时候可以勾选该选项呢

想要节省Flash空间时使用。具体看芯片手册中CPUINT中断章节

使用特权

评论回复
9
daichaodai| | 2022-1-11 08:34 | 只看该作者
谢谢分享避坑经验

使用特权

评论回复
10
kxsi| | 2022-1-11 08:57 | 只看该作者
保留系统自动生成的启动文件有什么用处吗

使用特权

评论回复
11
JackTang1994|  楼主 | 2022-1-11 11:06 | 只看该作者
kxsi 发表于 2022-1-11 08:57
保留系统自动生成的启动文件有什么用处吗

没有启动文件,芯片是运行不起来的。

使用特权

评论回复
12
JackTang1994|  楼主 | 2022-1-15 11:55 | 只看该作者
kxsi 发表于 2022-1-11 08:57
保留系统自动生成的启动文件有什么用处吗

就不用你写启动代码了,直接用C代码操作MCU就行

使用特权

评论回复
13
huquanz711| | 2022-1-15 18:45 | 只看该作者
经验都是这样积累的啊

使用特权

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

本版积分规则

29

主题

62

帖子

0

粉丝