Seeeduino XIAO (ATSAMD21) の限界を試すべく、下記投稿のライブラリをポーティングし送信速度がどこまで出せるか試してみた。
Hi-Speed Serial TX Library for AVR Series
ちなみに、Seeeduino XIAO (ATSAMD21) のSERCOM-USARTでは送受信とも最大3Mbpsである。
ライブラリの最高速度は12Mbps16Mbpsだが、さすがに、16MbpsだとGPIOが追い付かなくて無理っぽい感じ...8Mbpsならなんとか行けそうな気もするが、Output Driver Strength = 1 は必須かも。
【16Mbps (Output Driver Strength = 0)】
【16Mbps (Output Driver Strength = 1)】
【8Mbps (Output Driver Strength = 0)】
【8Mbps (Output Driver Strength = 1)】
【4Mbps (Output Driver Strength = 0)】
【4Mbps (Output Driver Strength = 1)】
【サンプル・スケッチ】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include "HSTSerial.h" #define TX_PORT (32 * 1 + 8) // PB08_A6_D6_TX #define HSTX HSTSerial<921600, TX_PORT> uint8_t data[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'}; void setup() { HSTX::begin(); } void loop() { HSTX::write(data, sizeof(data)); delay(100); } |
【修正履歴】
2020-04-25
オシロで詳しく調べてみたらSingle-Cycle I/O の実行クロック数がヘンだ。連続でHIGH/LOWを繰り返すと確かに1サイクルなのだが、繰り返さない場合は+1サイクル余計にかかってしまう。つまり、普通に使う限り2サイクルかかるということだ。AHB-APB BridgeのWait stateが入るのかな...もしかしてだけど。ということで、ビット当たり3から4クロックへ変更しそれに合わせて通信速度定義を修正。delay cycle数も確認してみたが問題なさそう。でも、まだ計算通りのクロックにならないのはなぜだろう?詳しく観察してみると数サイクル毎に±1サイクル微調整されてるもよう。そもそも armというのは複雑なパイプラインを駆使するPC向けアーキテクチャーのものなので、それをダウングレードして組み込み用にも使えるよって歌い文句にしただけの製品だから命令の実行サイクル数など計算しても意味がないよということらしい...知らなかった。orz
2020-04-24
1Mbps超えでエラーになるのはクロックのズレが原因だった。オシロで見るかぎり命令の実行サイクル数がドキュメント通りでなく計算値とかなりかけ離れているようだ。だが、なぜかはわからないがどうやっても調整することができない。仕方ないのでよく使う通信速度をオシロで時間合わせした速度定義を追加しておいた。通信速度を直接指定せずHSTSerial_115200からHSTSerial_12Mまでの通信速度定義を利用するとうまくいくはずだ。
計算上では16Mbpsのはずが最高でも12Mbpsまでしか出なかったがFT232Hを受信相手に最大12Mbpsで送信できることを確認した。但し、12Mbpsでは連続送信で時々データ化けが発生。8Mbps程度が限界かも...
2020-04-23
1Mbpsを超える速度では受信エラーになることが発覚。ATtiny85でも8Mbpsで送信できたのに...相性の問題か、或いは、送信タイミングが微妙にズレてるのかも。調査が必要だ。
【ライブラリ】
ライブラリは、Cortex-M0+の命令実行サイクルとSingle-Cycle I/O Portに依存してるためCortex-M0を含む他のデバイスでは動作しないことに注意しよう。このライブラリはビットあたり3サイクルかかっているが、AVRのビット・テスト・スキップ命令、或いは、shiftとandの合成命令があれば2サイクルで実行できる。特にAND/OR/XORなどの論理演算と他の命令の組み合わせは頻繁に使われてるはずなのであったらいいなって感じの命令だ。CPUアーキテクチャー云々よりも使いやすいのが一番だと思うが。
ちなみに、Single-Cycle I/O PortのサポートがないCortex-M0に対応したところで限界値はかなり低いと思われるため対応する価値はないかな。たぶん...
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 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
/* HSTSerial.h - Hi Speed TX Serial Library for ATSAMD21 Baudrate: 48MHz: 115200 -12M bps Exsample: #include "HSTSerial.h" #define TX_PORT (32 * 1 + 8) // PB08_A6_D6_TX #define HSTX HSTSerial<921600, TX_PORT> 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 "IOBUS.h" #define HSTSerial_115200 115500 #define HSTSerial_230400 232000 #define HSTSerial_320400 324000 #define HSTSerial_460800 466350 #define HSTSerial_921600 941500 #define HSTSerial_1M 1021000 #define HSTSerial_2M 2090000 #define HSTSerial_3M 3200000 #define HSTSerial_4M 4370000 #define HSTSerial_5M 5550000 #define HSTSerial_6M 6500000 #define HSTSerial_8M 8200000 #define HSTSerial_10M 10000000 #define HSTSerial_12M 11300000 #define HSTSerial_POS(pin) (pin & 31) #define HSTSerial_PORT(pin) (int)(&PORT_IOBUS->Group[pin >> 5]) #define HSTSerial_OUTCLR(pin) (int)(&((PortGroup *)0)->OUTCLR) #define HSTSerial_OUTSET(pin) (int)(&((PortGroup *)0)->OUTSET) #define HSTSerial_OUTTGL(pin) (int)(&((PortGroup *)0)->OUTTGL) #define HSTSerial_DELAY_CYCLE 3 // cycle of delay loop #define HSTSerial_TXBIT_CYCLE 4 // 3 + (1)??? #define HSTSerial_BIT_CYCLE ((float)F_CPU / BAUDRATE) #define HSTSerial_CPU_CYCLE (int)(HSTSerial_BIT_CYCLE + 0.5) #define HSTSerial_DELAY(n) \ do \ { \ int cycle = 0; \ float error = HSTSerial_BIT_CYCLE - HSTSerial_CPU_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_CPU_CYCLE - HSTSerial_TXBIT_CYCLE) > 0) \ { \ if (cycle / HSTSerial_DELAY_CYCLE) \ __asm__ volatile \ ( \ "mov r3, %[_COUNT_]" "\n\t" \ "1: sub r3, #1" "\n\t" \ "bne 1b" "\n\t" \ : \ : [_COUNT_] "I" (cycle / HSTSerial_DELAY_CYCLE) \ : "r3" \ ); \ if ((cycle % HSTSerial_DELAY_CYCLE) > 0) \ __asm__ volatile ("nop"); \ if ((cycle % HSTSerial_DELAY_CYCLE) > 1) \ __asm__ volatile ("nop"); \ } \ } \ while (0) template<uint32_t BAUDRATE, uint32_t PIN> class HSTSerial { private: public: static void begin(bool drvstr = true) { IOBUS::pinMode(PIN, OUTPUT, drvstr); } static void write(uint8_t data) { if (HSTSerial_CPU_CYCLE < 256 * HSTSerial_DELAY_CYCLE + HSTSerial_TXBIT_CYCLE - 1) { __asm__ volatile ( "mov r0, %[_DATA_]" "\n\t" "add r1, r0, r0" "\n\t" "eor r0, r0, r1" "\n\t" "mov r1, #1" "\n\t" "lsl r1, r1, %[_POS_]" "\n\t" "mov r2, %[_PORT_]" "\n\t" "lsl r2, r2, #24" "\n\t" "mov r3, %[_PGRP_]" "\n\t" "lsl r3, r3, #7" "\n\t" "add r2, r2, r3" "\n\t" "CPSID i" "\n\t" "str r1, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (PIN & 31) , [_REG_ ] "I" (HSTSerial_OUTCLR(PIN)) , [_PORT_] "I" (HSTSerial_PORT(PIN) >> 24) , [_PGRP_] "I" ((HSTSerial_PORT(PIN) >> 7) & 0xFF) , [_DATA_] "r" (data) : "r1", "r2", "r3" ); HSTSerial_DELAY(0); __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (PIN & 31) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); HSTSerial_DELAY(1); if ((PIN & 31) >= 1) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 1) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (1 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(2); if ((PIN & 31) >= 2) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 2) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (2 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(3); if ((PIN & 31) >= 3) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 3) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (3 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(4); if ((PIN & 31) >= 4) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 4) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (4 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(5); if ((PIN & 31) >= 5) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 5) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (5 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(6); if ((PIN & 31) >= 6) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 6) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (6 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(7); if ((PIN & 31) >= 7) { __asm__ volatile ( "lsl r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" ((PIN & 31) - 7) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } else { __asm__ volatile ( "lsr r3, r0, %[_POS_]" "\n\t" "and r3, r3, r1" "\n\t" "str r3, [r2, %[_REG_]]" "\n\t" : : [_POS_ ] "I" (7 - (PIN & 31)) , [_REG_ ] "I" (HSTSerial_OUTTGL(PIN)) : "r3" ); } HSTSerial_DELAY(8); __asm__ volatile ( "nop" "\n\t" "nop" "\n\t" "str r1, [r2, %[_REG_]]" "\n\t" "CPSIE i" "\n\t" : : [_REG_ ] "I" (HSTSerial_OUTSET(PIN)) ); HSTSerial_DELAY(9); } } static void write(uint8_t *buf, uint32_t len) { while (len--) write(*buf++); } }; #endif |
【参照ライブラリ】
Seeeduino XIAO (ATSAMD21G18) のGPIO速度