起因:
前面调试AVR128DA48的bootloader. 然后现在调试AVR128DA48的时钟auto-tune功能,结果发现按键中断怎么也不响应。以前是正常的
代码如下
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define DELAY_TIME 1000
#define PRESSED 1
#define NOT_PRESSED 0
#define PULL_UP_ENABLE 0x08
#define BUTTON_PIN PIN7_bm
void CLK_init(void);
void PORT_init(void);
uint8_t volatile button_event = NOT_PRESSED;
int main(void)
{
cli();
CLK_init();
PORT_init();
sei();
while (1)
{
if(button_event == PRESSED)
{
cli();
/* Set OSCHF clock to 1 MHz and enable auto-tune */
_PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, (CLKCTRL_FREQSEL_1M_gc | CLKCTRL_AUTOTUNE_bm));
_delay_ms(DELAY_TIME);
/* Disable auto-tune and keep the 1 MHz clock */
_PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_1M_gc);
button_event = NOT_PRESSED;
sei();
}
}
}
void CLK_init(void)
{
/* Set OSCHF clock to 1 MHz */
_PROTECTED_WRITE (CLKCTRL.OSCHFCTRLA, CLKCTRL_FREQSEL_1M_gc);
/* Enable external crystal oscillator */
_PROTECTED_WRITE (CLKCTRL.XOSC32KCTRLA, CLKCTRL_ENABLE_bm);
/* Set OSCHF as main clock and enable the clock signal output on pin PA7 */
_PROTECTED_WRITE (CLKCTRL.MCLKCTRLA, (CLKCTRL_CLKSEL_OSCHF_gc) | (CLKCTRL_CLKOUT_bm));
}
void PORT_init(void)
{
/* Set PC7 direction as input */
PORTC.DIRCLR = BUTTON_PIN;
/* Clear the interrupt */
PORTC.INTFLAGS = BUTTON_PIN;
/* Configure PC7 interrupt as rising and enable the internal pull-up */
PORTC.PIN7CTRL = PORT_ISC_RISING_gc | PULL_UP_ENABLE;
}
ISR(PORTC_PORT_vect)
{
/* Set the button state as PRESSED */
button_event = PRESSED;
/* Clear the interrupt */
PORTC.INTFLAGS = BUTTON_PIN;
}
检查FUSE配置,发现BOOTSIZE是0x03。这是测试bootloader时配置过的。改为0x00,发现就正常了。
改为0x00,结果是什么呐?数据书册说整个FLASH都是BOOT区了。
If FUSE.BOOTSIZE is written to ‘0’, the entire Flash is regarded as the BOOT section.
怎么会影响到中断呐?查看中断的章节。里面提到中断向量表的位置是可配置的,有苗头了。
• Interrupt Vectors Optionally Placed in the Application Section or the Boot Loader Section
细节来了:
14.3.2.2 Interrupt Vector Locations
The interrupt vector placement is dependent on the value of the Interrupt Vector Select (IVSEL) bit in the Control A
(CPUINT.CTRLA) register. Refer to the IVSEL description in CPUINT.CTRLA for the possible locations.
If the program never enables an interrupt source, the interrupt vectors are not used, and the regular program code
can be placed at these locations.
查看了一下寄存器,IVSEL等于0,中断向量表是放在BOOT区后面的。所以BOOT一使能,中断向量表就变位置了。中断的入口不对了。
为什么BOOTSIZE为0就可以呐?下面的 Note里面解释了,当整个FLASH都是BOOT区时,这个位就可以忽略了。中断向量放在BOOT开始位置。
Bit 6 – IVSEL Interrupt Vector Select
Value Description
0 Interrupt vectors are placed after the BOOT section of the Flash(1)
1 Interrupt vectors are placed at the start of the BOOT section of the Flash
Note:
1. When the entire Flash is configured as a BOOT section, this bit will be ignored.
另外的解决方法:置位IVSEL,使得中断向量表放在BOOT区。
初始化添加代码 CPUINT.CTRLA = CPUINT_IVSEL_bm;
不行不行,注意CPUINT.CTRLA的属性是更改受保护的。对于重要的系统寄存器,提供了更改保护。
Property: Configuration Change Protection
正确的更改方法是
_PROTECTED_WRITE (CPUINT.CTRLA, CPUINT_IVSEL_bm);
添加了上面的代码后,在BOOTSIZE = 3的情况下中断也正常响应了。
FUSE能影响到中断,深究还是要看手册才明白。
关于CCP,手册描述如下。
6.4.6 Configuration Change Protection (CCP)
System critical I/O register settings are protected from accidental modification. Flash self-programming is protected
from accidental execution. This is handled globally by the Configuration Change Protection (CCP) register.
Changes to the protected I/O registers or bits, or execution of protected instructions, are only possible after the
CPU writes a signature to the CCP register. The different signatures are listed in the description of the CCP register
(CPU.CCP).
Once the correct signature is written by the CPU, interrupts will be ignored for the duration of the configuration
change enable period. Any interrupt request (including non-maskable interrupts) during the CCP period will set the
corresponding Interrupt flag as normal, and the request is kept pending. After the CCP period is completed, any
pending interrupts are executed according to their level and priority.
There are two modes of operation: One for protected I/O registers, and one for protected self-programming.
|