USIによるTWI(I2C)実装の例。前回のSPIで紹介した”USIPORTS.h”を利用している。
USIの全機能を使いマスター/スレーブ両対応、かつ、アービトレーション(バス調停)も行うマルチマスター対応となっている。USIでここまでやってる例はまだ見たことがない。バスがフル稼働しコリジョン(衝突)が多々発生する状況化でもエラーなしで動作し続けることができるのでバス上に複数のマスターやスレーブを接続する複雑なシステムを構築することも可能だ。自分ながら素晴らしい出来栄えだと思う。
但し、SCLラインのLOW保持による同期化を行わない、或いは、マルチマスター対応ではないFTDIのI2Cマスターなどとの混在はできない。それと、USIは通信終了割込がないため代わりにhandle()メソッドにより終了をポーリングするメソッドを提供している。最悪は受信データ処理(コールバック)が次の通信まで待ちされる可能性があるのでポーリングは必須と考えたほうが良いだろう。
性能的には、マスター側はFSモードの400Kがほぼ出るが、スレーブ側は、割り込み処理の不必要なレジスタ・プッシュ&ポップのロスにより200~250K程度しか出ない。割り込み処理をアセンブラ言語で最適化するともっと高い性能が出せるはずだがメンテナンス等何かと面倒なことになるので妥協。
ちなみに、repeated-startについてI2Cの仕様書を読むと、repeated-startできるのは、write成功後に続くread時のみと記載されているが、仕様的には、write-writeやread-write,read-read、及び、複数回に渡るrepeated-startもできるはず。意味があるかどうかは別にして...
【マスター側エコーバックの例】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <string.h> #include "USITWI.h" #define SLAVE_ADDR 0x55 USITWI<USITWI_400K> twi; uint8_t snd[16], rcv[16]; void setup() { twi.begin(); } void loop() { for (uint8_t i = 0; i < sizeof(snd); ++i) snd[i] = (uint8_t)rand(); if (twi.writeAndRead(snd, sizeof(snd), rcv, sizeof(rcv), SLAVE_ADDR) == sizeof(rcv)) { if (memcmp(snd, rcv, sizeof(rcv)) == 0) ; // success } } |
【スレーブ側エコーバックの例】
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 |
#include <string.h> #include "USITWI.h" #define MY_ADDR 0x55 USITWI<USITWI_400K, MY_ADDR> twi; uint8_t buffer[16]; void twi_receive(uint8_t *buf, uint8_t len) { if (len <= sizeof(buffer)) { memcpy(buffer, buf, len); twi.setReadBytes(buffer, len); } } void setup() { twi.begin(); twi.setCallback(twi_receive); } void loop() { twi.handle(); } |
【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 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 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
#ifndef _USITWI_H #define _USITWI_H #include <util/delay_basic.h> #include "USIPORTS.h" #define USITWI_100K 0 #define USITWI_400K 1 #define USITWI_SCL_WAIT() while (!USI_PORT_IN(USI_SCL)) #define USITWI_CLK_LOW() USICR |= _BV(USITC) | _BV(USICLK) #define USITWI_CLK_HIGH() USICR |= _BV(USITC) #define USITWI_CLR_IRQ() USISR = _BV(USISIF) | _BV(USIOIF) | _BV(USIPF) #define USITWI_DELAY(t) do { if (t) _delay_loop_1((uint8_t)(((t) * (F_CPU / 1000000) * 256 + 767) / 768)); } while (0) #define USITWI_DELAY_THDSTA(m) USITWI_DELAY((m) ? 0.4 : 4.0) // 0.6/4.0 #define USITWI_DELAY_TLOW(m) USITWI_DELAY((m) ? 1.0 : 4.7) // 1.3/4.7 #define USITWI_DELAY_THIGH(m) USITWI_DELAY((m) ? 0.0 : 4.0) // 0.6/4.0 #define USITWI_DELAY_TSUSTA(m) USITWI_DELAY((m) ? 0.0 : 4.7) // 0.6/4.7 #define USITWI_DELAY_TSUSTO(m) USITWI_DELAY((m) ? 0.0 : 4.0) // 0.6/4.0 #define USITWI_DELAY_TSUDAT(m) USITWI_DELAY((m) ? 0.1 : 0.25) // 0.1/0.25 #define USITWI_DELAY_TBUF(m) USITWI_DELAY((m) ? 1.0 : 4.7) // 1.3/4.7 typedef void (*USITWI_CALLBACK)(uint8_t *buf, uint8_t len); extern volatile bool USITWI_usisif; extern void USITWI_init(uint8_t addr, uint8_t *buf, uint8_t len); extern void USITWI_setCallback(USITWI_CALLBACK func); extern void USITWI_setReadBytes(uint8_t *buf, uint8_t len); extern void USITWI_handle(); template<uint8_t TWI_CLK = USITWI_400K, uint8_t ADDR = 0x00, uint8_t SIZE = 16> class USITWI { private: uint8_t _buffer[SIZE]; bool _usidc; protected: void start() { _usidc = false; // // wait bus free // do { handle(); USICR &= ~_BV(USISIE); } while (USITWI_usisif || !USI_PORT_IN(USI_SDA) || !USI_PORT_IN(USI_SCL)); // // start confition // USITWI_usisif = true; USIDR = 0x00; // SDA LOW USITWI_DELAY_THDSTA(TWI_CLK); USI_PORT_LOW(USI_SCL); USICR |= _BV(USICS1); USITWI_CLR_IRQ(); if (TWI_CLK == USITWI_100K) USITWI_DELAY_TLOW(TWI_CLK); } void stop(bool restart) { USICR &= ~_BV(USICS1); // // setup for repeated start condition // if (restart) { USIDR = 0xFF; // SDA HIGH; USITWI_DELAY_TSUDAT(TWI_CLK); USI_PORT_HIGH(USI_SCL); USITWI_SCL_WAIT(); USITWI_DELAY_TSUSTA(TWI_CLK); } // // stop condition // else { USIDR = 0x00; // SDA LOW USITWI_DELAY_TSUDAT(TWI_CLK); USI_PORT_HIGH(USI_SCL); USITWI_SCL_WAIT(); USITWI_DELAY_TSUSTO(TWI_CLK); USIDR = 0xFF; // SDA HIGH; USITWI_DELAY_TBUF(TWI_CLK); } USITWI_usisif = false; USICR |= _BV(USISIE); } uint8_t transfer(uint8_t data, uint8_t dc) { USIDR = data; USISR = _BV(USIOIF); while (1) { USITWI_CLK_HIGH(); USITWI_SCL_WAIT(); // // data collision ? // if (USISR & dc) { _usidc = true; USICR &= ~_BV(USICS1); USIDR = 0xFF; break; } USITWI_DELAY_THIGH(TWI_CLK); USITWI_CLK_LOW(); if (TWI_CLK == USITWI_100K) { USITWI_DELAY_TLOW(TWI_CLK); if (USISR & _BV(USIOIF)) break; } else { if (USISR & _BV(USIOIF)) break; USITWI_DELAY_TLOW(TWI_CLK); } } USISR = _BV(USIOIF); return USIDR; } bool ack() { USI_PORT_HIGH(USI_SCL); USITWI_SCL_WAIT(); USITWI_DELAY_THIGH(TWI_CLK); USI_PORT_LOW(USI_SCL); if (TWI_CLK == USITWI_100K) USITWI_DELAY_TLOW(TWI_CLK); return !(USIDR & 0x01); } public: USITWI() : _usidc(0) { } void begin() { USITWI_init(ADDR, _buffer, sizeof(_buffer)); USITWI_CLR_IRQ(); USIDR = 0xFF; USICR = _BV(USISIE) | _BV(USIWM1) | _BV(USIWM0); USI_PORT_HIGH(USI_SDA); USI_PORT_HIGH(USI_SCL); USI_DDR_OUT(USI_SDA); USI_DDR_OUT(USI_SCL); } void end() { USI_DDR_IN(USI_SDA); USI_DDR_IN(USI_SCL); USI_PORT_LOW(USI_SDA); USI_PORT_LOW(USI_SCL); USICR = 0; } uint8_t writeAndRead(uint8_t *wbuf, uint8_t wlen, uint8_t *rbuf, uint8_t rlen, uint8_t addr) { return write(wbuf, wlen, addr, true) == wlen ? read(rbuf, rlen, addr) : 0; } uint8_t write(const uint8_t *buf, uint8_t len, uint8_t addr, bool restart = false) { uint8_t cnt; addr <<= 1; do { cnt = 0; // // start condition // start(); // // transmit slave address // transfer(addr, _BV(USIDC)); // // loop // while (!_usidc) { // // receive ack // USIDR = 0xFF; // // ack // if (!ack()) break; // // end of transfer ? // if (cnt >= len) break; // // transmit data // transfer(buf[cnt++], _BV(USIDC)); } } while (_usidc); // // stop or repeated start condition // stop(restart && (cnt == len)); return cnt; } uint8_t read(uint8_t *buf, uint8_t len, uint8_t addr, bool restart = false) { uint8_t cnt = 0; if (len) { addr = (addr << 1) | 0x01; do { cnt = 0; // // start condition // start(); // // transmit slave address // transfer(addr, _BV(USIDC)); // // loop // while (!_usidc) { // // transmit ack // USIDR = ((cnt > 0) && (cnt < len) ? 0x7F : 0xFF); // // ack // if (!ack()) break; // // ack collision ? // if (cnt >= len) { _usidc = true; USICR &= ~_BV(USICS1); USIDR = 0xFF; break; } // // receive data // buf[cnt++] = transfer(0xFF, 0); } } while (_usidc); // // stop or repeated start condition // stop(restart && (cnt == len)); } return cnt; } void handle() { return USITWI_handle(); } void setCallback(USITWI_CALLBACK func) { USITWI_setCallback(func); } void setReadBytes(uint8_t *buf, uint8_t len) { USITWI_setReadBytes(buf, len); } }; #endif // _USITWI_H |
【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 |
#include <Arduino.h> #include "USITWI.h" #define END() do { USICR &= ~(_BV(USICS1) | _BV(USIOIE)); USIDR = 0xFF; return 0x00; } while (0) typedef uint8_t (*USITWI_DISPATCH)(); volatile bool USITWI_usisif; static uint8_t _myaddr; static uint8_t *_write_buffer; static uint8_t _write_length; static uint8_t _write_count; static uint8_t *_read_buffer; static uint8_t _read_length; static uint8_t _read_count; static USITWI_CALLBACK _write_cb; static USITWI_DISPATCH _dispatch; static uint8_t read_next(); static uint8_t write_next(); static uint8_t read_start() { if (USIDR & 0x01) END(); _dispatch = read_next; USIDR = _read_buffer[_read_count]; return 0x00; } static uint8_t read_next() { if (++_read_count >= _read_length) END(); _dispatch = read_start; USIDR = 0xFF; return 0x0E; } static uint8_t write_start() { _dispatch = write_next; USIDR = 0xFF; return 0x00; } static uint8_t write_next() { _write_buffer[_write_count] = USIDR; if (++_write_count >= _write_length) END(); _dispatch = write_start; USIDR = 0x7F; return 0x0E; } static void callback() { if (_write_count) { _read_length = 0; if (_write_cb) (*_write_cb)(_write_buffer, _write_count); _write_count = 0; } } static uint8_t slave_address() { uint8_t addr = USIDR; if ((addr == 0) || (_myaddr == (addr & ~0x01))) { callback(); if (addr & 0x01) { if (_read_length != 0) { _dispatch = read_start; USIDR = 0x7F; return 0x0E; } } else { if (_write_length != 0) { _dispatch = write_start; USIDR = 0x7F; return 0x0E; } } } END(); } ISR(USI_OVF_vect) { uint8_t cnt = (*_dispatch)(); USITWI_DELAY_TSUDAT(USITWI_100K); USISR = _BV(USIOIF) | cnt; } ISR(USI_START_vect) { USITWI_usisif = true; _dispatch = slave_address; while (USI_PORT_IN(USI_SCL)) ; USIDR = 0xFF; USICR |= (_BV(USICS1) | _BV(USIOIE)); USITWI_CLR_IRQ(); } void USITWI_handle() { USICR &= ~_BV(USISIE); if (USITWI_usisif && (USISR & _BV(USIPF))) { USITWI_usisif = false; USICR &= ~(_BV(USICS1) | _BV(USIOIE)); USIDR = 0xFF; callback(); } USICR |= _BV(USISIE); } void USITWI_init(uint8_t addr, uint8_t *buf, uint8_t len) { _myaddr = addr << 1; _write_buffer = buf; _write_length = len; _write_count = 0; } void USITWI_setCallback(USITWI_CALLBACK func) { _write_cb = func; } void USITWI_setReadBytes(uint8_t *buf, uint8_t len) { _read_buffer = buf; _read_length = len; _read_count = 0; } |