本帖最后由 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;
}
编译运行代码,发现可以进入中断函数了。
|