goo blog サービス終了のお知らせ 

JH7UBCブログ

アマチュア無線 電子工作 家庭菜園など趣味のブログです

Raspberry Pi Pico MicroPython モールス符号練習機

2021-11-27 21:18:12 | Raspberry Pi Pico
 今まで勉強してきたRaspberry Pi Picoの機能活用とMicroPythonのプログラミングの練習としてモールス符号練習機(Morse Trainer)を作ってみました。

 電池2本でスタンドアローンで動作します。モードはランダムモードとABC順モードの2つです。速度は10kΩボリュームで6wpm~24wpmの間で調整できます。文字はOLEDに表示します。モールス音は、GPIO0に接続したスピーカーから出ます。周波数は700Hz固定としました。スタートボタンを押すと符号の送出が始まり、途中でこのボタンを押すと止まります。
  

 プログラミングの前に、モールス符号について説明します。モールス符号は、短点(dot)と長点(dash)の組み合わせで文字を表現します。文字内のdotまたはdashの間は1dotの時間、文字間は3dotの時間、語間は、7dotの時間と定められています。

 モールス通信の速度は、語/分(wpm(word per minute) )で表します。1語として「PARIS」という語を使い、下の図のように語間スペースも含めると50dot分になります。
 従って、1dotの時間(ms)は、1200/wpmで計算できます。逆に速度wpmは、1200/1dot(ms)で計算できます。良く使われる字/分は、wpm×5字/分となります。

 今回速度の可変範囲は、6wpm(30字/分)~24wpm(120字/分)としました。コンテストなどでも20wpmを聞き取れれば大丈夫です。

 スクリプトです。音を出すときは、pwm.duty_u16(0x8000)とし、700Hzデューティ比50%のPWM信号をGPIO0に出力します。spaceなど音を出さないときは、pwm.duty_u16(0)としてPWM信号を出しません。
 Picoの出力ポートは力持ちで、直接スピーカーをつないで音を出すことができます。ただし、音量は変えられません。
 なお、スクリプトは、main.pyとしてPicoに保存します。ssd1306というライブラリーを利用しますので、前のOLEDテストの記事に記載したようにダウンロードしてPicoにインストールしておきます。
-----------------------------------------------------------------------------------
"""
Morse Trainer Ver1
2021.11.27
JH7UBC Keiji Hata
"""

from machine import I2C,Pin,PWM,ADC
import random
import utime
import ssd1306

M_code = [".-","-...","-.-.","-..",".","..-.","--.","....","..",
           ".---","-.-",".-..","--","-.","---",".--.","--.-",".-.",
           "...","-","..-","...-",".--","-..-","-.--","--.."]
moji = ["A","B","C","D","E","F","G","H","I","J","K","L","M",
         "N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
mode_txt=["RAND","ABCD"]

def text_clr():
     oled.fill_rect(0,8,127,63,0)

def random_txt():
    global x0
    global y0
    column = 0
    row=0
    count=0
    while True:
         speed_read()
        x = x0 + 8 * column
        y = y0 + 8 * row
         i=int(random.random() * 26)
         oled.text(moji[i],x,y)
         oled.show()
         morse_out(i)
         column =column + 1
         count = count +1
        if count > 3:
             column = column +1
             count =0
        if column >14:
             column = 0
             row = row + 1
        if row > 5:
             break
        if start_sw.value()==0:
             break

def abcd_txt():
    global x0
    global y0
    column = 0
    row=0
    count=0
    for i in range(26):
        speed_read()
        x = x0 + 8 * column
        y = y0 + 8 * row
         oled.text(moji[i],x,y)
         oled.show()
         morse_out(i)
         column =column + 1
         count = count +1
        if count > 3:
             column = column +1
             count =0
        if column >14:
             column = 0
             row = row + 1
        if start_sw.value()==0:
             break

def dot():
     pwm.duty_u16(0x8000)
     utime.sleep_ms(dot_time)

def space():
     pwm.duty_u16(0)
    utime.sleep_ms(dot_time)

def dash():
     pwm.duty_u16(0x8000)
     utime.sleep_ms(dot_time)
     utime.sleep_ms(dot_time)
     utime.sleep_ms(dot_time)

def char_space():
     pwm.duty_u16(0)
     utime.sleep_ms(dot_time)
     utime.sleep_ms(dot_time)

def morse_out(n):
     m=M_code[n]
     l=len(m)
    for j in range(l):
         s=m[j]
        if s==".":
             dot()
         elif s=="-":
             dash()
         space()
     char_space()


def mode_disp(m):
     oled.fill_rect(16,0,32,8,0)
    oled.show()
     oled.text(mode_txt[m],16,0)
     oled.show()

def mode_change():
     utime.sleep_ms(10)
    global mode
    mode = mode +1
    if mode>1:
         mode=0
     mode_disp(mode)

def start_morse():
    global mode
     utime.sleep_ms(10)
     text_clr()
    if mode==0:
         mode_disp(mode)
         random_txt()
    elif mode==1:
         abcd_txt()

def speed_read():
    global wpm
    global dot_time
    global dash_time
     dot_time=speed.read_u16() >> 8
    if dot_time < 50:
         dot_time=50
    if dot_time >200:
         dot_time=200
     wpm=int(1200/dot_time)
     wpm_disp()

def wpm_disp():
    global wpm
    global old_wpm
    if wpm != old_wpm:
         oled.fill_rect(96,0,32,8,0)
         oled.show()
         oled.text(str(wpm),96,0)
         oled.show()
     old_wpm=wpm        

#setup
i2c=I2C(0,sda=Pin(16),scl=Pin(17),freq=400000)
oled=ssd1306.SSD1306_I2C(128,64,i2c)

#ピン割り当てと設定
start_sw=Pin(15,Pin.IN,Pin.PULL_UP)
mode_set=Pin(14,Pin.IN,Pin.PULL_UP)
speed=ADC(0)

#PWM設定
pwm=PWM(Pin(0))#morse音をPin0に出力
pwm.freq(700)   #tone=700Hz
pwm.duty_u16(0)#最初、音を出さない

x0 = 8
y0 = 16
mode = 0
mvalue=0
old_mvalue= 0
svalue=0
old_svalue=0
wpm = 10
old_wpm =0

#初期画面表示
oled.text("M:      WPM:",0,0)
oled.show()   
mode_disp(mode)
wpm_disp()


#main loop
while True:
     speed_read()
     mvalue=mode_set.value()
    if mvalue==0 and old_mvalue==1:
         mode_change()
     old_mvalue=mvalue
     svalue=start_sw.value()
    if svalue==0 and old_svalue==1:
         start_morse()
     old_svalue=svalue
     utime.sleep_ms(10)

-----------------------------------------------------------------------------------

 ブレッドボードです。


 ABC順モードです。

 白いボタンがSTART/STOP、青いボタンがモードボタンです。速度は右上に表示されます。

 電池は、39番ピンVSYSに接続します。

Raspberry Pi Pico MicroPython OLED表示テスト

2021-11-23 20:03:02 | Raspberry Pi Pico
 Raspberry Pi Picoで、0.96インチのI2C接続OLED(128×64ドット)の表示テストをします。(秋月電子で580円)

 このOLEDのコントローラは、SSD1306で、I2Cアドレスは、0x3Cです。

 今回は、SSD1306用のライブラリーを利用します。その方法は、メタエレ実験室というサイトの記事を参考にさせていただきました。

  まず、Thonny IDEで、ツール→Manage packages for MIcroPython deviceを開きます。
 SSD1306で検索し、検索結果から「micropython-ssd1306」をインストールします。Pico内のlibというフォルダにssd1306.pyというファイルが保存されます。
 これで、準備完了です。

 PicoとOLEDの配線回路図です。SDA=GPIO16,SCL=GPIO17とします。


 テストスクリプトです。main.pyとしてPicoにsaveします。
---------------------------------------------------------
from machine import I2C,Pin
from utime import sleep
import ssd1306

i2c=I2C(0,sda=Pin(16),scl=Pin(17),freq=400000)
oled=ssd1306.SSD1306_I2C(128,64,i2c)

oled.text("Hello World!", 0, 5)
oled.show()
---------------------------------------------------------
ブレッドボードです。「Hello World!」が表示されました。

 このライブラリでは、簡単なグラフィックスを描画することができます。
 tom's HARDWAREというサイトに説明がありました。

 oled.pixel(x,y,c)    x,yにdotを表示します。c=1で表示、c=0で消去です。
 oled.hline(x,y,w,c)  x,yを始点として長さwの水平な線を引きます。
 oled.vline(x,y,h,c)  x,yを始点として長さwの垂直な線を引きます。
 oled.line(x1,y1,x2,y2,c) x1,y1からx2,y2まで線を引きます。
 oled.rect(x,y,w,h,c)  x,yを始点(左上)として、幅w、高さhの四角形を描きます。
 oled.fill_rect (x,y,w,h,c)  x,yを始点(左上)として、幅w、高さhの四角形を描き、中を塗りつぶします。
 oled.fill(c) 画面全体をc色で塗りつぶします。つまり、c=0で全画面クリア、c=1で全画面白になります。

 Hello World!を四角形で囲み、1秒ごとに数字をカウントアップさせるスクリプトを書いて動かしてみました。
 スクリプトです。
------------------------------------------------------------------------------------
from machine import I2C,Pin
from utime import sleep
import ssd1306

i2c=I2C(0,sda=Pin(16),scl=Pin(17),freq=400000)

oled=ssd1306.SSD1306_I2C(128,64,i2c)

oled.rect(10,0,100,18,1)
oled.show()
oled.text("Hello World!",13,5)
oled.show()

n=0

while True:
    oled.fill_rect(20,40,30,10,0)
    oled.show()
    oled.text(str(n),20,40)
    oled.show()
    n=n+1
    sleep(1)
------------------------------------------------------------------------------------
ブレッドボードです。


 このライブラリでは、残念ながらフォントの大きさは変えられないようです。

Raspberry Pi Pico MicroPython I2C LCD1602表示テスト

2021-11-21 19:58:22 | Raspberry Pi Pico
 LCD1602は、従来から最も利用されている表示器です。最近は、配線数の少ないパラレルシリアルインターフェース付きのI2C用LCD1602が主流になってきました。

 今回は、そのインターフェース付きのI2C LCD1602の表示テストをします。今回もライブラリーを使わずにスクリプトを作ってみます。以前、Raspberry Piでテストしたスクリプトを元にしています。記事はこちら

  Raspberry Pi PicoにこのI2C LCD1602を接続する場合に問題点があります。Picoの電圧が3.3Vなのに対し、I2C LCD1602の電源電圧は、5Vなのです。ネット上のサイトや動画では、直接接続している例が多いのですが、LCD1602側で5VでプルアップされているI2Cラインを見てみました。ピークで約4.3Vの電圧がPicoにかかることが分かりました。



 Picoの入力電圧は、-0.5V~+4.13Vで、Picoが入力となる場合(スレーブ側になる場合)最大定格電圧を越えてしまいます。今回の場合、Picoはマスターとしてだけの動作ですので、大丈夫だと思うのですが、どうも精神衛生上よくありません。そこで、秋月電子のI2Cバス用の双方向電圧レベル変換モジュールPCA9306をPicoとLCD1602の間に入れることにしました。

 接続回路図です。PicoのI2Cポートは、id=1,SDA=GP14,SCL=GP15を使いました。

 スクリプトです。
---------------------------------------------------------------------------------------
"""
I2C LCD1602 test
2021.11.19
JH7UBC Keiji Hata
"""
from machine import I2C,Pin
from utime import sleep

i2c=I2C(1,sda=Pin(14),scl=Pin(15),freq=100000)

LCD_addr=0x27
LCD_EN=0x04 #LCD Enable
LCD_BL=0x08 #Back Light
CMD=0x00 #command mode
CHR=0x01 #character mode
LINE1=0x80 #Line1 top address
LINE2=0xC0 #Line2 top address

buf=bytearray(2)

def LCD_write(bits,mode):
    #High 4bits
    data=(bits & 0xF0)|mode
    buf[0]=data|LCD_EN|LCD_BL
    buf[1]=data|LCD_BL
    i2c.writeto(LCD_addr,buf)
    sleep(0.0001)#wait 100us
    #Low 4bits
    data=((bits<<4)&0xF0)|mode
    buf[0]=data|LCD_EN|LCD_BL
    buf[1]=data|LCD_BL
    i2c.writeto(LCD_addr,buf)
    sleep(0.0001)#wait 100us

def LCD_init():
    LCD_write(0x33,CMD)#8bit mode 0x03を2回送る
    LCD_write(0x32,CMD)#8bit mode,4bit mode 0x02を送る
    LCD_write(0x06,CMD)#Entry modeセット
    LCD_write(0x0C,CMD)#表示ON,カーソルOFF,カーソル点滅OFF
    LCD_write(0x28,CMD)#2桁表示,7ドットモード
    LCD_write(0x01,CMD)#Display clear
    sleep(0.002)#waite 2ms

def LCD_clear():
    LCD_write(0x01,CMD)
    sleep(0.002)#waite 2ms

def LCD_home():
    LCD_write(0x02,CMD)
    sleep(0.002)#waite 2ms

def LCD_cursor(x,y):
    if y==0:
        LCD_write(LINE1+x,CMD)
   if y==1:
       LCD_write(LINE2+x,CMD)

def LCD_print(str):
    for c in str:
       LCD_write(ord(c),CHR)

LCD_init()
LCD_clear()
LCD_home()
LCD_cursor(1,0)
LCD_print("Hello World!")
count=0

while True:
    LCD_cursor(1,1)
    LCD_print(str(count))
    count=count+1
    sleep(1)

---------------------------------------------------------------------------------------
 1行目に「Hello World!」が表示され、2行目に数字が0からカウントアップされます。

 ブレッドボードです。コントラストはインターフェース基板のボリュームで調整します。


Raspberry Pi Pico MicroPython I2C LCD AQM0802A表示テスト

2021-11-19 07:55:25 | Raspberry Pi Pico
 Raspberry Pi Picoで、MicroPythonを使い、I2C LCD 0802Aの表示テストをします。
 これまで、AQM0802Aは、PICやArduinoで利用し、その表示プログラムはJH7UBCホームページに掲載しています。AQM0802Aのコントローラは、ST7032です。ST7032のライブラリがあれば、それを利用するのが簡単です。

 今回はMicroPythonでのI2Cの使い方の勉強なので、ライブラリを使わないで、プログラミングをします。(ライブラリがあるのか、確認していません)

 PicoとAQM0802Aの接続回路図です。id=0 SCL=GP17,SDA=GP16としました。

 I2Cデバイスへの書き込みメッソドには、writeto(addr,buf)やwriteto_mem(addr,memaddr,buf)などがあります。
 writeto(addr,buf)で、addr=0x3e,buf[0]=0x00,buf[1]=0x02としてI2CのSCLとSDAを観測してみました。赤線がSCL、青線がSDAです。OKです。

 スクリプトです。Qiitaさんの旧版スクリプト新版スクリプトTdukurelさんのスクリプトを参考にさせていただきました。
 LCDを初期化した後、1行目に「JH7UBC」が表示され、2行目に数値が0からカウントアップされます。
------------------------------------------------------------------
from machine import Pin, I2C
import utime

i2c = I2C(0,freq=100000,scl=Pin(17),sda=Pin(16))
addr=0x3e
buf=bytearray(2)

def write_cmd(cmd):
    buf[0]=0x00
    buf[1]=cmd
    i2c.writeto(addr,buf)

def write_char(char):
    buf[0]=0x40
    buf[1]=char
    i2c.writeto(addr,buf)

def print(str):
    for c in str:
        write_char(ord(c))

def LCD_cursor(x,y):
    if y==0:
        write_cmd(0x80+x)
    if y==1:
        write_cmd(0xc0+x)

def LCD_clear():
    buf[0]=0x00
    buf[1]=0x01
    i2c.writeto(addr,buf)
    utime.sleep(0.001)

def LCD_home():
    buf[0]=0x00
    buf[1]=0x02
    i2c.writeto(addr,buf)
    utime.sleep(0.001)

def LCD_init():
    orders = [b'\x38', b'\x39', b'\x14', b'\x73', b'\x56', b'\x6c',b'\x38', b'\x0c', b'\x01']
    utime.sleep(0.04)
    for order in orders:
        i2c.writeto_mem(addr, 0x00, order)
        utime.sleep(0.001)

LCD_init()
LCD_clear()
LCD_home()
print('JH7UBC')
num=0

while True:
    LCD_cursor(0,1)
    print(str(num))
    num=num+1
    utime.sleep(1)


------------------------------------------------------------------
 LCD初期化の手順について説明します。各コマンドを順番にLCDに送ります。
 0x38 #8bit,2line
  ↓
 0x39 #IS=1:extension mode set
  ↓
 0x14 #Internal OSC Frequency
  ↓
 0x73 #Contrast 私の場合この値がちょうど良いコントラスト(要調整)
  ↓
 0x56 #Power/ICON/Contrast control
  ↓
 0x6C #Follower control
  ↓
 0x38 #IS=0:extension mode cancel
  ↓
 0x0C #Display ON
  ↓
 0x01 #Clear Display

 ブレッドボードです。無事表示されました。




Raspberry Pi Pico MicroPython I2C テスト

2021-11-18 15:30:25 | Raspberry Pi Pico
Raspberry Pi PicoのI2Cのテストをします。
machineからI2Cモジュールをインポートします。
コンストラクタI2Cの仕様は、I2C(id, scl, sda, freq)です。
idだけを指定した時のデフォルト値は、

id      sda  scl   freq
0 GPIO8 GPIO9 400000(400KHz)
1 GPIO6 GPIO7 400000(400KHz)
です。
なお、I2Cはid=0か1で次の12の組み合わせで利用することができます。
id      sda  scl 
0    GPIO0   GPIO1
1    GPIO2   GPIO3
0    GPIO4   GPIO5
1    GPIO6   GPIO7 デフォルト
0    GPIO8   GPIO9 デフォルト
1    GPIO10   GPIO11
0    GPIO12   GPIO13
1    GPIO14   GPIO15
0    GPIO16   GPIO17
1    GPIO18   GPIO19
0    GPIO20   GPIO21
1    GPIO22   GPIO23

 I2CバスにI2C LCD AQM0802Aを接続して、I2Cアドレスを調べてみます。
 スクリプトです。id=0としましたので、AQM0802AのSDAはGP8にSCLはGP9に接続します。実行結果はshellに表示されます。


 shellの上段は、I2C(1)の場合、下段はI2C(0)の場合です。
 freqとSCLとSDAのピン番号が表示されます。デフォルト値ですね。
 I2Cのクロック周波数は、最大400KHzになると思ったのですが、399361Hz(399.361KHz)と表示されました。この値が最大値のようです。

 AQM0802AのI2Cスレーブアドレスは62=0x3eと表示されました。

 次に、クロック周波数frqとscl,sdaを変えてみましょう。freq=100KHz(標準の周波数)、scl=GP17,sda=GP16の場合のスクリプトです。



 次は、I2C LCD AQM0802Aの表示テストをします。