GR-SAKURA
GR-KURUMI
GR-COTTON
GR-CITRUS
GR-PEACH
GR-KAEDE
GR-ADZUKI
GR-LYCHEE
GR-ROSE
GR-MANGO(*)
SNShield
Web Compiler
IDE for GR
TOPPERS関連
女子美コラボ
その他
※プロデューサミーティング中
作り方使い方資料
イベント関連
作品記事
体験記事
ライブラリ
ツール
その他・過去ファイル
RL78/G13用ライブラリに対して、次版のV2.02にSoftwareSerialを入れようと考えています。FTDI USBシリアル変換モジュールを用いて4800, 9600, 57600, 115200bpsでの動作確認を行いました。ソースは以下の通りです。
GR-ADZUKIでは、USB通信用(pin0, pin1)、Wire(pin7, pin8)、モーター(pin9, pin10)でシリアルがつぶれてしまい、ESP8266やXBee等を使ったリモート制御がやりづらい状況でした。SoftwareSerialによって、これを解決したいという理由です。
なお、Arduino(AVR)ではピンチェンジ割り込みというほぼ全端子に外部割り込みが使える機能があるので、ArduinoではSoftwareSerialはほぼ全端子に使えますが、RL78/G13ではそんな機能がないため、シリアルの受信開始用に使用できるピンは外部割り込みがアサインされたpin2(INT0), pin3(INT1)だけとなります。送信はいずれのピンでも大丈夫です。
9月初旬にV-upを考えていますので、何かご意見、コメントありましたらよろしくお願いいたします。
pins_arduino.hも変更してます。
以下、サンプルです。
#include <Arduino.h>#include <SoftwareSerial.h>SoftwareSerial mySerial(2, 3); // RX, TXvoid setup() { // Open serial communications and wait for port to open: Serial.begin(115200); // set the data rate for the SoftwareSerial port mySerial.begin(115200);}void loop() { // run over and over#if 0 mySerial.println("Hello, world?"); delay(100);#else if (mySerial.available()) { Serial.write(mySerial.read()); } if (Serial.available()) { mySerial.write(Serial.read()); }#endif}
SoftwareSerial を使いたい状況というのは他の用途で既に使ってる等の事情で HardwareSerial に余裕がなくそれでも尚シリアル通信が使いたいという場合だと思いますが、
Arduno の SoftwareSerial は送受信時に割り込みを止める仕組みとなっており、↑のような他の通信と同時に使用する用途には向かないと思います。
GR-SAKURA のSoftwareSerial がタイマー割り込みで全部を回す仕組みに組みなおした気がしますが、GR-ADZUKI も同様の方法を採るのが良いのでは。
SoftwareSerial クラスの仕様ですが HardwareSerial クラスと同様の機能を提供すてべきと思います。較べてみると色々違いがあります。SoftwareSerial クラスを HardwareSerial の派生クラスにするのが良いかもしれません。
HardwareSerial クラスも見てみるとメンバ変数 _rx_buffer_head 等が public になってる等、わけわからんところがあるので見直すべきと思う箇所があります。
HardwareSerial::begin() で受信と送信のバッファのサイズが指定できるよういつの間にかなっており、begin() と end() の中で malloc() と free() を呼んでいますがヒープを使用しない様ならないでしょうか? GNURL78 の標準のライブラリではスタックとヒープの領域が厳密に分けられておらず、ヒープ取得のリクエストがあるとスタックとヒープの共用の領域から「ある程度の容量」をヒープとして確保し、その中から要求された容量を確保する動作となるのでメモリの容量的に無駄があります。また、頻繁にヒープの取得と解放を繰り返すとヒープ領域が増大しスタック領域とクラッシュすることゝなるので、「なんか動くけど動かんプログラム」というものができてしまう危険性もあります。RL78 等の RAM の少ないマイコンで、標準関数でヒープを使うことは避けるべきと思います。begin() や end() の中で malloc()/free() するのではなく、ユーザーが静的か動的かで確保したバッファを指定できるようにするのが無難と思います。
> pins_arduino.hも変更してます。 > > pins_arduino.h
↓ が追加されてますが
const uint32_t PROGMEM port_to_output_PGM[] = { 0xFFF00, 0xFFF01, 0xFFF02, 0xFFF03, 0xFFF04, 0xFFF05, 0xFFF06, 0xFFF07, }; const uint32_t PROGMEM port_to_input_PGM[] = { 0xFFF00, 0xFFF01, 0xFFF02, 0xFFF03, 0xFFF04, 0xFFF05, 0xFFF06, 0xFFF07, };
↓で参照するのはおかしいのでは?
extern const uint16_t PROGMEM port_to_input_PGM[]; extern const uint16_t PROGMEM port_to_output_PGM[]; #define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) ) #define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
ビルドすると下記のエラーとなりました。
gr_common/cores/pins_arduino.h:407:24: error: conflicting types for 'port_to_output_PGM' const uint32_t PROGMEM port_to_output_PGM[] = { ^ In file included from gr_common/cores/wiring_private.h:39:0, from gr_common/cores/wiring_digital.c:31: gr_common/cores/Arduino.h:529:31: note: previous declaration of 'port_to_output_PGM' was here extern const uint16_t PROGMEM port_to_output_PGM[]; ^ In file included from gr_common/cores/Arduino.h:628:0, from gr_common/cores/wiring_private.h:39, from gr_common/cores/wiring_digital.c:31: gr_common/cores/pins_arduino.h:418:24: error: conflicting types for 'port_to_input_PGM' const uint32_t PROGMEM port_to_input_PGM[] = { ^ In file included from gr_common/cores/wiring_private.h:39:0, from gr_common/cores/wiring_digital.c:31: gr_common/cores/Arduino.h:528:31: note: previous declaration of 'port_to_input_PGM' was here extern const uint16_t PROGMEM port_to_input_PGM[]; ^ make: *** [makefile:116: gr_common/cores/wiring_digital.o] Error 1 make: *** Waiting for unfinished jobs....
pins_arduino.h の変更は行わずに ↓ の変更で良い気がしますが。
#define portOutputRegister(P) ( (volatile uint8_t *)( P0 + (P)) ) #define portInputRegister(P) ( (volatile uint8_t *)( P0 + (P)) )
> SoftwareSerialですが、115200bpsでFTDIとの通信はできるものの、ESP8266はうまくいかないようです。
ビルドすると通信速度に拠ってビット当たりディレイ時間の計算をしている箇所で
In file included from gr_common/libraries/SoftwareSerial/SoftwareSerial.cpp:44:0: gr_common/libraries/SoftwareSerial/SoftwareSerial.cpp: In member function 'void SoftwareSerial::begin(long int)': ./gr_common/cores/Arduino.h:452:26: warning: integer overflow in expression [-Woverflow] #define F_CPU (32 * 1000 * 1000) ^ gr_common/libraries/SoftwareSerial/SoftwareSerial.cpp:330:25: note: in expansion of macro 'F_CPU' uint16_t bit_delay = (F_CPU / speed) / 9;
以上の警告が出ますが
#define F_CPU (32 * 1000 * 1000L)
等する必要がある気がします。
SoftwareSerial.cpp の 時間待ち部分
void _delay_loop_2(uint16_t __count) { // todo: need to implement assemble at 9 cycles volatile uint16_t count = __count; while(count != 0) count--; }
を GCC for Renesas 4.9.2.201701-GNURL78 を使用して web コンパイラでの makefile 通りのコンパイルオプションでコンパイルすると
Disassembly of section .text._Z13_delay_loop_2t: 00000000 <__Z13_delay_loop_2t>: 0: 20 02 subw sp, #2 00000002 <.LBB83>: 2: a8 06 movw ax, [sp+6] 00000004 <.L20>: 4: b8 00 movw [sp+0], ax 6: 44 00 00 cmpw ax, #0 9: 61 f8 sknz b: ec 00 00 00 br !!0 <__Z13_delay_loop_2t> f: a8 00 movw ax, [sp+0] 11: b1 decw ax 12: ec 00 00 00 br !!0 <__Z13_delay_loop_2t> 00000016 <.L21>: 16: 10 02 addw sp, #2 18: d7 ret
コメントにある通り 1 ループ 9 サイクルのコードが出力されますが、精度は粗く、コンパイル条件が変わると出力コードの内容も保証できないので
void _delay_loop_2(uint16_t __count) { __asm __volatile( " movw ax, %0 \n" " cmpw ax, #0 \n" " bz $9f \n" "0: \n" " subw ax, #1 \n" " bnz $0 \n" "9: \n" : : "r"(__count) : "ax" ); }
等とした方が良いと思います。1 ループ 5 サイクルとなるので、遅延時間を計算している箇所も
// Precalculate the various delays, in number of 5-cycle delays uint16_t bit_delay = (F_CPU / speed) / 5;
等と変更する必要があります。
SoftwareSerial::recv() と SoftwareSerial::write(uint8) が GNURL78 のアレな部分も相まってどう見ても精度が出る訳ないコードとなっているので、インラインアセンブラでも使うべきと思います。
あと、SoftwareSerial::begin(long) の中の
// Note that this code is a _lot_ slower, mostly due to bad register // allocation choices of gcc. This works up to 57600 on 16Mhz and // 38400 on 8Mhz. _rx_delay_centering = subtract_cap(bit_delay / 2, (4 + 4 + 97 + 29 - 11) / 4); _rx_delay_intrabit = subtract_cap(bit_delay - 2, 11 / 4); _rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, (44 + 17) / 4);
は RL78/G13@32MHz 用に適当である気がしません。
ループの分岐先が間違ってたので訂正
void _delay_loop_2(uint16_t __count) { __asm __volatile( " movw ax, %0 \n" " cmpw ax, #0 \n" " bz $9f \n" "0: \n" " subw ax, #1 \n" " bnz $0b \n" "9: \n" : : "r"(__count) : "ax" ); }
ローカルラベルに 0 は一時期の GNURL78 で使用できない不具合があったことを思い出したので更に修正
void _delay_loop_2(uint16_t __count) { __asm __volatile( " movw ax, %0 \n" " cmpw ax, #0 \n" " bz $9f \n" "1: \n" " subw ax, #1 \n" " bnz $1b \n" "9: \n" : : "r"(__count) : "ax" ); }
_delay_loop_2() の 呼び出し ~ 復帰 のコスト等を考えると
// Precalculate the various delays, in number of 5-cycle delays uint16_t bit_delay = (F_CPU / speed) / 5 - 2;
くらいの調整を加味しても良いかも。
Fujitaさん、ありがとうございます!ESP8266との通信に成功しました。アセンブルで精度を上げられたのがでかいです!
アクセスポイントへの接続後のIP取得まででき、milkcocoaサービスへのデータアップまでいけました。ちょっとデータのアップが遅いため、途中でデータ化けが起きている可能性はありそうですが、微調整次第な気はします。以下、bit_delay値はTXをオシロを見つつ、8ビット単位で計算して115,200 Hzに近くなるように(-5)を入れてます。
uint16_t bit_delay = (F_CPU / speed) / 5 - 5;
ーーーーーーーーーーーーーーーーーーーーー
wait...Milkcocoa SDK demo
Connecting to WARPSTAR-1153D8FW Version:to station okJoin AP successIP: +CIFSR:STAIP,"192.168.0.9"+CIFSR:STAMAC,"18:fe:34:ef:08:e2"single okmilkcocoa on sucesssConnecting to MQTT... MQTT Connected!onpush28
ーーーーーーーーーーーーーーーーーーーーーー
以下、変更したソースです。