MAX7219の使い方 8×8LCD 無ライブラリー (3)

あれこれやっていましたが,やっとまた1つ理解が進みました。これも,分かってる方からすればな
んでもないことなのでしょうが,改めて理解できたというところです。
単体(1つだけ)の書き込みをしてる分にはいいのですが,for等で繰り返し処理をすると2回目以降
の書き込みで,1番目にLED書き込んたデータが2番目,3番目,4番目と順次表示されます。書き込
みは1番目のLEDなのに2番目以降のLEDに表示されるが理解できませんでした。これもやっと理解でき
ました。

 一度書き込まれたデータの後に書き込みを行うと,
 先に書き込まれたデータに上書きされるのではなく,
 先に書き込まれたデータは,次以降のLEDに順送りにされる。

このことが分からなくて,思った動作をさせられず,もがきました。

 for(int i=1;i<9;i++){
    maxTransfer(i,1);
    digitalWrite(CS_PIN, HIGH);
    delay(500);
  }

この繰り返しのスケッチは,一番左に1つドットを描き,それを一番下まで繰り返すスケッチです。このまま
だと動画のようになります。

それと, digitalWrite(CS_PIN, HIGH); の命令は,本来 maxTransfer( ,); のサブ関数内で一括処理して
た、ものです,データをかきこんだらそれをレジスタにその都度アップロード・ラッチを行う処理でした。
データの書き込み--アップロード(ラッチ)と一連の流れで処理してしまうと,別な命令の書き込み
ができないので,諸兄スケッチから変更しました。こうすることで、下記のスケッチのように、何もしない
命令( maxTransfer(0,0))を2番目、3番目、4番目のLEDに先送りにし、一度にデータをアップロード
ラッチすることで、余分な表示を防げます。

for(int i=1;i<9;i++){
    maxTransfer(0,0);
    maxTransfer(0,0);
    maxTransfer(0,0);
    maxTransfer(i,1);
    digitalWrite(CS_PIN, HIGH);
    delay(500);
  }

2番目,3番目,4番目に先に送られたno-opコードのため余分な表示がされなくなります。

一度表示されると,消去するまで,表示されますので,次のように一度表示したものを消去すると移動するよ
うに見えます。

For(int i=1;i<9;i++){
    maxTransfer(0,0);
    maxTransfer(0,0);
    maxTransfer(0,0);
    maxTransfer(i,1);
    digitalWrite(CS_PIN, HIGH);
    delay(500);
    maxTransfer(0,0);
    maxTransfer(0,0);
    maxTransfer(0,0);
    maxTransfer(i,0);
    digitalWrite(CS_PIN, HIGH);
  }

ここまでたどり着くのに結構時間がかかりました。ライブラリーを作成している先達は,いち早くこういうこ
とが理解できたのでしょうね。凡人はとてつもなく時間がかかります。

MAX7219の使い方 8×8LCD 無ライブラリー (2)

あれこれやってますが,どのHPを見ても,二個目,三個目,四個目のMAX7219に指令を出す方々が書いてあ
りませんでした。これまでかと思い,取説を確認してみるとありました。ありました。きちんと表記があるじゃないですか。

  ===二つ目以降のチップを操作する===

 4つのMAX7219をカスケード接続する場合,4番目のチップに書き込みを行うには,希望する
 16ビットワードの後に,3つのno-noコード(16進の0xXX0X表2)送信します。
 LOAD/CSがハイになった時点で,すべてのデバイスにデータがラッチられます。最初の3つのチップ
 は,no-noコマンドを受け取り,4番目のチップが目的のデータを受け取ります。

とありました。これで,もうちょっと先に進めそうです。

また,諸兄のHPを参考にarduino風に書き換えていた,SPIのデータ書き込みのスケッチも動きました。

#define CLK 13
#define DIN 11
#define CS  10

void setup(){
  Serial.begin( 9600 );     // シリアル通信を初期化する。通信速度は9600bps
    pinMode(CLK, OUTPUT );
    pinMode(DIN, OUTPUT );
    pinMode(CS, OUTPUT );
}

void wait(int i){
  volatile int j,k;
  for(j=0;j<i;j++){
    for(k=0;k<55;k++);
  }
}

//****************************************************************
//arduino自作関数
//データ書き込み
//****************************************************************
void LED_MAT_SEND(unsigned char SELECT, unsigned char DATA){
  digitalWrite( CS, HIGH );// Load ON
  wait(2);void 
  digitalWrite( CS, LOW );// Load OF

  for(int i=0;i<8;i++){               // Send Register data
        digitalWrite( CLK, LOW );//CLK OFF
        if(((SELECT << i) & 0b10000000)==0){
          digitalWrite( DIN, LOW );
          Serial.print( '-' );
        }
        else{
          digitalWrite( DIN, HIGH );
          Serial.print( '+' );
        }
      wait(1);
      digitalWrite( CLK, HIGH );//CLK ON
      wait(1);
  }

  for(int i=0;i<8;i++){               // Send LED DATA
      digitalWrite( CLK, LOW );//CLK OFF


      if(((DATA << i) & 0b10000000)==0){
        digitalWrite( DIN, LOW );
        Serial.print( '-' );
      }
      else{
        digitalWrite( DIN, HIGH );
        Serial.print( '+' );
      }
      wait(1);
      digitalWrite( CLK, HIGH );//CLK ON
      wait(1);
    }
    digitalWrite( CS, HIGH );// Load ON
    wait(2);
    digitalWrite( CS, LOW );// Load OFF
    Serial.print( "******" );
}

デバッグ用のSerial.printは残してあります。また,この, digitalWrite( CLK, HIGH );は結構クロックをつかうようなので,一括処理のものに後で変更しようと思います。このスケッチもSPIライブラリーを使うと,別な諸兄の書かれた下記のように6行程度ですんでしまいます。

void maxTransferCMD(uint8_t address, uint8_t value) {
  digitalWrite(CS_PIN, LOW);
  SPI.transfer(address); // Send address.
  SPI.transfer(value); // Send the value.
  digitalWrite(CS_PIN, HIGH); // Finish transfer.
}

今日,取説で見つけた記述をもとに,2個目3個目のMAX7219に書き込めるスケッチも考えてみたいと思います。

MAX7219の使い方 8×8LCD 無ライブラリー (1)

何年か前に、8×8の小さ目のLCDマトリックスで、PICを使って流れるサインボードのおもちゃを作成しまし
た。ネットサーフィンをしていると、ちょっと大きめの8×8LCDマトリックスがめにとまりましたので、
思わず、いくつかポチってしまいました。

この一つのものと、4連のもの合計3つをアリでぽちりました。

4連のものは、赤と青を購入しました。

この、LCDは、MAX7219というコントロールIC(ドライバー)が使われていて、比較的たやすく、コント
ロールができるようで、arduinoだと、専用のライプラリーもいくつかあるようです。また、このLCDの
面白いのは、横に連結して使えることです。どのくらい連結できるのかわかりませんが、arduinoの設定
の項目には11個の記載がありましたので、もしかして、電源のこと考えないなら、11個ぐらい連結で
きるのかもしれません。

ライブラリーを使えばいいのでしょうが、MAX7219を直接コントロールしたくて、あれこれしらべました。
幸い詳しく書かれている諸兄のHPが見つかりましたので、ここを参考にいろいろやってみます。

いままで、いろんなもののコントロールに、SPI(シリアル通信)を使う記事も見かけましたが、実際は
どんなふうにするのかいまいち理解がおよびませんでしたが、なんとなく理解することができました。
諸兄のHPによれば、下記のようになるそうです。SPIは3本の線でやり取りします。CLK,Cs,DOUTの3本の
線で下記のようにやり取りをします。

 ・Load端子をLowにセット

  -・CLK端子をLowにセット
  +・15ビット目の値をDINにセット
  +・CLK端子をHighにセット ここでシフトレジスタの0ビット目に①の値がセットされます
  +    つまり 00000000 0000000 がMAX7219のシフトレジスタに入ってま
  +・CLK端子をLowにセット
  +・14ビット目の値をDINにセット
  +・CLK端子をHighにセット ここでシフトレジスタがシフトし②が0ビット目に入ります
  -  つまり 00000000 000000①② が入ってます
      これを繰り返して 16ビットすべて送信したら

  LoadをHighにセットします

  この瞬間シフトレジスタの値が 指定されてたレジスタに反映されます。

この流れを、プログラムに組んでやれば、基本的なデータの送信ができます。この送信の手順は、

のデータシートに記載のあるタイミング図をみればいいみたいですが、いままで、この意味がよく分かり
ませんでした。今となって、やっとその意味が分かりました。

このデータ送信も、arduinoだとSPI(シリアル通信)のライブラリーがあるので、簡単にできてしまうよう
なので、ちょっと、びっくりですね。

スマホ用の画面が崩れるーーWP

CやVBAのコードを見やすくする Highlighting Code Block というプラグインをインストールしました。
おかげ様でコード等は見やすくなりましたが,困った問題が一つ発生です。
それは,今まで,何のても加えずに,スマホ等でもそれに応じた画面の表示ができていたのですが,PCの画面
が表示されるようになりました。
このWPも多少手を加えて,投稿記事を~で囲めばPC用の表示に,で囲めばスマホ用の画
面になるようにしていましたがそれもできなくなりました。
あれこれやりましたが,PCとスマホ用で使用するテーマを変更するために,Multi Device Switcher という
プラグインを導入しました。PCの画面とは,イメージが違ってしまいますが,今までと同じようにスマホでも
画面の全部が表示できるようになりました。

コードの表示も問題なさそうです。

Arduino PWM 再び (2)

割り込みを使ったPWMのLチカをやりましたが,機能として備わっているPWMもやってみることにしました。
でもがっかりしました。簡単過ぎるのです。

analogWrite(PIN番号,DUTY)

これだけで,できてしまうのです。ただ,analogWriteでは,周波数とかの細かい設定はできないようです。
DUTYは最高が0~255まで,指定できるようです。PICだと,設定値をビット演算して,レジスターに書き
込むのでちょっと面倒です。周波数とかの細かい設定をするには,やはりレジスタをいじるようです。

昨日作った割り込みのPWMと備え付け機能のPWMを使い,LEDをだんだん明るくして,だんだん暗くする
というスケッチを書いて動かしてみました。

include<avr/io.h>
#include <avr/interrupt.h>


int k=0;
int BLUE_LED_PIN = 11;  // PWM出力させるピン番号を指定
int RED_LED_PIN = 13;   //11は備え付け,13は割り込みj
int DUTY = 0;


void setup() {
  pinMode(RED_LED_PIN, OUTPUT);
  pinMode(BLUE_LED_PIN, OUTPUT);


  TCCR1A  = 0;
  TCCR1B  = 0;
  TCCR1B = bit(WGM12) | bit(CS11) | bit(CS10); // CTCモードで分周率64
  TIMSK1 = bit(OCIE1A); // 割り込みをタイマー1に設定
  //TCCR1B |= (1 << WGM12) | (1 << CS12);  //CTCmode //prescaler to 256
  OCR1A   = 6-1;
  //TIMSK1 |= (1 << OCIE1A);
}


ISR (TIMER1_COMPA_vect) {
 if(k<255){
    k=k+1;
  }
  else{
    k=0;
  }
  if(k==0){
    digitalWrite(RED_LED_PIN, HIGH);
  }
  if(k==DUTY){
    digitalWrite(RED_LED_PIN, LOW);
  }
}


void loop() {
   while(DUTY<255){//だんだん明るくする
    analogWrite(BLUE_LED_PIN, DUTY);
    DUTY=DUTY+1;
    delay(25);
  }
  delay(500);
   while(DUTY>1){//だんだん暗くする
    analogWrite(BLUE_LED_PIN, DUTY);
    DUTY=DUTY-1;
    delay(25);
  }
  delay(500);
}

ほぼ同じような動作をしますが,割り込みを使った方は,あるタイミングでちらつきます。どのタイミングで
ちらつくのか分かりませんが,多分,カウント等余分な動作をしているからかなと推測します。

赤のLEDが割り込みでの点滅で,青のLEDが備え付け機能を使った点滅です。

Arduino PWM 再び

このところ,Arduinoをいじってます。PICに比べると遙かにわかりやすいです。ただ,PICに比べると,細か
な設定等が難しいのが欠点であるといえば欠点ですが。

今回は,ArduinoにもPWMの機能が備わっていますが,この機能を使わずに割り込みで,PWMを簡易的に実現してみました。PWMを実現するには,単純に,端子をLOWとHIGHTに切り替えることができればいいので,通常は,

   端子をHIGHT
   delay(50)
   端子をLOW

みたいにすれば,たやすく実現できます。ただ,このdelay(50)がちょっと気に入らないので,使わないで
実現できないかと考えた訳です。構想としては,TIMER割り込みの回数を数えて,

   0回目の割り込み→→端子をHIGHT
   n回目の割り込み→→端子をLOW
   規定の回数の割り込みで,また,0から数えるLOW

組み込みのPWMでも,レジスターレベルでカウントして,考え方としては,上記のものと同じ考えでやってる
ようです。

arduinoで,やってみました。LEDがだんだん明るくなると次は次第に暗くなるというスケッチを描いてみまし
た。

#include<avr/io.h>
#include <avr/interrupt.h>
int k=0;
int n=0;
void setup() {
  pinMode(13, OUTPUT);


  TCCR1A  = 0;
  TCCR1B  = 0;
  TCCR1B = bit(WGM12) | bit(CS11) | bit(CS10); // CTCモードで分周率64
  TIMSK1 = bit(OCIE1A); // 割り込みをタイマー1に設定
  //TCCR1B |= (1 << WGM12) | (1 << CS12);  //CTCmode //prescaler to 256
  OCR1A   = 11-1;
  //TIMSK1 |= (1 << OCIE1A);
}


ISR (TIMER1_COMPA_vect) {
 if(k<100){
    k=k+1;
  }
  else{
    k=0;
    digitalWrite( 13, HIGH);
  }
  if(k==n){
    digitalWrite( 13, LOW);
  }
}


void loop() {
   while(n<100){//だんだん明るくする
    n=n+1;
    delay(25);
  }
   while(n>1){//だんだん暗くする
    n=n-1;
    delay(25);
  }
 
}

動作している様子です。時々,ちらつきがあります。LEDで動かしていますが,もしかして,これで,モー
ターを制御するとばたついたりするかもしれません。

お散歩グッズの修理?

このところ,夜にお散歩(ウォーキング)をしています。そのとき,安全面を考えて,名前は分かりませんが
LEDで点滅するグッズを身につけています。

買った時からどうもスイッチの感度が悪く,スイッチを入れるのに,かなり,押し込まないと点灯しませんで
した。
そこで,修理といえるほどではないのですが,分解して,接点をお掃除することにしました。

写真の四つのねじを外して,カバーをとります。

基盤ば見えますので,この基盤を裏返しします。

この基盤にスイッチがあります。押すとペコペコと音がする5mm程度金属がスイッチです。

画像はあまりよくないのですが,この金属のカバー?と基盤の間に隙間がありますので,そこを,エアダス
ターで何回かブローして,接点回復剤で,さらにスプレします。最後にエアダスターで,余分な接点回復剤
をタブローします。電池を入れて,テストして問題なかったので,戻します。

今回は,近くのHCで入手したKUREの2-26を使いましたが,〇MAZONには,専用の回復剤もある
ようですが,どれがいいのかは,わかりません。お掃除?の結果,今までの動作が嘘だったかのように,軽い
力で操作ができるようになりました。本当なら,ペコペコする金属を基盤から外して,接点を掃除した方がい
んでしょうけど,か細い爪で基盤に付いているだけなので,それを曲げて取り外すのが怖くで,基盤にすいた
ままのお掃除でした。

ユーザーフォーム SetFocus VBA エクセル 検索 (3)

動作の様子です。使ってる名前は,データ作成サイトで作成した架空のものです。

以下,プロシャージャの全文です。

 

 

-ThisWorkbook-
Private Sub Workbook_Open()'************************************************
  Application.OnKey "{F3}",   "BK_clere"
End Sub

このサブは,背景色をクリアーする機能を F3キー に割り当てる部分です。

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
  Application.ScreenUpdating = True
End Sub

この部分は,アクティブセルを強調するための記述で,条件付き書式と合わせて設定してます。

-UserForm1-
Dim keyWord As String
Dim myRange As Range
Dim myObj As Range

Private Sub ListBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)'***********************************************
  If KeyCode = vbKeyReturn Then
    If ListBox1.ListCount = 0 Then
      UserForm1.TextBox1.SetFocus
      Exit Sub
    Else
     Range(Cells(ListBox1.List(ListBox1.ListIndex, 1), 4).Address).Interior.ColorIndex = 6 (br /)
     'Rangで単一セルを指定の時はaddressが必要
     Cells(ListBox1.List(ListBox1.ListIndex, 1), 4).Select
     Label1.Caption = "検索結果=" & ListBox1.List(ListBox1.ListIndex, 0)
     ListBox1.Clear
     UserForm1.TextBox1.SetFocus
    End If
  End If
  If KeyCode = vbKeyF1 Then
    Range(Cells(ListBox1.List(ListBox1.ListIndex, 1), 4).Address).Interior.ColorIndex = 0
    Cells(ListBox1.List(ListBox1.ListIndex, 1), 4).Select
    Label1.Caption = "検索結果=" & ListBox1.List(ListBox1.ListIndex, 0)
    ListBox1.Clear
    UserForm1.TextBox1.SetFocus
  End If
End Sub

Private Sub TextBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer)'*************************** 
  Dim K As Integer   
  keyWord = TextBox1.Text

  If KeyCode = vbKeyF1 Then'F1が押されたとき
    ActiveCell.Interior.ColorIndex = 0
    Label1.Caption = "検索結果の削除"
  End If

  If KeyCode = vbKeyReturn Then 'リターンキーが押されたとき
    If keyWord <> "" Then

      K = fukusu_kensaku(keyWord)

      If K > 1 Then
        Label1.Caption = "検索結果=" & "複数該当"
        UserForm1.ListBox1.ListIndex = 0
      Else
        Label1.Caption = "検索結果=" & "一人該当"
        UserForm1.ListBox1.ListIndex = 0
      End If
    UserForm1.TextBox1.Text = ""
    End If

   UserForm1.TextBox1.SetFocus
  End If
End Sub

Private Sub UserForm_Initialize()'************************************************ 
  TextBox1.Text = ""
  With ListBox1
    .ColumnCount = 2
    .ColumnWidths = "120;50"
  End With

  With TextBox1
    .BackColor = RGB(204, 255, 255)
  End With
End Sub

Private Sub UserForm_Activate()'************************************************ 
  UserForm1.TextBox1.SetFocus
End Sub

Private Sub TextBox1_Change()'************************************************ 
  keyWord = TextBox1.Text
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)'************************************************ 
  With TextBox1
    .BackColor = RGB(255, 255, 255)
  End With
  With ListBox1
    .BackColor = RGB(204, 255, 255)
  End With
End Sub

Private Sub ListBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
'*********************************************
  With TextBox1
    .BackColor = RGB(204, 255, 255)
  End With
  With ListBox1
    .BackColor = RGB(255, 255, 255)
  End With
End Sub

Private Sub ListBox1_Change()'***********************
  If ListBox1.ListCount >= 1 Then
    Cells(ListBox1.List(ListBox1.ListIndex, 1), 4).Select
  End If
End Sub

-Module1-
Sub macro2()'***********************
  UserForm1.Show vbModeless
End Sub

Function fukusu_kensaku(key_word As String) As Integer'***********************
  Dim myRange As Range
  Dim myObj As Range
  Dim keyWord As String
  Dim K As Integer
  Set myRange = Range(Range("D2"), Cells(Rows.Count, 4).End(xlUp))
  keyWord = key_word
  Set myObj = myRange.Find(keyWord, LookAt:=xlPart)
  K = 0

  If myObj Is Nothing Then
    MsgBox "'" & keyWord & "'はありませんでした"
    Exit Function
  End If
  Dim msg As String
  Dim myCell As Range
  Set myCell = myObj
  Do
    K = K + 1

    With UserForm1.ListBox1
      .AddItem myCell.Value
      .List(K - 1, 1) = myCell.Row
    End With
    myCell.Offset(0, -3).Value = keyWord & K '■■■■欄外に記入

    Set myCell = myRange.FindNext(myCell)
  Loop While myCell.Row <> myObj.Row

  If K > 1 Then
    UserForm1.ListBox1.SetFocus
    Call SendKeys("{DOWN}")
    Call SendKeys("{UP}")
  End If
  If K = 1 Then
    UserForm1.ListBox1.SetFocus
    Call SendKeys("{Enter}")
  End If
  fukusu_kensaku = K
End Function

Private Sub BK_clere()'***********************
  ActiveCell.Interior.ColorIndex = 0
End Sub

Public Sub SendKeys(Keys As String, Optional Wait As Boolean = False)'***********************
  Static w As Object '// WshShellオブジェクト
// WshShellオブジェクトが生成されていない場合
  If w Is Nothing Then
'// WshShellオブジェクトを生成
    Set w = CreateObject("WScript.Shell")
  End If

  Call w.SendKeys(Keys, Wait)
End Sub