フォト
2025年3月
            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          
無料ブログはココログ

« 2018年1月 | トップページ | 2018年3月 »

2018年2月の5件の記事

2018年2月17日 (土)

Arduino Unoの内部EEPROMの利用(書込みの高速化)に関するメモ

Arduino Unoの内部EEPROMへのデータ書き込みに関するメモです。


先日「豊四季タイニーBASIC for MW25616L実験用表示モジュール」の実装で、
プログラムを内部EEPROMに保存しています。

方法としては、Arduino標準クラスライブラリEEPROM を使っています。
(Arduino リファレンス - EEPROM Library

  #include <EEPROM.h>

  // 内部EEPROMメモリへの保存
  topAddr = EEPROM_PAGE_SIZE*prgno;
  for (uint16_t i=0; i < SIZE_LIST; i++) {
    EEPROM.write(topAddr+i,listbuf[i]); 
  }

512バイトのデータ書き込みは、上のような感じでやっています。
クラスライブラリEEPROM は1バイト単位でしか書き込みが出来ないためか、
書込みに時間がかかります(感覚的に2,3秒くらい?)。

改善策はないかと、EEPROM.h のソースを読むと、
#include <avr/eeprom.h> を読み込んでおり、内部EEPROM用のAPIが存在することが
分かりました。

eeprom.hのソースを読むと、ブロック単位の書込みが行えるAPI等色々と用意されています。
内部EEPROM用のAPIを使う方法に修正しました。

  #include <avr/eeprom.h>

  // 内部EEPROMメモリへの保存
  topAddr = EEPROM_PAGE_SIZE*prgno;
  eeprom_update_block(listbuf,topAddr,SIZE_LIST);

書込みには下記のAPIを使っています。

  void eeprom_update_block (const void *__src, void *__dst, size_t __n);

試してみると、ほぼ瞬時に書き込みを行うことが出来ました。
コードも短くなり、フラッシュメモリの消費量も減りました。
内部EEPROM用のAPIは、#include <avr/eeprom.h> でヘッダーファイルをインクルード
することで利用出来ます。

クラスライブラリEEPROM を使うよりも、APIを使った方がお手軽に利用できます。

用意されているAPIはeeprom.hを直接覗いてみれば確認できます。
arduino-1.8.5\hardware\tools\avr\avr\include\avr\eeprom.h

2018年2月15日 (木)

アクティブマトリクス蛍光表示管の実験用表示モジュールの試用 (2)

間が空きましたが、
アクティブマトリクス蛍光表示管(CL-VFD) MW25616L 実験用表示モジュールの試用の続きです。

VFD

前回の続きからは方向を変え、
「豊四季タイニーBASICを乗せて制御すれば面白いのでは?」と思い、やってみました。

モジュール的にはArduino互換なので、先日メモリー節約したバージョンに
micro:bit版の機能を一部を結合して実装しました。
micro:bit版の流用で2、3日くらいで実装できました。

下記のサイトにてとりあえず公開いたします。
興味のある方は参照願います。
・豊四季タイニーBASIC for MW25616L実験用表示モジュール
   https://github.com/Tamakichi/ttbasic_MW25616L

リファレンス・マニュアルも付けました。
文法やコマンド等の詳細については、リファレンス・マニュアルを参照して下さい。
 
作成には、豊四季タイニーBASICのオリジナル版を利用しています。
また、VFDの制御には、「VFDふぁん」さんが公開している参考プログラムを利用しています。

さて、このバージョンで、どんなことが出来るかというと、

シリアル接続して、直接コマンドを打って、
VFDにメッセージ文の表示が可能です。全角文字もそのまま入力出来ます。

02

コマンドで簡単に表示できるのは、やはり良いです!
従来(micro:bit版)のコマンドにVDF制御のために、以下の専用コマンドを用意しました。

専用コマンド一覧

VBRIGHT :輝度を0(0%)~255(100%)の範囲で設定します。
  例:VBRIGHT 100

VDISPLAY :表示のオン・オフを設定します。
  例:VDISPLAY OFF

VMSG :指定したメッセージ文を指定速度でスクロール表示します。 
   例:VMSG 10,"日本語のメッセージ表示テスト"

VSCROLL :指定長の空白を指定速度でスクロール表示します。
   例:VSCROLL 256,10

VPUT :表示バイトデータを指定速度で直接指定します。
  例:VPUT MEM,16,10

逆に、メモリ的制約からいくつかのコマンドを削除しています。

プログラム例

コマンドを使ったプログラム作成して、ちょっとした動きのある表示も出来ます。
プログラム作成はシリアル接続のスクリーンエディタにて入力出来ます。

10 'VDF表示サンプル
20 TONE 440,100:TONE 880,100
30 @(0)="熱いぞ!埼玉!"
40 @(1)="豊四季タイニーBASIC"
50 @(2)="VFD対応バージョン"
60 @(3)="只今開発中!"
70 @(4)="ちょっとメモリー的に厳しい.."
80 VBRIGHT 255
90 FOR S=0 TO 4
100 VCLS:VMSG 10,STR$(@(S))
110 FOR I=0 TO 255 STEP 15
120 VBRIGHT 255-I:WAIT 40
130 NEXT I
140 WAIT 500
150 VBRIGHT 255:WAIT 500
160 VSCROLL 256,3
170 WAIT 500
180 NEXT S
190 GOTO 80
I2C接続のRTCモジュールを使って時刻をリアルタイム更新表示するプログラム
(2018/08/06 修正:10行のVBRIGHT 100をVBRIGHT 255の修正)

03

10 VCLS:VBRIGHT 255
20 @(0)="時","分","秒"
30 M=ARRAY+6:POKE M,0
40 R=I2CR($68,M,1,M+1,7)
50 VMSG -1,"現在の時刻は"
60 FOR I=0 TO 2
70 VMSG -1,HEX$(PEEK(M+3-I),2);STR$(@(I))
80 NEXT I
90 VMSG 0,"です"
100 WAIT 1000
110 VSCROLL 256,-1
120 GOTO 40

スライド抵抗器でVFD上に表示しているバーをドット単位で左右に動かすプログラム

05

10 @(0)=$FFFF
20 A=ANA(20)>>2:IF A=0 A=1
30 VPUT ARRAY,2,-1
40 VSCROLL A,0
50 VSCROLL 256,-1
60 WAIT 20
70 GOTO 20

色々と機能てんこ盛りの結果、フラッシュメモリの利用率は99%!
メモリ容量的に、micro:bit版と比べると色々と制約が発生しました。
  ・プログラムサイズは 512バイトまで
  ・内部EEPROMに保存できるプログラムは2本まで

  ・変数はA~Z、配列変数 @(0)@(15)まで
  ・スクリーンエディタの画面サイズは 横48文字x縦8行 固定

他に色々と制約があります。

暫く、動作確認をして修正を行っていきます。
スケッチは一部を修正すれば、Arduino UNOでも利用出ます。

参考サイト
・VFDふぁん - MW25616L 実験用表示モジュールを使ってみる(2)


追記 2017/02/15(夜)

メモリーの制約からフルスクリーンエディタの画面サイズが横48文字x縦8行はとなり、
長い行のプログラムの作成においては、視野の狭い範囲でのスクロール編集となり、
ちょっと使いづらいです。

そこで、フルスクリーンエディタを有効・無効設定するコマンドを追加しました。

Photo

スクリーンエディタ機能を無効にすると、行単位での編集しか出来ないですが、
これはこれで良いかも。

無効にすると、逆スクロール編集、任意の位置にカーソルを移動しての編集、
画面制御を行うコマンド(LOCATE等)が機能しなくなりますが、
スクリーンエディタ機能を臨機応変に切り替えて利用することで、
とりあえずは実用としてしのげそうです。

2018年2月11日 (日)

豊四季Tiny BASIC for ArduinoのSRAM消費軽減対応

豊四季Tiny BASIC for Arduino(オリジナル版)のSRAM消費を軽減する修正を行ってみました。

Arduino UNOでは、SRAMが2kバイトしかありません。
オリジナル版のまま利用した場合、グローバル変数が1481バイト(72%)を利用しており、
残りが567バイトです。機能拡張するにはちょっと辛いです。

そこで、キーワード、エラーメッセージ等のグローバルな固定データをフラッシュメモリ上に
配置する修正をおこない、SRAMの消費を少々押さえました。

参考にしたサイト

・Arduinoで遊ぶページ - Arduino Uno編 - メモリの種類
・武蔵野電波 - Arduino 日本語リファレンス - PROGMEMとFマクロ
・Arduino - PROGMEM

良く知られているテクニックですが、
Arduino(AVRマイコン)でPROGMEMキーワードを使って変数の宣言を行うことで、
SRAMではなく、フラッシュメモリ上にデータを配置することが出来ます。

ただし、データの参照がちょっと面倒で、専用の参照を行う関数を利用する必要があります。
データ構造にも制約があり、キーワードテーブル等で使う2次元配列は対応されていません。
入れ子になる1次元配列を定義してから、その一次元配列のアドレスを配列として宣言して
2次元配列っぽい構造を作成します。

修正前のキーワードテーブル(オリジナル版よりソースを引用しています)

// Keyword table
const char *kwtbl[] = {
  "GOTO", "GOSUB", "RETURN",
  "FOR", "TO", "STEP", "NEXT",
  "IF", "REM", "STOP",
  "INPUT", "PRINT", "LET",
  ",", ";",
  "-", "+", "*", "/", "(", ")",
  ">=", "#", ">", "=", "<=", "<",
  "@", "RND", "ABS", "SIZE",
  "LIST", "RUN", "NEW"
};

修正したキーワードテーブル

// Keyword table
#define KW(k,s) const char k[] PROGMEM=s  // キーワード定義マクロ
KW(k000,"GOTO"); KW(k001,"GOSUB"); KW(k002,"RETURN");
KW(k003,"FOR"); KW(k004,"TO"); KW(k005,"STEP"); KW(k006,"NEXT");
KW(k007,"IF"); KW(k008,"REM"); KW(k009,"STOP");
KW(k010,"INPUT"); KW(k011,"PRINT"); KW(k012,"LET");
KW(k013,","); KW(k014,";");
KW(k015,"-"); KW(k016,"+"); KW(k017,"*"); KW(k018,"/"); KW(k019,"("); KW(k020,")");
KW(k021,">=");KW(k022,"#"); KW(k023,">"); KW(k024,"="); KW(k025,"<="); KW(k026,"<");
KW(k027,"@"); KW(k028,"RND"); KW(k029,"ABS"); KW(k030,"SIZE");
KW(k031,"LIST"); KW(k032,"RUN"); KW(k033,"NEW");

const char*  const kwtbl[] PROGMEM = {
 k000,k001,k002,
 k003,k004,k005,k006,
 k007,k008,k009,
 k010,k011,k012,
 k013,k014,
 k015,k016,k017,k018,k019,k020,
 k021,k022,k023,k024,k025,k026, 
 k027,k028,k029,k030,
 k031,k032,k033,
};

KW()マクロは、

const char k000[] PROGMEM="GOTO";
const char k001[] PROGMEM="GOSUB";

と定義するのを省略するのに利用しています。
テーブルの参照はちょっと面倒です。
キーワード比較を行う部分は、一旦、SRAM上のメモリーにコピーして利用しています。

char* pkw = 0; //ひとつのキーワードの内部を指すポインタ
char kwtbl_str[16]; // コマンド比較用

// オリジナル
pkw = (char *)kwtbl[i]; //キーワードの先頭を指す

// 修正版
pkw = strcpy_P(kwtbl_str, (char*)pgm_read_word(&(kwtbl[i])));

ARM cortex-M系のgccでは、constキーワードを付ければ、
フラッシュメモリ上に配置してくれて、かつ参照はSRAMと同等に行えるのと比べると
ちょっと面倒ですね。

とりあえず、この修正によりSRAM消費量を712バイト抑えることが出来ました。
SRAMは1279バイトほど余っているので、ちょっとした機能追加は出来ると思います。

もしかしたら、もっと効率の良いやり方があるかもしれませんが、これで良しとします。

Arduino IDE 1.8.15

ちょっといじった感じでは、オリジナル版と同じ感じで動いています。

ttbasic

この修正版は、下記にて公開いたいます。
https://github.com/Tamakichi/ttbasic_arduino_uno


2018年2月 7日 (水)

micro:bitをArduino環境で使う (7) シリアル通信

micro:bitではハードウェアシリアル通信ポートが1つ利用出来ます。

Arduino環境で利用する場合、USB経由のシリアル通信専用に割り付けれています。

具体的にはポート21、22に割り付けられていているのですが、このポートは
外部端子には接続されていせん。

しかし、ハードウェア的にmicro:bitではシリアル通信ポートを任意の外部端子に
割り付けることが出来ます。

Arduino環境でも任意のポートに割り付けられないかとやってみました。
まずは、Serialインスタンスを実装しているUartクラスを使って試してみます。

スケッチ(注意:このスケッチはダメだったケースです)
//
// micro:bit シリアルポートの利用
// 任意のポートを使ってシリアル通信を行う
//

#define Rxpin 12         // シリアル RxDピン(任意指定可能)
#define Txpin 13         // シリアル TxDピン(任意指定可能)
#define baudRate 9216000 // 通信速度(1200 ~ 921600bps)

// Uartインスタンスの生成
Uart Serial2(NRF_UART0, UART0_IRQn, Rxpin, Txpin); 
//extern "C"  void UART0_IRQHandler() {  Serial2.IrqHandler(); };

void setup() {
  Serial2.begin(baudRate); // 通信開始
}

void loop() {
  Serial2.println("Hello,world");
  delay(1000);
}

上記の例では、Uartクラスを使ってデフォルトのSerialオブジェクトとは
別のインスタンスを生成しています。
インスタンス生成時に利用するポートを指定することが出来ます。

外部端子を使う利点として、通信速度に115200以上の指定が可能です。
(USB経由だと115200までしか利用出来ない)

試しに、上限の921600bpsを指定してみると、TeraTermにで問題なく出力できました。
(パソコンに接続にはUSB-Serialモジュールを利用しています)

送信については、任意のポートに割り付けて利用することが出来ました。

01

ところが、受信処理を実装するとデータ受信を行うことが出来ません。
問題点を調べると、
シリアル受信を行うコールバック関数(割り込み処理)が
デフォルトのSerialインスタンスに固定されています。

Uart.cpp
extern "C"

{
  void UART0_IRQHandler()
  {
    Serial.IrqHandler();
  }
}

Arduinoのコア部分で上記のように固定されており、
これを修正することが出来ません。
本来ならば
  Serial.IrqHandler();

  Serial2.IrqHandler();
に変更しないといけません。

IrqHandler()の実体は下記のようになっており、
Serialインスタンスのリングバッファにデータを格納しています。
これでは、後から生成したSerail2インスタンスでのデータ受信を行うことが出来ません。


void Uart::IrqHandler()
{
  if (nrfUart->EVENTS_RXDRDY)
  {
    rxBuffer.store_char(nrfUart->RXD);
    nrfUart->EVENTS_RXDRDY = 0x0UL;
  }
}

この方法は諦めて、Arduino環境のUart.h、Uart.cppに修正を入れることにしました。
ポート設定を行うメンバー関数setPort()を追加しました。

Uart.hの修正:下記の2つのメンバー関数の宣言の追加

// <-- add by Tamakichi 2018/02/07
    void setPort(uint8_t _pinRX, uint8_t _pinTX);
    void setPort(uint8_t _pinRX, uint8_t _pinTX, uint8_t _pinCTS, uint8_t _pinRTS );
// -->>     

Uart.cppの修正:下記の2つのメンバー関数の本体の追加

// <-- add by Tamakichi 2018/02/07
void Uart::setPort(uint8_t _pinRX, uint8_t _pinTX)
{
  uc_pinRX = g_ADigitalPinMap[_pinRX];
  uc_pinTX = g_ADigitalPinMap[_pinTX];
  uc_hwFlow = 0;
}

void Uart::setPort(uint8_t _pinRX, uint8_t _pinTX, uint8_t _pinCTS, uint8_t _pinRTS)
{
  uc_pinRX = g_ADigitalPinMap[_pinRX];
  uc_pinTX = g_ADigitalPinMap[_pinTX];
  uc_pinCTS = g_ADigitalPinMap[_pinCTS];
  uc_pinRTS = g_ADigitalPinMap[_pinRTS];
  uc_hwFlow = 1;
}
// -->>

この追加したメンバー関数を利用したスケッチ

//
// micro:bit シリアルポートの利用
// 任意のポートを使ってシリアル通信を行う
// 本スケッチはArduinoのUart.h、Uart.cppの修正が必要
//

#define Rxpin 12         // シリアル RxDピン(任意指定可能)
#define Txpin 13         // シリアル TxDピン(任意指定可能)
#define baudRate 9216000 // 通信速度(1200 ~ 921600bps)

void setup() {
  Serial.setPort(Rxpin, Txpin);
  Serial.begin(baudRate); // 通信開始
  Serial.println("Hello,world");
}

void loop() {
  if (Serial.available()) {
    Serial.write(Serial.read());
  }
}

実行結果

02

これで、とりあえずは任意のポートを利用した送受信を行うことが出来ました。
ただし、Arduino環境に修正を加える必要があります。

2018/5/11 追記

シリアル通信のためのUart.h、Uart.cppの修正、もっと簡単な方法で修正出来ました。
Uart.hの中で
#define ARDUINO_GENERIC
を追記することで、追加した関数と同等のsetPins()が有効になります。

#include <nrf.h>

#include "HardwareSerial.h"
#include "RingBuffer.h"

#include <cstddef>
// <-- add by Tamakichi 2018/05/06
#define ARDUINO_GENERIC
// -->
class Uart : public HardwareSerial
{
  public:
    Uart(NRF_UART_Type *_nrfUart, IRQn_Type _IRQn, uint8_t _pinRX, uint8_t _pinTX);
    Uart(NRF_UART_Type *_nrfUart, IRQn_Type _IRQn, uint8_t _pinRX, uint8_t _pinTX, uint8_t _pinCTS, uint8_t _pinRTS );
#ifdef ARDUINO_GENERIC
    void setPins(uint8_t _pinRX, uint8_t _pinTX);
    void setPins(uint8_t _pinRX, uint8_t _pinTX, uint8_t _pinCTS, uint8_t _pinRTS);
#endif // ARDUINO_GENERIC

2018年2月 4日 (日)

豊四季Tiny BASIC for micro:bit をV0.07に更新しました

豊四季Tiny BASIC for micro:bit をV0.07に更新しました。
公開サイト
  https://github.com/Tamakichi/ttbasic_microbit

V0.06からの変更点
 ・スクリーンエディタの機能強化
    - 全角文字(シフトJIS)対応
    - [F7]キー : 行の分割
    - [F8]キー : 行の結合
    - [DEL]、[BS]でブランク行の削除

  Windows 10上のTeraTermでしか動作確認していませんが、
  IMEでの全角入力に対応しました。
  プログラム中のコメントや文字列に全角文字が利用出来ます。

  弊害としては、日本語コードを通すためにキーコードの一部を変更しました。
  INKEY()でキー判定を行うプログラムでは修正が必要です。
   - [UP]、[DOWN]、[RIGHT]、[LEFT]、[ページUP]、[ページDOWN]、[HOME]、[END]
     等のキーコードを変更しました。

  今後の修正もありうるので、キーコード用の定数を用意しました。
   - KUP、KDOWN、KRIGHT、KLEFT、KSPASE、KENTER   

・全角文字列用関数・コマンドの追加
  半角文字関連を扱う関数の全角対応版を用意しました。
   - WLEN()        : 文字数の取得
   - WCHR$()      : SJISコードから文字への変換
   - WASC()     : SJIS文字のSJISコードの取得
   - WCSTR$() : 変数が参照している文字列の出力

・美咲フォント(8x8ドット 教育漢字)対応
  フラッシュメモリに教育漢字(+英数記号・ひらがな・カタカナ)を乗せました。
  利用頻度の高い1710文字が利用出来ます。
  SJISコードにて該当するフォントデータを参照することが出来ます。
   - WADR(SJISコード)  : 指定したSJISコードに対応するフォントデータ格納アドレス取得

・文字列入力関数の追加
  文字列(全角を含む)入力を行う関数を追加しました。
   - GETS()     : 入力した文字列を指定アドレスに格納しそのアドレスを返す。

・不具合対応
  - BIN$(0)がブランク表示となる不具合の対応
  - MATRIX OFFでのポート初期化しわすれの対応
  - LIST表示のIF文で空白がつまる現状の対応
     (新:IF X=CW  X=X-1 旧:IF X=CWX=X-1)


今回の修正で出来るようになった例です。
下記のプログラム実行例は、コメントや文字列に全角を利用しています。
また、"あ"に対応する美咲フォントのデータを参照してそのパターンを表示しています。

  02

次に、Neopixcelマトリックスタイプを使って、文字を表示する例です。
 

プログラムの次のような感じです。

10 'NeoPixelで文字表示
20 SETFONT 0,$50,$A8,$88,$88,$70
30 MSG TOP,0,CHR$(0)
40 NPBEGIN 12,64
50 NPCLS
60 S="こんにちは さい玉":C0=RGB8(0,2,3)
70 FOR I=1 TO WLEN(S)
80 A=WADR(WASC(S,I))
90 FOR Y=0 TO 7
100 D=PEEK(A+Y)
110 FOR X=0 TO 7
120 IF D&($80>>X) C=C0 ELSE C=0
130 IF Y&1 POKE MEM+Y*8+X,C ELSE POKE MEM+Y*8+7-X,C
140 NEXT X
150 NEXT Y
160 NPPUT 0,MEM,64,1
170 WAIT 400
180 NEXT I
190 GOTO 70

機能拡張は、このあたりで一旦止めて、サンプルプログラム等を充実させていきたいと思います。

« 2018年1月 | トップページ | 2018年3月 »