Arduino-IDE活用
1 はじめに
2 準備するもの
3 テストラン
4 winavr流に書いてみる
5 RTCモジュール8564NBの読み書き(I2C)
6 SDカードR/Wモジュールの試用
7 SDカード(スタンダードサイズ)R/Wモジュール
8 マイクロSDカードに温度を記録する
9 Arduino-IDEメモ MEMO
10 RTC DS3231モジュールの実験
11 atmega328を UNO ボードとして使う(ROMが大きい) その2も
12 IDEのアップデート 1.8.5→1.8.9
13 UNO8で諸テスト
14 mega88のボード設定
1 はじめに
Arduinoボード(あるいは互換ボード)を購入して、周辺回路のシールドを加えて求める機能を実現するのであれば正規の方法に従えば良いのですが、私の場合はそれをまったく考えていません。
流れとして自由な回路を考えてavr-gccでコンパイルして作るのが私の楽しみなのです。 エディタでたどたどしいプログラムを書きgccでコンパイルしてエラーがあればその解決方法を考え出すのが楽しみの一つでもあります。
日常的な環境として、Cで書いてhexファイルを作る環境、hexファイルをデバイスにロードする環境が整っています。
そこで、Arduino-IDEで書いたプログラムが動くハードウエアを準備して、Arduino-IDEの多くのライブラリを利用したAVRプログラムを考えて本来のCによるプログラム作りに役立てようとするのがここでのArduino-IDEの活用法です。Arduinoを楽しむ目的でないことをご承知下さい。
したがってそのプロセスは、第一にArduino-IDEが適用できるハードウエアを準備して、Arduino-IDEでプログラム(スケッチ)を書いて、生成されたhexファイルをavrプログラマで書き込む、となります。
具体的には、ATmega328を16MHzで使用するハードウエアを準備して、それに該当するArduinoボードを仮に割り当て、Arduino-IDEを用いてコンパイルします。ArduinoシステムはUARTを通じてプログラムを書こうとしますがその機能は準備せず別のアップロードソフトであるsenshuさんのavrdude-GUIとライタはpic18spxかHIDaspxを使う方法です。
C単独では到達しにくい部分にArduinoライブラリの補助で近づこうと考えるのがArduino-IDEを併用する理由です。実際にI2Cルーチンの学習には非常に効果的でした。
2 準備するもの
準備するものは次のとおりです。
1 ハードウエア ATmega328ボード: 16MHz水晶クロック リセットsw IPSコネクタ UARTはTDとRDのコネクタ 5V電源端子 抵抗つきLED(19番ピン)
(2018.07.01) 周辺素子の関係で3.3Vで使用するときは8MHzの水晶に変更しています。
2 Arduino-IDE:このページからwindowsを選択してarduino-1.0-windows.zipをダウンロードしました。解凍して適当な場所に置きます。以前の版とは違ってファイル群をその場所に置くだけで使えるようです。付属のファイル群を特定のフォルダに作ることはなくなったようです。マイドキュメントにホルダができるようですが当初は空になっています。ショートカットをデスクトップに作っておきます。
(2018.07.01)現在のArduino-IDEバージョンは 1.8.5 です。C:\_0MicroComp\に置いています。
3 書き込みソフト:avrdude-GUIを使います。多くのプログラマが使えること、Arduino-IDEが生成するhexファイルの場所を簡単に指定できること、Arduino-IDEが作ったtempフォルダの無用のファイル群を一括削除する機能があること、から最適なソフトだと思います。
4 プログラマ(ハードウエア):日頃から pic18spx を多用しています。 HIDaspx や他のものでも変わりません。
3 テストラン
Arduino-IDEの起動:ショートカットから起動します。
使用ボードの選択:起動画面から「tools」→「board」を選択して「Arduino Duemilanove w/ ATmega328」にしました。
プログラム(スケッチ)のロード:「File」→「Exampls」→「1.Basics」と進み、「blink」を選択します。
コンパイル:変更することなく操作アイコンの左から2つ目の○に矢印(upload)をクリックします。少しの時間を経て「Binary sketch size: 1026 bytes (of a 30720 byte maximum)」と表示されますのでコンパイルが終わったことがわかります。この後Arduino-IDEはUART経由でプログラムを書き込もうとしますがハードウエアが繋がっていないのでエラーになります。が放置します。
プログラムの書き込み:avrdude-GUIを起動して(ライタの条件を設定して)、プログラムの「write」をクリックすると自動的にhexファイルを読み出してAVRに書き込みます。ダメなときはもう一度クリックします。ここでLEDが2秒周期で点滅すると関係のハードとソフトは問題なく完成していることになります。
おまけ:プログラムの delay(1000); を delay(200); に変更して書き込んでみます。点滅がはやくなるはずです。
4 winavr流に書いてみる
setup(){}とloop(){}の条件は守ってwinavr流にプログラムを書いてみました。
seup()はリセット後に一度だけ実行されるルーチンで、loop()はその後続けられるルーチンです。
#include <avr/io.h>
#include <util/delay.h>
uint8_t d=32;
void setup(){
DDRB=0b00100000;
}
void loop(){
PORTB=d;
_delay_ms(250);
PORTB=0b00000000;
_delay_ms(750);
}
専用プログラム(スケッチ)と同じ動作になります。Arduino-IDEはWINAVRの文法がそのまま使えそうです。
このことからwinavr(avr-gcc)のプログラムを考えるときの一つの方法としてArduino-IDEが使えると考えます。
5 RTCモジュール8564NBの読み書き
秋月がら購入したこのRTCモジュールはI2C通信によるインターフェイスです。以前にも報告していますが復習のために改めて試行しました。
Arduino-1.0になって一部取り扱いが変更されています。
ブレッドボード上に下記の回路を組み立てました。
プログラム:
RTCの基本的な設定(書き込み)と読み出しテストが目的です。このRTCはI2Cですから当然I2Cの取り扱いの復習も目的になります。
設定は時分秒だけとして日付データなどは設定しません。24時間時計としてTeraTermから時刻設定と1秒毎の時刻表示をさせます。継続して使用できるようにスーパーキャパシタでバックアップをして、その時のために時刻設定をするか、継続時刻を表示させるかをTeraTermから指定できるようにします。リセットするとTeraTermに時刻設定の有無をたずね、'y'ならhhmmss6桁の入力を求め、'n'なら継続時間を表示します。
プログラムは次のようになりました(avr-gccを日頃扱っていますのでその流儀になっています)。
/* rtc_8564nb2 時刻設定文字列を使う */
#include <Wire.h>
uint8_t data[7]; //RTC バッファ
char str[8]; //時刻用文字列
char ans;
uint8_t i,d=99,hh,mm,ss;
#define RTCaddress 0xa2 >> 1 //TWI address of RTC-unit
#define inPin 8 //set digital8 as input
void setup() {
pinMode(inPin, INPUT);
Serial.begin(19200);
Wire.begin(); // I2Cマスタとして使用
Serial.println("set time? y/n");
while(Serial.available()== 0){};
ans=Serial.read();
Serial.println(" ");
if(ans=='y'){
Serial.print("hhmmss ? ");
while(Serial.available()<7){};
i=0;
while (Serial.available()!= 0 ){ //読み取りできる間
str[i] = Serial.read();
i++;
if(i==7){break;}
}
hh=(str[0]-'0')*16+str[1]-'0';
mm=(str[2]-'0')*16+str[3]-'0';
ss=(str[4]-'0')*16+str[5]-'0';
Wire.beginTransmission(RTCaddress); // マスタから書込開始(7ビットスレーブアドレス)
Wire.write(uint8_t(0x00)); // 書込開始の内部アドレス指定
/* initial time */
Wire.write(uint8_t(0x00)); //controlバイト1 = 0x00 第1バイト書込
Wire.write(uint8_t(0x00)); //controlバイト2 = 0x00 第2バイト書込
Wire.write(ss); //秒
Wire.write(mm); //分
Wire.write(hh); //時 最終バイト書込
Wire.endTransmission(); //書込終了
delay(10);
}// endif ans=y
Serial.println(" ");
Serial.println("finished initializing RTC unit 初期化完了"); //init message
}
void loop(void){
Wire.beginTransmission(RTCaddress); // マスタから書込開始 読み取り開始内部アドレス指定のため
Wire.write(uint8_t(0x02)); // 内部アドレス0x02から読み取り
Wire.endTransmission(); // 書込終了
Wire.requestFrom(RTCaddress,3); // マスタから読み取り開始 読み取りバイト数指定
/* 読み取りループ */
i=0; //
while (Wire.available()!= 0 ){ // 読み取りできる間 連続読み取り
data[i] = Wire.read(); //
i++; // 終了の指定はしない
}
if(d!=data[0]){ //秒の値が変化したら表示
Serial.print(data[2] & 0x3f,HEX); //時標示 上位2ビット不要(マスクすること)
Serial.print(" ");
Serial.print(data[1] & 0x7f,HEX); //分標示 上位1ビット不要(マスクすること)
Serial.print(" ");
Serial.print(data[0] & 0x7f,HEX); //秒標示 上位1ビット不要(マスクすること)
Serial.println(" ");
d=data[0];
}
}
|
Arduino-1.0から変更された点は
Wire.send() → Wire.write() に、
Wire.recieve() → Wire.read() になったことと、
このときの引数に 0x00 が使えたものが バイト型指定をせねばならないこと。
すなわち、byte(0x00) とするか uint8_t(0x00) とキャストする必要が生じたことです。
一般にI2Cのread/writeシーケンスは次の手順で行われますが、
書き込みシーケンス
スタート・コンディション発行
コントロールバイト送信(writeアドレス)
内部アドレス(H)送信
内部アドレス(L)送信
第1データ(バイト)送信
第2データ(バイト)送信
最終データ(バイト)送信
ストップ・コンディション発行
|
読み取りシーケンス
スタート・コンディション発行
コントロールバイト送信(readアドレス)
内部アドレス(H)送信
内部アドレス(L)送信
リピート・コンディション発行(AVRではs・cと同じ)
第1データ(バイト)受信(ACK応答)
第2データ(バイト)受信(ACK応答)
最終データ(バイト)受信(NACK応答)
ストップ・コンディション発行
|
Arduino-IDEでは形を変えた関数になっているようです。これらに相当する部分をプログラム中に赤字のコメントで入れました。
マスタからスレーブを読み書きするときは赤字の部分に注意してプログラムすると良いでしょう。
なお、シリアル通信ではTeraTermで受ける場合に UTF-8 UTF-8m に設定が必要です。SHIFT-JISでは文字化けしました。
上記のプログラムで 5,122バイトのhexファイルになりました。avr-gccの2倍程度の量になるかと思います。mega328を使って簡単なものを作るには問題がないようです。
I2Cの通信方法がavr-gccではわからないときにこのRTCの読み書きができたことはその後のI2Cの学習に大きな役割を果たしました。Arduino-IDEの文法も必要ですから負担にはなりますが一つの方法で行き詰まったときの解決策としてArduino-IDEを使うのも利用法の一つではないかと考えます。
(付) ブレッドボードの配線方法を変えました。細い線では接触不良になる危険を感じますので、0.65mmのUEWを買いました。線の長さはデバイダで測り紙の上に写して、それより14mm長く切ります。両端をハンダで被膜を剥がしてラジオペンチに付けた7mmの印に合わせて直角に曲げます。手間はかかりますが『適当に』切っていたときより気持ち良く配線できました。写真の太いUEW部分です。(コネクタに接続している部分は前のままです)
もどる
6 SDカードR/Wモジュールの試用
2016.06.25
アマゾンで HiLetgo Micro SD メモリ カード シールド モジュール 6 ピン SPI が出ていましたので試しに買ってみました。送料込みで¥250でした。レビューにarduinoで動作するとありましたので実験してみました。aruduinoのボードではなくATmega328をブートローダーなく使っていますので適当にminiかunoでコンパイルしてAVR用のライタで書き込んでいます。
とりあえず試運転 2016.06.25
ATmega328、8MHzX'talのブレッドボードにモジュールを差し込みました。そのままでは挿入長さが足りないのでアダプターを介しています。接続は次のとおりです。
SDモジュール mega328
1 GND GND
2 Vcc Vcc
3 MISO 18 MISO
4 MOSI 17 MOSI
5 SCK 19 SCK
6 CS 16 SS (aruduinoのpin10)
この他に、PCとteratarm通信用に 2 RXD と 3 TXD 及びGNDをつないでいます。
(もちろん Xtal、RESET、ISP回路を用意してあります。電源は3.3Vです。)
プログラムはaruduino IDEから、
File→スケッチの例→SD→CardInfo を選びました。aruduinoボードは pro mini 8MHz 3.3V としています。
プログラムは次のとおりですが、SS端子だけ4を10に変更しています。
/*
SD card test
This example shows how use the utility libraries on which the'
SD library is based in order to get info about your SD card.
Very useful for testing a card when you're not sure whether its working or not.
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
** CS - depends on your SD card shield or module.
Pin 4 used here for consistency with other Arduino examples
created 28 Mar 2011
by Limor Fried
modified 9 Apr 2012
by Tom Igoe
*/
// include the SD library:
#include <SPI.h>
#include <SD.h>
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
const int chipSelect = 4; ← ここは 10 に変更した
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("\nInitializing SD card...");
// we'll use the initialization code from the utility libraries
// since we're just testing if the card is working!
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
return;
} else {
Serial.println("Wiring is correct and a card is present.");
}
// print the type of card
Serial.print("\nCard type: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unknown");
}
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
if (!volume.init(card)) {
Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
return;
}
// print the type and size of the first FAT-type volume
uint32_t volumesize;
Serial.print("\nVolume type is FAT");
Serial.println(volume.fatType(), DEC);
Serial.println();
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
volumesize *= 512; // SD card blocks are always 512 bytes
Serial.print("Volume size (bytes): ");
Serial.println(volumesize);
Serial.print("Volume size (Kbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Mbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);
// list all files in the card with date and size
root.ls(LS_R | LS_DATE | LS_SIZE);
}
void loop(void) {
}
スケッチ → コンパイルしたバイナリを出力 を選んでファイルに納め、いつものとおりavrdude-GUIとHIDaspxで書き込みました。ただし、モジュールのSPIとAVRの書き込みが干渉してライタが動きません。書き込み時はSDモジュールを抜いておきます。
フォルダに自動車のドライブレコーダーで使っていた4GBのマイクロSDを挿入し、teratarmを立ててresetしたところ次のような表示が出ました。
とりあえず「使えるらしい」といったところです。
2016.07.08追記
手元に4GBHCが2枚と2GBが1枚ありますが、4GBHCの内の1枚は SD2 と認識するのですがフォーマットしろ、と表示されて内容は読み取れません。USB接続のリーダー(¥100均品)ではフォーマット、書き込み、読み取りすべてOKなのにこの装置では使えませんでした。原因は不明です。
もどる
7 SDカード(スタンダードサイズ)R/Wモジュール
アマゾンでスタンダードサイズのSDカード用モジュールがありましたので買いました。(EasyWordMall SDカードスロットソケットリーダーモジュールArduino用 ¥225)
3.3V電源レギュレータとプルアップ抵抗が付いているだけです。これで5V使用できるのかと少し疑問ですが、私は3.3Vで使うので問題はありません。
上記のマイクロSDソケットとパラレルに接続して抜き差しでどちらも実験できるようにしました。
実は、手持ちに64MBminiSDがあって、これが使えるかどうかを見たかったのです。結果として、3枚の内2枚は書き込みができました。残りの1枚はSD1と認識するのですがフォーマットせよとのメッセージが出て書き込みができません。windowsではまったく認識されません。原因不明です。 64MBのSD(GBではありません)はもう求めることもできないと思いますが、単純に計算して58バイトのデータが100万個記録できるこtになります。毎秒58バイトのデータを保存しても1年はゆうに記録できます。
もう一つ不思議な事がありました。マイクロSDモジュールで使用できなかった1枚の4GBカードが、サイズアダプタを使えばスタンダードモジュールで書き込みができました。これはwindowsでは使えるのですがマイクロSDモジュールでは使えなかったものです。
プログラムは上記のSDinfoにカウントアップする数値と一定の文字列asdfを書き込むものとしました。上記と同じaruduino mini 8MHzとしています。
/*
The circuit:
* SD card attached to SPI bus as follows:
** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
** CS - depends on your SD card shield or module.
Pin 4 used here for consistency with other Arduino examples
*/
// include the SD library:
#include <SPI.h>
#include <SD.h>
// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;
const int chipSelect = 10; //変更
int value = 123; //ファイル書き込みのため追加
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("\nInitializing SD card...");
// we'll use the initialization code from the utility libraries
// since we're just testing if the card is working!
if (!card.init(SPI_HALF_SPEED, chipSelect)) {
Serial.println("initialization failed. Things to check:");
Serial.println("* is a card inserted?");
Serial.println("* is your wiring correct?");
Serial.println("* did you change the chipSelect pin to match your shield or module?");
return;
} else {
Serial.println("Wiring is correct and a card is present.");
}
// print the type of card
Serial.print("\nCard type: ");
switch (card.type()) {
case SD_CARD_TYPE_SD1:
Serial.println("SD1");
break;
case SD_CARD_TYPE_SD2:
Serial.println("SD2");
break;
case SD_CARD_TYPE_SDHC:
Serial.println("SDHC");
break;
default:
Serial.println("Unknown");
}
// Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
if (!volume.init(card)) {
Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
return;
}
// print the type and size of the first FAT-type volume
uint32_t volumesize;
Serial.print("\nVolume type is FAT");
Serial.println(volume.fatType(), DEC);
Serial.println();
volumesize = volume.blocksPerCluster(); // clusters are collections of blocks
volumesize *= volume.clusterCount(); // we'll have a lot of clusters
volumesize *= 512; // SD card blocks are always 512 bytes
Serial.print("Volume size (bytes): ");
Serial.println(volumesize);
Serial.print("Volume size (Kbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.print("Volume size (Mbytes): ");
volumesize /= 1024;
Serial.println(volumesize);
Serial.println("\nFiles found on the card (name, date and size in bytes): ");
root.openRoot(volume);
// list all files in the card with date and size
root.ls(LS_R | LS_DATE | LS_SIZE);
}
void loop(void) {
SD.begin(); //この行以下はファイル書き込みのため追加した
File dataFile = SD.open("aaa.txt", FILE_WRITE);
// もしファイルが開けたら値を書き込む
if (dataFile) {
value += 1;
dataFile.print(value);
dataFile.println(", asdf");
dataFile.close();
// シリアルポートにも出力
Serial.println(value);
}
// ファイルが開けなかったらエラーを出力
else {
Serial.println(F("error opening aaa.txt"));
}
// 一秒待つ
delay(1000);
}
もどる
8 マイクロSDカードに温度を記録する
aruduino-IDEはシリアル通信やI2C、LCD、SDカードなどの取り扱いが簡単でプログラムが楽にかけます。そこで測定データをLCDに表示しながらSDカードに記録するのにどれほどのROM量を必要とするのか、それはATmega328Pで可能なのかどうかを調べたくてブレッドボードに組んでみました。測定データとしては扱いが簡単な温度センサーを使っています。
MCP ATmega328P 8MHz
温度センサー DS7505 (デジタル値出力)I2C接続
RTC DS3231 (高精度品)I2C接続
マイクロSD 自作フォルダ+2GB SPI接続(miso mosi cs clk Vcc GND)
mega328Pの接続:
1 RST
2 RD
3 TD
4
5
6 LCD DB4 緑
7 Vcc
8 GND
9 Xtal
10 Xtal
11 LCD DB5 青
12 LCD DB6 灰
13 LCD DB7 白
14 LCD RS 茶
15 LCD E 黄
16 SD CS
17 SD MOSI
18 SD MISO
19 SD SCK
20 AVcc
21
22 GND
23
24
25
26
27 I2C SDA
28 I2C SCK
I2Cの SCK と SDA はありあわせの10kでプルアップしています。
表示とSDカード記録はループ任せで、特にSDには1秒に5サンプルほど記録されています。実用には細かな設定が必要だとは思いますが、現在で32kROMの54%に収まっています。この分では温度の記録は大丈夫です。しかし、気圧も扱うとどうなるでしょうか。なんとか収まりそうにも思うのですが。
例によってATmega328P 8MHzの基板をaruduinoに見立ててArduino-IDEでコンパイルして、AVR用のライタで書き込んでいます。AVRのMCとクロックが自由にならないのですが、aruduinoのライブラリが使えるのがメリットです。
/* *********************************************************************************
* name SD160810.ino
* ATmega328p 8MHz 3.3V
* 温度データを時刻とともにSDカードに記録するテストプログラム。
* RTC:DS3231 温度センサー:MAXIM DS7505 SDカード:フォルダ自作 2GB
* 年月日時分秒と温度(小数1位まで)をLCDに表示し、SDカードにtxtとして保存する。
* 保存テキストファイル名は BBB.txt である。
*
* 記録は自由ループのため毎秒数回採られている(変更の必要あり)。
* 最大30,720バイトのフラッシュメモリのうち、スケッチが16,228バイト(52%)を使って
* います.
* 最大2,048バイトのRAMのうち、グローバル変数が1,139バイト(55%)を使っています。
*
********************************************************************************** */
#include <LiquidCrystal.h>
LiquidCrystal lcd(8,9,4,5,6,7);
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
// val
const int chipSelect = 10; // 328p CS端子
uint8_t i,t1,t2,mm,ss,u; // u:generally
uint8_t t[10],tp[2]; // t[]:RTCdata tp[]:TEMPdata
char d[18],s[15]; // string for print & input
void setup() {
Serial.begin(9600);
lcd.begin(16,2);
Wire.begin(0x90>>1); // 温度センサー DS7505
Wire.begin(0xd0>>1); // RTC ds3231
SD.begin();
pinMode(2, INPUT_PULLUP);
}
void loop() {
lcd.clear();
// 温度センサー12ビット読み取りに設定
Wire.beginTransmission(0x90>>1);
Wire.write(0x01); // inner=01 set mode 9bit or 12bit
Wire.write(0x60); delay(10); // set 12bit mode
Wire.endTransmission() ; delay(20);
//温度センサー データ読み取りモードに戻す
Wire.beginTransmission(0x90>>1);
Wire.write(0x00); delay(10); // inner=00 set mode read data
Wire.endTransmission() ; delay(20);
lcd.clear();
#if 0 // set RTC 電池バックアップのため必要なときに有効にして設定する。
Serial.println("Input ! YYMMDDdhhmmss 24hsys.");
delay(20000);// 入力終了を待たずに読み込むため20秒のwaitを入れてある
i=0;
while(Serial.available()) { // 受信したデータが存在する
s[i] = Serial.read(); // 受信データを s[] に読み込む
i++;
}
for(i=0;i<13;i++){ Serial.println(s[i]);}// 入力確認用
// inner address該当のd[0]=0 のためd[1]=ssとなり1だけずれる
t[7]=(s[0]-'0')*16+(s[1]-'0'); // yy
t[6]=(s[2]-'0')*16+(s[3]-'0'); // mm
t[5]=(s[4]-'0')*16+(s[5]-'0'); // dd
t[4]=(s[6]-'0'); // day of week sun=1
t[3]=(s[7]-'0')*16+(s[8]-'0'); // hh
t[2]=(s[9]-'0')*16+(s[10]-'0'); // mm
t[1]=(s[11]-'0')*16+(s[12]-'0'); //ss
t[3]=d[3]&0B10111111; // set 24h
t[0]=0;
Wire.beginTransmission(0xd0>>1);
for(i=0;i<8;i++){Wire.write(t[i]);}
Wire.endTransmission() ;
#endif // end of set RTC
while(1){ // 以下実質のループ
// 以下時刻処理
Wire.beginTransmission(0xd0>>1); // RTC write mode
Wire.write(0x00); delay(10); // inner address
Wire.endTransmission(false) ; // restart
Wire.requestFrom(0xd0>>1,7); // デバイス(アドレス=0x48)に対し7バイトをread要求
i=1;
while(Wire.available()) { // 要求より短いデータが来る可能性あり
t[i] = Wire.read(); // 1バイトを受信
i++;
}
d[1]=t[7]/16+'0'; d[2]=t[7]%16+'0'; d[3]='/'; // year
d[4]=t[6]/16+'0'; d[5]=t[6]%16+'0'; d[6]='/'; // month
d[7]=t[5]/16+'0'; d[8]=t[5]%16+'0'; // day
d[9]=t[3]/16+'0'; d[10]=t[3]%16+'0'; d[11]=':'; // hour
d[12]=t[2]/16+'0'; d[13]=t[2]%16+'0'; d[14]=':'; // minute
d[15]=t[1]/16+'0'; d[16]=t[1]%16+'0'; // second
delay(20);
// 分未満四捨五入 pin2(4番)= L の時
if( digitalRead(2)==LOW){
//delay(2000);
if(t[1]>=0x30){t[2]++;}
t[1]=0; t[0]=0;
Wire.beginTransmission(0xd0>>1);
for(i=0;i<3;i++){Wire.write(t[i]);}
Wire.endTransmission() ;
delay(100);
}
// end of 分未満四捨五入 pin2(4番)= L の時
// 時刻表示
lcd.setCursor(1,0);
for(i=1;i<9;i++){lcd.print(d[i]);} // YY/MM/DD
lcd.setCursor(1,1);
for(i=9;i<17;i++){lcd.print(d[i]);} // hh:mm:ss
// 時刻表示 終わり
// 以上時刻処理
// 以下温度処理
Wire.requestFrom(0x90>>1,2); // デバイス(アドレス=0x90>>1)に対し2バイトを要求
i=1;
while(Wire.available()) { // 要求より短いデータが来る可能性あり
tp[i] = Wire.read(); // 1バイトを受信
i++;
}
// 温度表示
s[1]=tp[1]/10+0x30; s[2]=t[1]%10+0x30; s[3]='.';
s[4]=(((t[2]/16)*10)/16)+0x30; s[5]=' ';
lcd.setCursor(11,1);
for(i=1;i<6;i++){lcd.print(s[i]);} // 配列は設定できない
// 以上 温度処理・表示
// 以下 SDへデータ書き込み
File dataFile = SD.open("bbb.txt", FILE_WRITE); // プログラム中のファイル名、SDのファイル名
// もしファイルが開けたら値を書き込む
if (dataFile) {
for(i=1;i<9;i++){dataFile.print(t[i]);} // YYMMDD
dataFile.print(" ");
for(i=9;i<17;i++){dataFile.print(t[i]);} // hhmmss
dataFile.print(" ");
for(i=1;i<5;i++){dataFile.print(s[i]);} // temperture
dataFile.println(" ");
dataFile.close();
}
delay(100);
}//while 実質ループ終わり
} // program end
時計について、分未満を四捨五入する機能を追加しました。aruduinoのpin2(mega328Pの4番pin)を一瞬GNDに落とすことで30秒以上なら次の分の0秒に進め、30秒未満ならその分の0秒に遅らせます。RTCは精度が高く、またリチウム電池でバックアップしているのでしばらく使っても誤差は数秒程度です。この機能を使って合わせれば時刻設定機能はまず必要がありません。ただ、手抜きしていますので繰り上がりの時間(分の1位が9の時)は使えません。ご動作します。完璧にするには 分→時→日→月→年 まで繰り上がりに配慮が必要です。
もどる
9 Arduino-IDEメモ
1 mega8(RC8MHz)に適用する
2 Arduino-IDEリファレンス
3 ボードの追加 ESP8266,AtTiny2313
4 libraryの追加(skechbookフォルダーのなかにあるlibrariesへ) 2021.07.31
5 githubからのダウンロード 2021.07.31
1 mega8(RC8MHz)に適用する 2017.05.22
手持ちのmeg8をI2CドライブのLCDスレーブにするためにArduino-IDEに合う設定を探しました。デバイスへの書込みは慣れたavrdude-GUIで行いますのでhexファイルができれば良いことになります。
以前のbords.txtは拡張(追加)が簡単だったのですが今のものはできないようです。したがってmega8のボードを探さねばなりません。幸いなことに Arduino NG or older ボードがありました。内蔵RC発振8MHzで使うのですが、このボードは水晶の16MHzになっています。ヒューズはIDEでは書き込まないので(avrdudeで書き込む)変更の必要はないのですが目についたので変えました。16MHzのままでもいいのですが時間が関係するルーチンに影響しますので8MHzに変えたほうがよいでしょう。
次の変更を行いました。
## Arduino NG or older w/ ATmega8 で追加及び修正した場所
atmegang.build.f_cpu=8000000L
atmegang.menu.cpu.atmega8=ATmega8(3.3V, 8 MHz)
atmegang.menu.cpu.atmega8.bootloader.low_fuses=0xe4
atmegang.menu.cpu.atmega8.bootloader.high_fuses=0xd9
Arduino-IDEで 「スケッチ」→「コンパイルしたバイナリを出力」を選択してできた.hexファイルを書き込んでいます。
2 Arduino-IDEリファレンス 2017.05.22
文法については Arduino 日本語リファレンス を参照しています。
なお、現在(2017.5)はArduino-IDE のVer1.8.1を使っています。
Top
3 ボードの追加 ESP8266,AtTiny2313 2017.05.29
ボードを追加するには次の手順で行います。
1 Arduino-IDEの「ファイル」→「環境設定」→「追加のボードマネージャーのURL」に次のURLを貼り付けます。
ESP8266 :http://arduino.esp8266.com/stable/package_esp8266com_index.json
Tiny2313:http://drazzy.com/package_drazzy.com_index.json
このとき、上記の両方共必要なときは2つとも書かねばなりません。そのまま追加すると前のものが消されてしまします。
右端のページマーク(?)をクリックすると複数行の記入ができて便利です。
2 「ツール」→「ボード」から最上部の「ボードマネージャー」を開きます。
3 ESP8266 で検索して、「esp8266 by ESP8266 Community」の最新版をインストールします。
attiny で検索して、上方にある「ATTinyCore by Spence Konde」の最新版をインストールします。
Top
4 libraryの追加 2120.07.31
ライブラリーの追加は、arduino_sckechbookの中にある libraries に置きます。E:\MC\_0MicroComp\arduino_sckechbook\libraries です。
Top
5 githubからのダウンロード 2120.07.31
githubからヘッダファイルなどファイルをダウンロードするときは、
右にある「Raw」を右クリック → 名前をつけてリンク先を保存 を選びます。保存場所が表示されます。(C:\arduino1.8.x/libraris/Myに保存します)
Top
------------------------------------------------------------
10 RTC DS3231モジュールの実験 2018.06.30
MAXIMのRTC DS3231 は2ppmという精度を持った素晴らしいICだと思います。しかし、最近のMAXIMのページでは主力は5ppmの製品に移っているように思えます。また、1000個単位の購入価格が単価3ドルを超えています。しかし、中国製のモジュールがなぜか安く買えます。もっとも、以前は100円ほどだったようですが今は231円しています。やがてはなくなるかも、と思って追加購入しました。
現在は時計を作るつもりはないのですが、専用のライブラリを使わずにどの程度で液晶時計(時刻表示まで)ができるのか調べてみたくて組んでみました。ブレッドボードを簡単にするために、LCDはR8C/M12Aを使って3.3VでシリアルのRXDだけで動くものを使っています。LCDへの命令は'!'でクリア、101始まりで1行目1カラムから、201始まりで2行目1カラムからとしています。
このRTCモジュールは、CR2032のホルダがついていますが、充電池用なのでVccとつながっています。一次電池を使いますので回路の200Ωを外して電圧がかからないように変更しています。
MCUは mega328p で、Arduino-IDEは Arduino pro mini 8MHz を使っています。Arduino-IDEでコンパイルしますがAVRへの書き込みは専用の書き込み(AVRdude-GUI+pic18spx)を使っています。
今回のArduino-IDEのバージョンは 1.8.5 を使っています。
14番ピンにタクトスイッチを入れて、@押したままリセットすればyymmddhhmmssで時刻設定ができ、A動作状態で押すと分未満四捨五入で時刻調整ができるよにしています。
I2Cのシーケンス(Arduino-IDEの)は次のとおりです。
#include <Wire.h> ヘッダファイルのインクルード
Wire.begin(); setup()で開始 ()内はマスタでは「なし」
書き込み時
Wire.beginTransmission(0x68); //RTC address
Wire.write(0); //inner address
for (n = 0; n < 7; n++) { Wire.write(d[n]); } //書き込みデータ(バイト型)
Wire.endTransmission(); //終了
読み込み時
Wire.beginTransmission(0x68); //RTC address
Wire.write(0); //inner address
Wire.endTransmission(false); //restart
Wire.requestFrom(0x68, 7); // request 7 bytes from slave device 0x68
n = 0; while (Wire.available()) { d[n] = Wire.read(); n++; } // slave may send less than requested
スケッチをコンパイルしたところ、ROMは4362バイト(14%)、RAMは398バイト(19%)となりました。
プログラムは次のとおりです。(一部 F()マクロ を使ってRAMを節約しています。)
/* *******************************************************************
RTC DS3231モジュールの読み取り、シリアル表示ルーチン
ds3231_reader.ino 2018.06.29
ATmega328 (use Arduino pro mini 8MHz)
LCD表示は シリアル駆動LCD 使用
14番pin sw=LOW & reset → 時刻設定 yymmddhhmmss
表示中に 14番pin sw=LOW → 分未満四捨五入
******************************************************************** */
#include <Wire.h>
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial for output
pinMode(8, INPUT_PULLUP); // no.14
// pinMode(7, INPUT_PULLUP); // no.13
}
//--------------------------------------------------------------------
void loop() {
uint8_t d[10];
char s1[16], s2[16];// LCD表示文字列 1行目と2行目
uint8_t n, f1, f2;
Serial.println('!'); delay(100);
if (digitalRead(8) == LOW) { // if sw on 時刻設定
Serial.println(F("yymmddhhmmss at 24h")); delay(50);
n = 0;
while (Serial.available() < 13) {}
while (Serial.available() > 0) {
s1[n] = Serial.read(); // 受信データを読み込む
n++;
}
// Serial.println(); delay(100);
// Serial.println(s1); delay(100);
d[0] = (s1[10] - '0') * 16 + (s1[11] - '0'); //sec
d[1] = (s1[8] - '0') * 16 + (s1[9] - '0'); //min
d[2] = (s1[6] - '0') * 16 + (s1[7] - '0'); //hour
d[3] = 1; //w_day:none
d[4] = (s1[4] - '0') * 16 + (s1[5] - '0'); //day
d[5] = (s1[2] - '0') * 16 + (s1[3] - '0'); //month
d[6] = (s1[0] - '0') * 16 + (s1[1] - '0'); //year
Wire.beginTransmission(0x68); //RTC address
Wire.write(0);//inner address
for (n = 0; n < 7; n++) {
Wire.write(d[n]);
}
Wire.endTransmission();
}
while (1) {
//読み取り表示ルーチン
Wire.beginTransmission(0x68); //RTC address
Wire.write(0); //inner address
Wire.endTransmission(false); //restart
Wire.requestFrom(0x68, 7); // request 7 bytes from slave device 0x68
n = 0;
while (Wire.available()) { // slave may send less than requested
d[n] = Wire.read(); // receive a byte as character
n++;
}
f2 = f1; f1 = d[0];
if (f2 != f1) { // 秒の数値が変わったときだけ表示する
// d[0]:sec d[1]:min d[2]:hour d[3]:w_day d[4]:day d[5]:mon d[6]:year
s1[0] = (d[6] / 16) + '0'; s1[1] = (d[6] % 16) + '0'; s1[2] = ' ';
s1[3] = (d[5] / 16) + '0'; s1[4] = (d[5] % 16) + '0'; s1[5] = ' ';
s1[6] = (d[4] / 16) + '0'; s1[7] = (d[4] % 16) + '0'; s1[8] = ' ';
s1[9] = 0;
s2[0] = (d[2] / 16) + '0'; s2[1] = (d[2] % 16) + '0'; s2[2] = ':';
s2[3] = (d[1] / 16) + '0'; s2[4] = (d[1] % 16) + '0'; s2[5] = ':';
s2[6] = (d[0] / 16) + '0'; s2[7] = (d[0] % 16) + '0'; s2[8] = ' ';
s2[9] = '\0';
Serial.print(F("10120")); delay(50);
Serial.println(s1); delay(50); //year month day
Serial.print(F("201 ")); delay(50);
Serial.println(s2); delay(50); // hour minute second
}
// 分未満四捨五入
if (digitalRead(8) == LOW) {
uint8_t min2;
min2 = (d[1] / 16) * 10 + d[1] % 16;
if (d[0]>=48) min2++;// >=0x30
min2 = (min2 / 10) * 16 + min2 % 10;
Wire.beginTransmission(0x68);
Wire.write(0);
Wire.write(0);
Wire.write(min2);
Wire.endTransmission();
}// end of 分未満四捨五入
}// end of while
}// end of loop
Top
11 atmega328を UNO ボードとして使う(ROMが大きい) 2019.01.25
mega328のarduinoもどきでは、5Vなら問題はないのですが、3.3Vで使いたいときに16MHzでは動くようですがメーカーの推奨条件ではありません。通常は8MHzで使いたいものです。
ボードを探すと pro miniボードではmega328の3.3Vがあります。この項の場合はAVRのライタを使いますのでMCのタイプさえ合えばどのボードでコンパイルしても使えるはずです。
ごく最近、uno と pro-mini nano ではブートローダのROM領域が違うことを知りました。unoは512バイトですが、mini、nanoは古いタイプのブートローダーで2048バイトとなっています。
同じmega328を使っても、unoに比べると他は1.5kバイトだけ小さいプログラムしか書けないことになります。mega328にuno 16MHzでコンパイルしたものを書き込んで3.3V 8MHzで実行することは可能なのですが、delay()のような時間に関係するものは長さが狂うことになります。
そこで、unoの8MHz版を考えることにしました。arduinoシステムに含まれる boards.txt を修正することにしました。このファイルの
uno.name=Arduino/Genuino Uno のすべてをコピー&ペーストでもう一つ作り
タイトルの uno.name=Arduino/Genuino Uno を uno8.name=Arduino/Genuino Uno8に変更し、以下は uno.をすべてuno8.に変更しました。
この修正が済んでからIDEを立ち上げるとボードに uno8 が現れます。それを選んでコンパイルすればROM領域が大きく、かつ、正しいスケッチが書けます。
blinkで確認しました。これからmega328をarduinoで使うときはuno8を使うことにします。
なお、ヒューズは Low:F7 High:D5 Ex:FD (電源は遅い立ち上がり、BOOT:512バイト BrownOut:2.7V)としています。
ATmega328pをuno8ボードに(uno8その2) 2021.09.15
以前に作ったmega8のボードがありました。ISPとLCD(16*2)とシリアルの端子を付けたものですが調子がよくありません(原因不明)。修理しようかと思いましたが手持ちにメモリが大きなmega328がありますので取り替えることにしました。
簡単に済ますはずでしたが、まずISPが動きません散々苦労した挙げ句6ピンの端子を変更して動くようになりました。次にLCD表示機が働かず苦労しました。それで、最初はICをはんだ付けしていたのですが袋小路に入ってしまったのでソケットに変更しています。
3.3Vで使えるように水晶を8MHzとして自作のuno8としています。
現在は使う目的がありませんので、単にuno8ボードとしてLCDとシリアルの端子をつけただけに終わっています。
Top
12 IDEのアップデート 1.8.5→1.8.9 2019.06.08
IDEバージョンアップの通知表示があったので実行しました。このページから例によって「Windows ZIP file for non admin install」を選び、「JUST DOWNLOAD」で arduino-1.8.9-windows.zip をダウンロードしました。それを解凍して得られる arduino-1.8.9 ホルダーをホルダーごと D:\MC\_0MicroComp に書き加えました。もちろんここには旧のarduino-1.8.5が残っています。
新しい1.8.9の aruduino.exe のショートカットをデスクトップに出して使うことにします。当然旧1.8.5も残っていますので1.8.5を実行することもできます。
IDEの環境設定は(全部ではないようですが) C:\Users\user_name\AppData\Local\arduino15 フォルダに保存されているようで、過去のスケッチ はそのまま呼び出せるようです。
旧バージョンのlibrariesフォルダは、何かで移動させてarduino_sckechbookフォルダ内にあるのですが、このアップデートを機会にデフォルトのD:\MC\_0MicroComp\arduino-1.8.9に置くことにします。
新の1.8.9を立てて、nanoでblinkを変更して書き込みましたが、問題なく実行されました。ただし、プロセッサの選択は新しくできたATmega328p(Old Bootloader)を選びます。nanoのものは2kBあったので新しいものは小さくなったのかもしれません。
ESP8266の書き込み:これはずいぶん苦労しましたが、確かなところは今もわかりません。
はじめの 1.8.5 と 1.8.9 を並列しておいた環境では書き込みエラーでどちらも使えませんでした。そこで古いバージョンを外して、すなわち C:\Users\user_name\AppData\Local\arduino15 を消して、改めて「環境設定」に http://arduino.esp8266.com/stable/package_esp8266com_index.json を書き込んで ボードマネージャーから 8266ボードをインストールしました(バージョンは最新の2.5.2)。すると一度は書き込みに成功したのですが、その後二度と書けませんでした。
それで、バージョンを落として8266ボードのバージョンを 2.4.2 に戻したところ書き込みができました。ともにblinkだけの小さなスケッチですがビルドと書き込みにかなりの時間がかかりました。(2.5.2は書き込みがやや速いのですが、2.4.2は遅いです)
ハードウエアはこれを使いました。
<解決>装置が不安定なのでよくよく調べてみると、たとえば、RESETスイッチを押したときにLEDが点滅のままだったり、点灯したり、あるいは消灯だったりします。resetスイッチ周りの接触が心配になって(かなり無理なはんだ付けもありました)、resetスイッチを別に付けえると正常に動作するようでした。そこでスイッチ付きの基盤を新しく作り変えることにしました。基盤の幅を300ミルでなく穴6個(2.5mm*6)に大きくしました。その結果、動作は全く問題なく、arduino1.8.9、8266ボード2.5.2ともに正常になりました。
「半田付けは時間が経つと壊れる」。確実なはんだは問題がなくとも、ちょい付けでたまたまつながっているものは経年変化で離れてしまうようです。実験とはいえ気をつけることにします。
Top
13 UNO8で諸テスト 2021.09.22
しばらく電子工作をしていなかったのですが、aruduino UNOを8MHzに変更して、書き込みはISPによる UNO8 をもとにaruduinoの機能を探ってみたいと思います。過去と重複するものがあるかもしれません。
タイマー割り込み 2021.09.22
回路はD13(19番ピン)にLEDを接続しただけです。
https://playground.arduino.cc/Main/MsTimer2/からMsTimer2.zipをダウンロードして、解凍したフォルダをlibrariesフォルダに置きました。
これによりタイマー割り込みができるそうです。
プログラムは例文のとおりです。(スケッチ例の下方に出てきます)
#include <MsTimer2.h>
void flash() {
static boolean output = HIGH; //最初の1回だけ初期化される
digitalWrite(13, output);
output = !output;
}
void setup() {
pinMode(13, OUTPUT);
MsTimer2::set(500, flash); // 500ms period
MsTimer2::start();
}
void loop() {
}
これでLEDが点滅します。
外部割り込み 2021.09.24
unoには割り込みポートが2つあります。INT0(D2,4番ピン)とINT1(D3,5番ピン)です。
ピンに直接タクトSWをつけましたがチャタリングのせいか不安定です。コンデンサを付加しましたが安定に動作しません。
そこで、loop{}の中でディレイを使って1秒毎にD8ピンにHIGH、LOWを出力し、これをINT0ピンに入力しました。
ハードウエアは、D13にLED、D8にLED(ともにHで点灯)をつけ、D8とINT0を接続しています。
スケッチは次のとおりです。
//外部割り込み_0_01 wari0_01
volatile int state = LOW;
int a = LOW;
void setup() {
pinMode(13, OUTPUT);
pinMode(8, OUTPUT);
attachInterrupt(0, blink1, RISING);
}
void loop() {
a = !a;
digitalWrite(8, a);
digitalWrite(13, state);
delay(1000);
}
void blink1() {
state = !state;
}
これで目的のD13のLEDが2秒周期で点滅しました。RISINGを指定したのでD8のLEDが点灯時に(HIGHになったときに)割り込みLEDが点滅します。きれいなON、OFF信号が与えられれば割り込みが働くことがわかりました。
外部割り込み その2 2021.10.03
D7ピンにスイッチを付け(対GND)このピンを pinMode(7, INPUT_PULLUP); でソフトウエアプルアップしておきます。
このピンを読み取った後 delay(5)でプログラムを止めます。この間にチャタリングが終わってしまうつもりです。
D8ピンのHL出力をINT0のD2ピン(4番ピン)に加えています。
プログラムです。
//外部割り込み_0_02 wari0_02
//チャタリングをソフトで処理
int pin = 13;
volatile int state = LOW;
int a ;
void setup() {
pinMode(13, OUTPUT);
pinMode(8, OUTPUT);
pinMode(7, INPUT_PULLUP);
attachInterrupt(0, blink1, FALLING);
//LOW CHANGE RISING FALLING
}
void loop() {
a=digitalRead(7);
delay(5);
digitalWrite(8,a);
digitalWrite(13, state);
}
void blink1() {
state = !state;
}
誤動作なくスイッチに反応するようになりました。
Top
14 mega88のボード設定 2021.09.27
短いarduinoプログラムでmega88を使いたいときがあります。 このサイトにmega88ボードの登録方法が書かれていました。
ここのATmega88_168P_328P_100.zip (10kB)をダウンロードして、解凍の後、arduino/hardwarに置きました。
これでボードの選択にmega88、mega168、mega328が現れます。
Top
工事中
0 タイトル 2017.05.22
Top