超高速かつ送信専用のシリアルライブラリを作ってみた。ATtiny85でも921600bps以上で送信できるぞ。(/・ω・)/
16MHzの場合で最低38400bpsから最高8000000bps(8MHz)までの対応だ。但し、921600bpsを超える速度についてはオシロで波形確認しただけで実際に受信可能かどうかは試していない。
余ってるピンがあれば送信できるので受信は出来なくても利用価値はあるだろう。
ちなみに、コードの実行サイクル数でクロックを作っているため割り込み禁止状態で送信を行うことには注意してほしい。16MHzでの割り込み禁止時間は次の通り。921600bps以上の可能な限り早い速度がお勧め。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
38400bps ... 260.4us 57600bps ... 173.6us 115200bps ... 86.8us 230400bps ... 43.4us 460800bps ... 21.7us 921600bps ... 10.9us 1000000bps ... 10.0us 1152000bps ... 8.7us 1500000bps ... 6.6us 2000000bps ... 5.0us 2500000bps ... 4.0us 3000000bps ... 3.3us 3500000bps ... 2.9us 4000000bps ... 2.5us 4500000bps ... 2.2us 5000000bps ... 2.0us 6000000bps ... 1.7us 7000000bps ... 1.5us 8000000bps ... 1.3us |
最大で0.5クロックのタイミングずれが発生するのでどれくらいの誤差になるか計算してみた。クロック自体の誤差は考慮していないが最高速度では20%程度の誤差となるようだ。
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 |
[8.0MHz] ----------------------------------------------- baudrate cpu cycles/bit error ----------------------------------------------- 921600 8.680555555555556 0.0576 1000000 8 0 1152000 6.944444444444444 0.072 1500000 5.333333333333333 0.09375 2000000 4 0 2500000 3.2 0.15625 3000000 2.666666666666667 0.1875 3500000 2.285714285714286 0.21875 4000000 2 0 4500000 x 5000000 x 5500000 x 6000000 x 7000000 x 8000000 x [16.0MHz] ----------------------------------------------- baudrate cpu cycles/bit error ----------------------------------------------- 921600 17.36111111111111 0.0288 1000000 16 0 1152000 13.88888888888889 0.036 1500000 10.66666666666667 0.046875 2000000 8 0 2500000 6.4 0.078125 3000000 5.333333333333333 0.09375 3500000 4.571428571428571 0.109375 4000000 4 0 4500000 3.555555555555556 0.1406250000000002 5000000 3.2 0.15625 5500000 2.909090909090909 0.171875 6000000 2.666666666666667 0,1875 7000000 2.285714285714286 0.21875 8000000 2 0 [16.5MHz (digispark)] ----------------------------------------------- baudrate cpu cycles/bit error ----------------------------------------------- 921600 17.90364583333333 0.0279272727272727 1000000 16.5 0.0303030303030303 1152000 14.32291666666667 0.0349090909090909 1500000 11 0 2000000 8.25 0.0606060606060606 2500000 6.6 0.0757575757575758 3000000 5.5 0.0909090909090909 3500000 4.714285714285714 0.1060606060606061 4000000 4.125 0.1212121212121212 4500000 3.666666666666667 0.1363636363636364 5000000 3.3 0.1515151515151515 5500000 3 0 6000000 2.75 0.1818181818181818 7000000 2.357142857142857 0.2121212121212121 8000000 2.0625 0.2424242424242424 8250000 2 0 |
【サンプル・スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#define HSTSerial_PORT B #define HSTSerial_POS 1 #include "HSTSerial.h" #define HSTX HSTSerial<921600> void setup(void) { HSTX::begin(); } void loop(void) { HSTX::write(0x41); delay(100); } |
【修正履歴】
2020-07-19
ストップビット処理を全AVRシリーズ共通コードに変更。
2020-07-10
インラインアセンブラの入出力パラメタの使い方が但しなかったので修正。
2020-06-24
write()処理内で割り込み禁止前に割り込み許可状態を保存&復旧するよう改良。
2020-04-30
参照していたAVR用高速GPIOライブラリの変更に伴う修正とスタートビット出力を1サイクル命令に変更。
2020-04-27
全AVRシリーズに共通なコードに改良。改良しなくても良かったんだけど、なんとなく...
2020-04-24
稀に信号が反転してしまうことがあったので修正。FT232Hを受信相手に8Mbpsまで通信できることを確認。ついでに最近投稿したAVR用高速GPIOライブラリを利用しポート指定方法を変更してみた。
2020-04-23
計算上は誤差18%なので無理かなと思っていたのだがFTDIのFT232Hを受信相手に6Mbpsまでの送信が可能なことを確認。相性が良かっただけ?
2020-04-20
コンパイル・ミスを防ぐため、使用する全てのレジスタを明示。
2020-04-18
2サイクルでビット処理できるよう改良してみた。この改良により16MHzでは最大8000000bpsとなる。これ以上は絶対無理。
2020-04-17
命令のサイクル数を勘違いしていたがC言語での修正は困難であったためインライン・アセンブラで書き直してみた。しかし、インライン・アセンブラの書き方って特殊過ぎてわかりづらい...
2020-04-16
ファイル名、クラス名を変更。
2020-04-15
誤差補正処理が特定の速度で誤補正するのを修正。
2020-04-14
誤差が0.5クロック以内に収まるように補正処理を追加。誤差データも0.5クロックで計算しなおした。
【ライブラリ】
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 |
/* HSTSerial.h - Hi Speed TX Serial Library for AVR Series Baudrate: 16MHz: 38400 - 8000000 bps 8MHz: 19200 - 4000000 bps Exsample: #include "HSTSerial.h" #define HSTX HSTSerial<921600, IOPORT_PIN('B', 1)> void setup(void) { HSTX::begin(); } void loop(void) { HSTX::write(0x41); } Copyright (c) 2020 Sasapea's Lab. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __HSTSERIAL_H #define __HSTSERIAL_H #include <stdint.h> #include <avr/io.h> #include "ioport.h" #define HSTSERIAL_DELAY_CYCLE 3 // cycle of delay loop #define HSTSERIAL_SHIFT_CYCLE 2 // cycle of bit assembly #define HSTSERIAL_CLOCK_CYCLE ((float)F_CPU / BAUDRATE) #define HSTSERIAL_SWCLK_CYCLE (int)(HSTSERIAL_CLOCK_CYCLE + 0.5) #define HSTSERIAL_DELAY(n) \ do \ { \ int cycle = 0; \ float error = HSTSERIAL_CLOCK_CYCLE - HSTSERIAL_SWCLK_CYCLE; \ float round = (error < 0 ? -0.5 : 0.5); \ error -= (int)error; \ if (((int)(error * (n) + round) ^ (int)(error * ((n) + 1) + round)) & 1) \ cycle += (error < 0 ? -1 : 1); \ if ((cycle += HSTSERIAL_SWCLK_CYCLE - HSTSERIAL_SHIFT_CYCLE) > 0) \ { \ if (cycle / HSTSERIAL_DELAY_CYCLE) \ __asm__ volatile \ ( \ "ldi r27, %[_COUNT_]" "\n\t" \ "1: dec r27" "\n\t" \ "brne 1b" "\n\t" \ : \ : [_COUNT_] "M" (cycle / HSTSERIAL_DELAY_CYCLE) \ : "r27" \ ); \ if ((cycle % HSTSERIAL_DELAY_CYCLE) > 0) \ __asm__ volatile ("nop"); \ if ((cycle % HSTSERIAL_DELAY_CYCLE) > 1) \ __asm__ volatile ("nop"); \ } \ } \ while (0) template<uint32_t BAUDRATE, uint8_t PIN> class HSTSerial { private: public: static void begin() { IOPORT::digitalWrite(PIN, HIGH); IOPORT::pinMode(PIN, OUTPUT); } static void write(uint8_t data) { if (HSTSERIAL_SWCLK_CYCLE < 256 * HSTSERIAL_DELAY_CYCLE + HSTSERIAL_SHIFT_CYCLE - 1) { __asm__ volatile ( "mov r25, %[_DATA_]" "\n\t" "lsl r25" "\n\t" "eor %[_DATA_], r25" "\n\t" "ldi r25, 1 << %[_POS_]" "\n\t" "in r23, __SREG__" "\n\t" "cli" "\n\t" "out %[_PORT_], r25" "\n\t" : [_DATA_] "=r" (data) : [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) , [_POS_ ] "I" (IOPORT_PINPOS(PIN)) : "r23", "r25" ); HSTSERIAL_DELAY(0); __asm__ volatile ( "sbrc %[_DATA_], 0" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(1); __asm__ volatile ( "sbrc %[_DATA_], 1" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(2); __asm__ volatile ( "sbrc %[_DATA_], 2" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(3); __asm__ volatile ( "sbrc %[_DATA_], 3" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(4); __asm__ volatile ( "sbrc %[_DATA_], 4" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(5); __asm__ volatile ( "sbrc %[_DATA_], 5" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(6); __asm__ volatile ( "sbrc %[_DATA_], 6" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(7); __asm__ volatile ( "sbrc %[_DATA_], 7" "\n\t" "out %[_PORT_], r25" "\n\t" : : [_DATA_] "r" (data) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) ); HSTSERIAL_DELAY(8); __asm__ volatile ( "sbis %[_POUT_], %[_POS_]" "\n\t" "out %[_PORT_], r25" "\n\t" "out __SREG__, r23" "\n\t" : : [_POUT_] "I" (_SFR_IO_ADDR(IOPORT_OUTREG(PIN))) , [_PORT_] "I" (_SFR_IO_ADDR(IOPORT_TGLREG(PIN))) , [_POS_ ] "I" (IOPORT_PINPOS(PIN)) ); HSTSERIAL_DELAY(9); } } static void write(uint8_t *buf, uint8_t len) { while (len--) write(*buf++); } }; #endif |
【参照ライブラリ】
AVR用高速GPIOライブラリ