調べて見るとUSIを提供しているMCUがそれなりにあった。既に廃品種になってるものがあることから以外に古くから提供されていたようである。
ATA6616C, ATA6617C, ATA664251
ATmega165, ATmega169, ATmega325, ATmega329, ATmega645, ATmega649
ATmega3250, ATmega3290, ATmega6450, ATmega6490
ATtiny1634, ATtiny2313, ATtiny2313A, ATtiny4313
ATtiny24, ATtiny44, ATtiny84, ATtiny24A, ATtiny44A, ATtiny84A
ATtiny43U, ATtiny26, ATtiny25, ATtiny45, ATtiny85
ATtiny87, ATtiny167, ATtiny261, ATtiny461, ATtiny861
USI自体は同じ仕様であるが、MCUにより入出力ポートの割り当てや、次回に紹介するTWIのISR名が違うようなのでまずはその違いを吸収するためのヘッダーファイルを作成。
【SPI/TWI共通ヘッダーファイル】
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
#ifndef _USIPORTS_H #define _USIPORTS_H #define USI_PORT_HIGH(p) (USI_PORT(p##_X) |= _BV(p##_N)) #define USI_PORT_LOW(p) (USI_PORT(p##_X) &= ~_BV(p##_N)) #define USI_PORT_INV(p) (USI_PIN(p##_X) |= _BV(p##_N)) #define USI_PORT_IN(p) (USI_PIN(p##_X) & _BV(p##_N)) #define USI_DDR_OUT(p) (USI_DDR(p##_X) |= _BV(p##_N)) #define USI_DDR_IN(p) (USI_DDR(p##_X) &= ~_BV(p##_N)) #define USI_PORT(p) USI_PORT_REG(p) #define USI_PIN(p) USI_PIN_REG(p) #define USI_DDR(p) USI_DDR_REG(p) #define USI_PORT_REG(p) PORT##p #define USI_PIN_REG(p) PIN##p #define USI_DDR_REG(p) DDR##p #define USI_SDA_X USI_DI_X #define USI_SDA_N USI_DI_N #define USI_SCL_X USI_USCK_X #define USI_SCL_N USI_USCK_N #ifndef USI_PIN_POSITION #define USI_PIN_POSITION 0 #endif #if defined(__AVR_ATmega165__ ) || defined(__AVR_ATmega165A__ ) || defined(__AVR_ATmega165P__ ) || defined(__AVR_ATmega165PA__ ) || \ defined(__AVR_ATmega169__ ) || defined(__AVR_ATmega169A__ ) || defined(__AVR_ATmega169P__ ) || defined(__AVR_ATmega169PA__ ) || \ defined(__AVR_ATmega325__ ) || defined(__AVR_ATmega325A__ ) || defined(__AVR_ATmega325P__ ) || defined(__AVR_ATmega325PA__ ) || \ defined(__AVR_ATmega3250__) || defined(__AVR_ATmega3250A__) || defined(__AVR_ATmega3250P__) || defined(__AVR_ATmega3250PA__) || \ defined(__AVR_ATmega329__ ) || defined(__AVR_ATmega329A__ ) || defined(__AVR_ATmega329P__ ) || defined(__AVR_ATmega329PA__ ) || \ defined(__AVR_ATmega3290__) || defined(__AVR_ATmega3290A__) || defined(__AVR_ATmega3290P__) || defined(__AVR_ATmega3290PA__) || \ defined(__AVR_ATmega645__ ) || defined(__AVR_ATmega645A__ ) || defined(__AVR_ATmega645P__ ) || \ defined(__AVR_ATmega6450__) || defined(__AVR_ATmega6450A__) || defined(__AVR_ATmega6450P__) || \ defined(__AVR_ATmega649__ ) || defined(__AVR_ATmega649A__ ) || defined(__AVR_ATmega649P__ ) || \ defined(__AVR_ATmega6490__) || defined(__AVR_ATmega6490A__) || defined(__AVR_ATmega6490P__) #define USI_DI_X E #define USI_DI_N 5 #define USI_DO_X E #define USI_DO_N 6 #define USI_USCK_X E #define USI_USCK_N 4 #define USI_OVF_vect USI_OVERFLOW_vect #elif defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny2313A__) || defined(__AVR_ATtiny4313__) #define USI_DI_X B #define USI_DI_N 5 #define USI_DO_X B #define USI_DO_N 6 #define USI_USCK_X B #define USI_USCK_N 7 #define USI_OVF_vect USI_OVERFLOW_vect #elif defined(__AVR_ATtiny24A__) || defined(__AVR_ATtiny44A__) || defined(__AVR_ATtiny84A__) #define USI_DI_X A #define USI_DI_N 6 #define USI_DO_X A #define USI_DO_N 5 #define USI_USCK_X A #define USI_USCK_N 4 #define USI_START_vect USI_STR_vect #elif defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) #define USI_DI_X A #define USI_DI_N 6 #define USI_DO_X A #define USI_DO_N 5 #define USI_USCK_X A #define USI_USCK_N 4 #elif defined(__AVR_ATtiny1634__) #define USI_DI_X B #define USI_DI_N 1 #define USI_DO_X B #define USI_DO_N 2 #define USI_USCK_X C #define USI_USCK_N 1 #elif defined(__AVR_ATtiny43U__) #define USI_DI_X B #define USI_DI_N 4 #define USI_DO_X B #define USI_DO_N 5 #define USI_USCK_X B #define USI_USCK_N 6 #elif defined(__AVR_ATtiny26__) #define USI_DI_X B #define USI_DI_N 0 #define USI_DO_X B #define USI_DO_N 1 #define USI_USCK_X B #define USI_USCK_N 2 #define USI_START_vect USI_STRT_vect #elif defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__) #define USI_DI_X B #define USI_DI_N 0 #define USI_DO_X B #define USI_DO_N 1 #define USI_USCK_X B #define USI_USCK_N 2 #elif defined(__AVR_ATtiny261__) || defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__) #if (USI_PIN_POSITION == 0) #define USI_DI_X B #define USI_DI_N 0 #define USI_DO_X B #define USI_DO_N 1 #define USI_USCK_X B #define USI_USCK_N 2 #else #define USI_DI_X A #define USI_DI_N 0 #define USI_DO_X A #define USI_DO_N 1 #define USI_USCK_X A #define USI_USCK_N 2 #endif #elif defined(__AVR_ATtiny87__ ) || defined(__AVR_ATtiny167__) || \ defined(__AVR_ATA6616C__ ) || defined(__AVR_ATA6617C__ ) || defined(__AVR_ATA664251__) #if (USI_PIN_POSITION == 0) #define USI_DI_X B #define USI_DI_N 0 #define USI_DO_X B #define USI_DO_N 1 #define USI_USCK_X B #define USI_USCK_N 2 #else #define USI_DI_X A #define USI_DI_N 4 #define USI_DO_X A #define USI_DO_N 2 #define USI_USCK_X A #define USI_USCK_N 5 #endif #endif #endif //_USIPORTS_H |
SPIマスタークラスのテンプレート・クラス実装。他サイトで紹介されているものとの相違点は、SPIクロック指定ができることと、複数スレーブにも対応可能となっていること。
【SPIマスタークラス】
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
#ifndef _USISPI_H #define _USISPI_H #include <util/delay_basic.h> #include "USIPORTS.h" #define USISPI_1M 1 #define USISPI_2M 2 #define USISPI_4M 4 #define USISPI_8M 8 #define USISPI_CSN_NONE 0xFF typedef void (*USISPI_CSN_PROC)(bool enable, uint8_t addr); #define USISPI_DELAY(t) { \ if ((t) <= 768 / (F_CPU / 1000000)) \ _delay_loop_1((uint8_t)((t) * (F_CPU / 1000000) * 256 / 768)); \ else \ _delay_loop_2((uint16_t)((t) * (F_CPU / 1000000) * 65536.0 / 262100)); \ } #define USISPI_NOP() __asm__ __volatile__ ("nop" "\n\t"::) template<uint8_t SPI_CLK = USISPI_8M, uint8_t CSN_PIN = USISPI_CSN_NONE, uint8_t ENABLE_DELAY = 20, uint8_t DISABLE_DELAY = 50> class USISPI { private: USISPI_CSN_PROC _csn_proc; public: USISPI() : _csn_proc(0) { } void begin() { USI_DDR_IN(USI_DI); USI_PORT_HIGH(USI_DO); USI_PORT_HIGH(USI_USCK); USI_DDR_OUT(USI_DO); USI_DDR_OUT(USI_USCK); if (CSN_PIN != USISPI_CSN_NONE) { digitalWrite(CSN_PIN, HIGH); pinMode(CSN_PIN, OUTPUT); } } void end() { USI_DDR_IN(USI_DO); USI_DDR_IN(USI_USCK); USI_PORT_LOW(USI_DO); USI_PORT_LOW(USI_USCK); if (CSN_PIN != USISPI_CSN_NONE) { pinMode(CSN_PIN, INPUT); digitalWrite(CSN_PIN, LOW); } } void setCallback(USISPI_CSN_PROC proc) { _csn_proc = proc; } uint8_t write(const uint8_t *buf, uint8_t len, uint8_t addr = CSN_PIN) { cs(true, addr); for (uint8_t i = 0; i < len; ++i) transfer(*buf++); cs(false, addr); return len; } uint8_t read(uint8_t *buf, uint8_t len, uint8_t addr = CSN_PIN) { cs(true, addr); for (uint8_t i = 0; i < len; ++i) *buf++ = transfer(0xFF); cs(false, addr); return len; } uint8_t writeAndRead(uint8_t *wbuf, uint8_t wlen, uint8_t *rbuf, uint8_t rlen, uint8_t addr = CSN_PIN) { uint8_t i; cs(true, addr); for (i = 0; i < wlen; ++i) transfer(*wbuf++); for (i = 0; i < rlen; ++i) *rbuf++ = transfer(0xFF); cs(false, addr); return rlen; } void cs(boolean enable, uint8_t addr = CSN_PIN) { // // chip select // if (addr != USISPI_CSN_NONE) { if (enable) { USI_PORT_LOW(USI_USCK); if (_csn_proc) (*_csn_proc)(enable, addr); else digitalWrite(addr, LOW); } else { if (_csn_proc) (*_csn_proc)(enable, addr); else digitalWrite(addr, HIGH); USI_PORT_HIGH(USI_USCK); } } // // chip select from special logic with USCK // // nrf24l01+ control with 3 ATtiny85 pins // http://nerdralph.blogspot.jp/2014/01/nrf24l01-control-with-3-attiny85-pins.html // else { if (enable) { USI_PORT_LOW(USI_USCK); USISPI_DELAY(ENABLE_DELAY); } else { USI_PORT_HIGH(USI_USCK); USISPI_DELAY(DISABLE_DELAY); } } } uint8_t transfer(uint8_t txd) { USIDR = txd; // // FCK / 16 // if (SPI_CLK == (F_CPU / 1000000 / 16)) { const uint8_t cr = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); USISR = _BV(USIOIF); do { USICR = cr; USISPI_NOP(); USISPI_NOP(); USISPI_NOP(); USISPI_NOP(); } while (!(USISR & _BV(USIOIF))); } // // FCK / 8 // else if (SPI_CLK == (F_CPU / 1000000 / 8)) { const uint8_t cr = _BV(USIWM0) | _BV(USICS1) | _BV(USICLK) | _BV(USITC); USISR = _BV(USIOIF); do USICR = cr; while (!(USISR & _BV(USIOIF))); } // // FCK / 4 // else if (SPI_CLK == (F_CPU / 1000000 / 4)) { const uint8_t cr = _BV(USIWM0) | _BV(USICS1) | _BV(USITC); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); USICR = cr; USISPI_NOP(); } // // FCK / 2. default. // else { const uint8_t cr0 = _BV(USIWM0) | _BV(USICLK) | _BV(USITC); const uint8_t cr1 = _BV(USIWM0) | _BV(USITC); USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; USICR = cr0; USICR = cr1; } return USIDR; } }; #endif // _USISPI_H |
【標準的な使い方】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include "USISPI.h" #define SPI_CSN_PIN PB4 USISPI<USISPI_8M, SPI_CSN_PIN> spi; void setup() { spi.begin(); } void loop() { spi.write(...); spi.read(...); spi.writeAndRead(...); or spi.cs(true); spi.transfer(...); spi.cs(false); } |
【複数スレーブ対応(1)】
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 |
#include "USISPI.h" #define SPI_CSN_0_PIN PA0 #define SPI_CSN_1_PIN PA1 #define SPI_CSN_2_PIN PA2 USISPI<USISPI_8M> spi; void setup() { pinMode(SPI_CSN_0, OUTPUT); pinMode(SPI_CSN_1, OUTPUT); pinMode(SPI_CSN_2, OUTPUT); digitalWrite(SPI_CSN_0, HIGH); digitalWrite(SPI_CSN_1, HIGH); digitalWrite(SPI_CSN_2, HIGH); spi.begin(); } void loop() { spi.write(..., SPI_CSN_0); spi.read(..., SPI_CSN_1); spi.writeAndRead(..., SPI_CSN_2); or spi.cs(true, SPI_CSN_0); spi.transfer(...); spi.cs(false, SPI_CSN_0); } |
【複数スレーブ対応(2)】
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 |
#include "USISPI.h" #define SPI_CSN_0_PIN PA0 #define SPI_CSN_1_PIN PA1 #define SPI_CSN_2_PIN PA2 USISPI<USISPI_8M> spi; void select_slave(bool enable, uint8_t addr) { digitalWrite(addr, enable); } void setup() { pinMode(SPI_CSN_0, OUTPUT); pinMode(SPI_CSN_1, OUTPUT); pinMode(SPI_CSN_2, OUTPUT); digitalWrite(SPI_CSN_0, HIGH); digitalWrite(SPI_CSN_1, HIGH); digitalWrite(SPI_CSN_2, HIGH); spi.begin(); spi.setCallback(select_slave); } void loop() { spi.write(..., SPI_CSN_0); spi.read(..., SPI_CSN_1); spi.writeAndRead(..., SPI_CSN_2); or spi.cs(true, SPI_CSN_0); spi.transfer(...); spi.cs(false, SPI_CSN_0); } |