割込み処理は割込みベクター毎に割込みハンドラが生成されるためコードがやけにデカくなるのが気になっていたが、AVR8シリーズのCPUINTのCVT(Compact Vector Table)機能を使うことで割込みベクターを共通化しコードを小さくすることができるらしいので試してみた。
メモリの少ないCPUや数多くの割込み処理を駆使するアプリではメリットが大きいと思うが問題はCVTが既存の割込みベクターの一部(1、2、3番)を利用するため今までの割込み処理の定義をどうするかが問題。
既存の割込み処理定義を書き換えるのは面倒だし、かといってダブッてる割込みベクターをなんとかしないと使えなくなる割込みがでてしまうし...
で、結局、今まで通りの方法で割込み処理が定義できたほうがいいんじゃね?ということで、ISRマクロの書き換えとダブッてしまう1,2,3番のベクターを未使用の番号に振替し、そのアドレスを変数に保存&実行する方法で考えてみた。この方法では、共通の割込み処理内から従来の割込み処理をcallするため各ハンドラーはreti命令ではなくretで戻らなければならないが書き換えられたISRマクロにより自動的に行われるので特に意識することはない。
avr8_interrupt.hにてISRマクロを書き換えるためavr8_interrupt.hのインクルードは必須となるが、この方法なら各ソースを変更せずにCVT対応が可能となる。
それと全ての割込みベクターをRAMにコピー&実行するモード(CONFIG_CPUINT_CVT=2)にも対応してみた。このモードでは、Cpuint::interrupt()の呼び出しにより動的に割込みベクターの登録や取得が可能となるため割込みをデイジーチェーン処理させたりすることもできる。
【avr8_config.h】
/* CPUINT */
#define CONFIG_CPUINT_CVT 0 /* 0=disable, 1=ROM, 2=RAM */
#define CONFIG_CPUINT_CVT_NMI 0 /* 0=disable, 1=enable */
#define CONFIG_CPUINT_CVT_LVL1 0 /* 0=disable, 1=enable */
【ライブラリ】
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
/* avr8_interrupt.h - CPU Interrupt Macro Copyright (c) 2026 Sasapea's Lab. All right reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #pragma once #include <avr/interrupt.h> #if CONFIG_CPUINT_CVT #undef ISR #ifdef __cplusplus #define ISR(vector, ...) \ extern "C" void vector (void) __attribute__ ((__INTR_ATTRS)) __VA_ARGS__; \ void vector (void) #else #define ISR(vector, ...) \ void vector (void) __attribute__ ((__INTR_ATTRS)) __VA_ARGS__; \ void vector (void) #endif #undef NMI_vect_num #define NMI_vect_num 101 #undef NMI_vect #define NMI_vect _VECTOR(101) #undef BOD_VLM_vect_num #define BOD_VLM_vect_num 102 #undef BOD_VLM_vect #define BOD_VLM_vect _VECTOR(102) #if defined(CLKCTRL_CFD_vect) #undef CLKCTRL_CFD_vect_num #define CLKCTRL_CFD_vect_num 103 #undef CLKCTRL_CFD_vect #define CLKCTRL_CFD_vect _VECTOR(103) #else #undef RTC_CNT_vect_num #define RTC_CNT_vect_num 103 #undef RTC_CNT_vect #define RTC_CNT_vect _VECTOR(103) #endif #endif |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
/* avr8_cpuint.h - CPU Interrupt Driver for Microchip AVR8 Series Copyright (c) 2026 Sasapea's Lab. All right reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #pragma once #include "avr8_config.h" #if CONFIG_CPUINT_CVT extern "C" uint16_t __vectors; class Cpuint { public: typedef enum { STATUS_NMIEX = CPUINT_NMIEX_bm, STATUS_LVL1EX = CPUINT_LVL1EX_bm, STATUS_LVL0EX = CPUINT_LVL0EX_bm, } STATUS; typedef void (*isr_t)(void); static void setup(bool roundrobin = true) { for (uint8_t i = 4; i < NUM_VECTORS; ++i) VECTORS[i] = (isr_t)(&__vectors + i); CPUINT.LVL1VEC = 0; _PROTECTED_WRITE(CPUINT.CTRLA, CPUINT_CVT_bm | (roundrobin ? CPUINT_LVL0RR_bm : 0)); } #if CONFIG_CPUINT_LVL1 static uint8_t highest(void) { return CPUINT.LVL1VEC; } static void highest(uint8_t num) { /* num = XXX_vect_num */ if (num < NUM_VECTORS) CPUINT.LVL1VEC = num; } #endif static isr_t interrupt(uint8_t num) { /* num = XXX_vect_num */ return num < NUM_VECTORS ? VECTORS[num] : nullptr; } static void interrupt(uint8_t num, isr_t isr) { /* num = XXX_vect_num */ if (num < NUM_VECTORS) VECTORS[num] = isr; } static STATUS status(void) { return (STATUS)CPUINT.STATUS; } private: #if CONFIG_CPUINT_CVT > 1 static constexpr uint8_t NUM_VECTORS = _VECTORS_SIZE / _VECTOR_SIZE; #else static constexpr uint8_t NUM_VECTORS = 4; #endif friend void cpuint_isr(uint8_t num); static isr_t VECTORS[NUM_VECTORS]; }; #endif |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
/* avr8_cpuint.cpp - CPU Interrupt Driver for Microchip AVR8 Series Copyright (c) 2026 Sasapea's Lab. All right reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include "avr8_cpuint.h" #if CONFIG_CPUINT_CVT #define CVT(vector, ...) \ extern "C" void vector (void) __attribute__ ((signal, __INTR_ATTRS)) __VA_ARGS__; \ void vector (void) #define CPUINT_CVT_NMI_vect_num 1 #define CPUINT_CVT_NMI_vect _VECTOR(1) #define CPUINT_CVT_LVL1_vect_num 2 #define CPUINT_CVT_LVL1_vect _VECTOR(2) #define CPUINT_CVT_LVL0_vect_num 3 #define CPUINT_CVT_LVL0_vect _VECTOR(3) extern "C" void __bad_interrupt(void); extern "C" void _VECTOR(101) (void) __attribute__((weak)); extern "C" void _VECTOR(102) (void) __attribute__((weak)); extern "C" void _VECTOR(103) (void) __attribute__((weak)); Cpuint::isr_t Cpuint::VECTORS[] = { __bad_interrupt, _VECTOR(101) ? _VECTOR(101) : __bad_interrupt, _VECTOR(102) ? _VECTOR(102) : __bad_interrupt, _VECTOR(103) ? _VECTOR(103) : __bad_interrupt, }; inline void cpuint_isr(uint8_t num) { if (num < Cpuint::NUM_VECTORS) Cpuint::VECTORS[num](); else ((Cpuint::isr_t)(&__vectors + num))(); } #if CONFIG_CPUINT_CVT_NMI CVT(CPUINT_CVT_NMI_vect) { cpuint_isr(NMI_vect_num); } #endif #if CONFIG_CPUINT_CVT_LVL1 CVT(CPUINT_CVT_LVL1_vect) { cpuint_isr(CPUINT.LVL1VEC); } #endif CVT(CPUINT_CVT_LVL0_vect) { cpuint_isr(CPUINT.LVL0PRI); } #endif |
【関連投稿】
Microchip AVR8 用のライブラリを自作する。(GPIO)
Microchip AVR8 用のライブラリを自作する。(FUSE)
Microchip AVR8 用のライブラリを自作する。(CLOCK)
Microchip AVR8 用のライブラリを自作する。(RESET)
Microchip AVR8 用のライブラリを自作する。(PORTMUX)
Microchip AVR8 用のライブラリを自作する。(USART)
Microchip AVR8 用のライブラリを自作する。(RTC)
Microchip AVR8 用のライブラリを自作する。(TCA)
Microchip AVR8 用のライブラリを自作する。(TCB)
Microchip AVR8 用のライブラリを自作する。(VREF)
Microchip AVR8 用のライブラリを自作する。(DAC)
Microchip AVR8 用のライブラリを自作する。(AC)
Microchip AVR8 用のライブラリを自作する。(ADC)
Microchip AVR8 用のライブラリを自作する。(ZCD)
Microchip AVR8 用のライブラリを自作する。(SPI)
Microchip AVR8 用のライブラリを自作する。(TWI)
Microchip AVR8 用のライブラリを自作する。(MAIN)
Microchip AVR8 用のライブラリを自作する。(CONFIG)
Microchip AVR8 用のライブラリを自作する。(ALARM)
Microchip AVR8 用のライブラリを自作する。(TASK)
Microchip AVR8 用のライブラリを自作する。(CCL)
Microchip AVR8 用のライブラリを自作する。(TCD)
Microchip AVR8 用のライブラリを自作する。(TCE)
Microchip AVR8 用のライブラリを自作する。(WEX)
Microchip AVR8 用のライブラリを自作する。(TCF)
Microchip AVR8 用のライブラリを自作する。(CPUINT)
Microchip AVR8 用のライブラリを自作する。(DOWNLOAD)
