概要
非接触空間センサーのドットマトリクスLEDへの応用を考えました。
シリアル-パラレル変換はシフトレジスタ(74HC595)を使う簡単な方法です。SPIにGPIOを2ピン追加するだけ、非接触センサーのカラーLEDも同様にシフトレジスタを制御しています。
双方向ハンドセンサーリンク https://interactive-hand-sensor.com/root/
シフトレジスタとSPIを使ったシリアルーパラレル変換の長所
- SPI以外の追加ピンが少ない(1〜3)
- SS(IC選択)はRCKピンを使う
- OE(出力ON/OFF)とCLR(リセット)は省略できる
- プログラムが簡単
- SPI出力の後にRCKをON/OFFするだけ
動画
回路図
シフトレジスタ書き込み部分プログラム(Python3)
# *** 書き込み値 Rcの設定 ***
chg = lambda x: 1 << (x - 1) if x > 0 else 0
Rc = [-1, -1]
oe8(True) # 出力Off
for rc in args: #r:3, c:1
for i in range(len(rc)):
k = chg(rc[i])
if Rc[i] == -1:
Rc[i] = k
elif Rc[i] != k:
Rc[i] |= k
Rc[0] = ~Rc[0] # invert row
# *** 書き込み ***
spi.write( bytearray([Rc[1], Rc[0]]) ) # spi書き込み
fix8(True) # データ保存、IC選択
fix8(False) # データ保存、IC選択
oe8(False) # 出力On
プログラム
main.py
from machine import PWM
from Sensor_CLED import *
def makePwm(gpio, freq, duty16):
pwm = PWM(gpio)
pwm.freq(freq)
pwm.duty_u16(duty16)
makePwm(pwmCled_gpio, 50, 1<<11) # set pwm
def makeIndi():
val = True
def closure(timer):
nonlocal val
CLed.setIndi(val)
val = not val
return closure
indi = makeIndi()
tim = Timer()
tim.init(freq=3, mode=Timer.PERIODIC, callback=indi)
def makeCounter():
cnt = 0
def closure(num):
nonlocal cnt
cnt += 1
if cnt == num:
cnt = 0
return True
return False
return closure
counter = makeCounter()
def direction(lst8):
CRI = 600
''' 1, 0, 7,
2, 6,
3, 4, 5, '''
def near(a, b):
if ((a == 0) and (b == 7)) or ((a == 7) and (b == 0)):
return True
return abs(a - b) == 1
def ccw(a, b):
if ((a == 0) and (b == 7)) or ((a == 7) and (b == 0)):
return b < a
return b > a
top3 = sorted(lst8, reverse=True)[:3]
if top3[0] < CRI:
return -1 # less than CRITERIA
if min(lst8) > (top3[0] >> 1):
return 8 # no direction
idxTop = [lst8.index(v) for v in top3]
next0is1 = near(idxTop[0], idxTop[1])
next0is2 = near(idxTop[0], idxTop[2])
dif01near = (top3[0] - top3[1]) < (top3[1] >> 1)
dif12near = (top3[1] - top3[2]) < (top3[2] >> 1)
if (next0is1 and dif01near) and ((next0is2 and not dif12near) or (not next0is2)):
rev = idxTop[0] + (0.5 if ccw(idxTop[0], idxTop[1]) else -0.5)
if rev == -0.5:
return 7.5
return rev
return idxTop[0]
# **************** led8x8 *******************
oe8 = getPinOut('OE_I8x8')
fix8 = getPinOut('FIX_8x8')
spi = getPinOut('SPI') # 6,7,8,9pin
def writeSpi(args): # args:(row0, col0), (row1, col1) up to 2 pieces, same row or col
chg = lambda x: 1 << (x - 1) if x > 0 else 0
Rc = [-1, -1]
oe8(True)
for rc in args: #r:3, c:1
for i in range(len(rc)):
k = chg(rc[i])
if Rc[i] == -1:
Rc[i] = k
elif Rc[i] != k:
Rc[i] |= k
Rc[0] = ~Rc[0] # invert row
spi.write( bytearray([Rc[1], Rc[0]]) )
#print(bin(Rc[0]), bin(Rc[1]))
fix8(True)
fix8(False)
oe8(False)
i8x8 = {-1:((0,0),),0:((4,1),(5,1)),0.5:((6,1),),1:((7,2),),1.5:((8,3),),2:((8,4),(8,5)),2.5:((8,6),),3:((7,7),),3.5:((6,8),),4:((4,8),(5,8)),4.5:((3,8),),5:((2,7),),5.5:((1,6),),6:((1,4),(1,5)),6.5:((1,3),),7:((2,2),),7.5:((3,1),),8:((4,4),(4,5),(5,4),(5,5))}
# ***************** main ********************
if __name__ == '__main__':
cled = CLed()
Sensor.init() # *** initialize Sensor ***
print('******************* START ********************')
cled.turnOn(5)
time.sleep(1)
while True:
Sensor.setAd()
cled.turnOn()
if counter(10):
lst = Sensor.getAdLst()
dir = direction(lst)
writeSpi(i8x8[dir])
#print(lst)
#print(dir)
Sensor_CLED.py
from impQ import *
from machine import Timer
import time
COL = getPinOut('COL') # 3,4,5pin
ROW = getPinOut('ROW')
ssR = getPinOut('SS') # 6pin
spi = getPinOut('SPI') # 6,7,8,9pin
selSen = getPinOut('SEL_SEN') # 10pin
oeIled = getPinOut('OE_ILED') # 11pin
fix = getPinOut('FIX_CLED') # 12pin
oeCled, oeCled_gpio = getPinOut('OE_CLED', True) # 13pin: closure, gpio
pwmCled, pwmCled_gpio = getPinOut('PWM', True) # 14pin
COL_LEN = 8
ROW_LEN = 1
# def make_irqHandler():
# val = True
# def closure(timer):
# nonlocal val
# val = not val
# def getVal():
# return val
# return closure, getVal
# irq_handler, irq_val = make_irqHandler()
# tim = Timer()
# tim.init(period=1000, mode=Timer.PERIODIC, callback=irq_handler)
# ********************************* Sensor *************************************
class Sensor():
# cri = [90,135,195,275,390,535,720,990,1300,1750,2200,2800]
cri = [90,275,535,990,1750,2800]
VON_CORRECTION = 20 # for GND Level correction
def __init__(self):
self.__adVal = 0
self.__value = 0
@property
def AD(self):
return self.__adVal
@property
def Value6(self): # 1 ~ 6
return self.__value
@Value6.setter
def Value6(self, adVal):
for i in range(len(self.cri)):
if adVal < self.cri[i] + self.VON_CORRECTION:
self.__value = i
return
self.__value = len(self.cri)
@classmethod
def init(cls): # *********** init *************
cls.adAryInit = [[0 for _ in range(COL_LEN)] for _ in range(ROW_LEN)]
print('cri:', cls.cri)
cls.sens = [[Sensor() for _ in range(COL_LEN)] for _ in range(ROW_LEN)]
# *********** calibration ***********
shift = 8
for i in range(1 << shift): # add 256 times
cls.setAd(True)
for r in range(ROW_LEN): # >> 8
for c in range(COL_LEN):
cls.adAryInit[r][c] = cls.adAryInit[r][c] >> shift
@classmethod
def getAdLst(cls, shift=0):
if ROW_LEN == 1:
return [s.AD>>shift for s in cls.sens[0]]
else:
adLst = []
for r in range(ROW_LEN):
adLst.extend([s.AD>>shift for s in cls.sens[r]])
return adLst
@classmethod
def setAd(cls, isInit=False): #
''' row -> col '''
for r in range(ROW_LEN):
outNumBit(ROW, r) # set row
for c in range(COL_LEN):
outNumBit(COL, c) # set col
cls.__spiAdc(r, c, isInit)
@classmethod
def __spiAdc(cls, r, c, isInit):
''' get AD-value and set it to sens[r][c] '''
ledSta = oeCled_gpio.value() # buffering oe
cls.__adcOn(True, False) # oeIled:off
Voff = cls.__getAdVal(c)
cls.__adcOn(True) # oeIled:on
Von = cls.__getAdVal(c)
cls.__adcOn(False)
oeCled(ledSta) # undo oe
if isInit:
cls.adAryInit[r][c] += noMinus(Von - Voff - (Voff>>2))
else:
adVal = noMinus(Von - Voff - (Voff>>2) - cls.adAryInit[r][c])
cls.sens[r][c].__adVal = adVal
cls.sens[r][c].Value6 = adVal
@classmethod
def __getAdVal(cls, c):
wLst = bytearray([ 6 | (c >> 2), c << 6, 0])
rLst = bytearray([0,0,0])
ssR(False) # ss on
spi.write_readinto(wLst,rLst)
ssR(True) # ss off
return ((rLst[1] & 0x0f) << 8) + rLst[2]
@classmethod
def __adcOn(cls, isSelSen, isIled = None):
if isIled is None:isIled = isSelSen
selSen(isSelSen) # selsen(10) True:sensor on
oeIled(isIled)
# ********************************** CLed **************************************
class CLed():
''' Serial Parallel converter for color LED '''
indiColor = 6
__indiBlk = False # indicator blink
allOff = True
indiOff = False # *** indicator OFF ***
def __init__(self):
pass
def turnOn(self, tpl2=-1):# val:int/tuple
# ***** value preperation *****
ssR(False) # ss on
oeCled(False) # oe off
for r in range(ROW_LEN):
outNumBit(ROW, r) # set row
if type(tpl2) == int:
if tpl2 == -1: # *** normal ***
sLst = [s.Value6 for s in Sensor.sens[r]]
else: # paint all surface
sLst = [tpl2 for _ in range(COL_LEN)]
else: # tpl2 is tuple ex:tpl = ((1,2,3,4,5,6,7,6),(1,2,3,4,5,6,7,6))
sLst = [tpl2[r][c] if tpl2[r][c] > 0 else 0 for c in range(COL_LEN)]
self.allOff &= (sum(sLst) == 0)
self.setSb(sLst, r)
self.allOff = True
ssR(True) # ss off
oeCled(True) # oe on
def setSb(self, lst, row): # set single board, cube-indicator, row:indicator row detection
# colorTbl = (0,4,5,1,3,2,6) #IHS10
colorTbl = (0,1,3,2,6,4,5) #IHS11
val24 = 0
for c in range(COL_LEN): # col7+col6+col5+..col0
isIndi = ((lst[c] == 0) and self.allOff and self.__indiBlk) and (not self.indiOff)
if (row == ROW_LEN - 1) and (c == COL_LEN - 1) and isIndi:
cVal = self.indiColor
else:
cVal = lst[c] # sensor value
val24 += colorTbl[cVal] << c * 3
wLst = bytearray([(val24 >> b * COL_LEN) & 0xff for b in range(2, -1, -1)])
ssR(False) # ss on
spi.write(wLst)
ssR(True) # ss off
fix(True) # latch clock and output color LED
fix(False)
@classmethod
def setIndi(cls, isOn):
cls.__indiBlk = isOn
@classmethod
def setIndiColor(cls, color = 6):
cls.indiColor = color
if __name__ == '__main__':
print('Sensor_CLED is running now!')
impQ.py
from machine import Pin, SPI
def GPIOOUT(pin, ini = 0, isGPIO = False):
gpio = Pin(pin, Pin.OUT)
gpio.value(ini)
def closure(val):
gpio.value(val)
if isGPIO:
return closure, gpio
else:
return closure
def GPIO_NUM_BIT(pin, bit):
gpio = Pin(pin, Pin.OUT)
gpio.value(0)
sel = 1 << bit
def closure(num):
gpio.value(num & sel)
return closure
def outNumBit(nbLst, num): # nbLst:row or col
for f in nbLst:
f(num)
def noMinus(n):
if n < 0:
return 0
return n
pinDic = {'COL':((0,0),(1,1),(2,2)),'SS':(5,1),'SPI':0,'SEL_SEN':7,'OE_ILED':8,'OE_I8x8':12,'FIX_8x8':13,
'FIX_CLED':9,'OE_CLED':10,'PWM':11,'PWM2':18,'ROW':((19,0),(20,1),(21,2))}
'''
ABC:345 SS:6 MOSI:7 MISO:8 SCK:9 SEL_SEN:10 OE_ILED:11 FIX_ILED:12 OE_CLED:13 PWM_CLED:14
'''
def getPinOut(pStr, isGPIO = False): # pin string
if pStr not in pinDic:return None
pn = pinDic[pStr]
if pStr in ('COL','ROW'):
return [GPIO_NUM_BIT(pin, bit) for (pin, bit) in pn]
elif pStr == 'SPI':
spi = SPI(pn, baudrate=2000000, sck=Pin(6), mosi=Pin(3), miso=Pin(4))
return spi
elif type(pn) == tuple:
return GPIOOUT(pn[0], pn[1]) # active-H
elif type(pn) == int:
return GPIOOUT(pn, 0, isGPIO) # closure, gpio
else:
return None
if __name__ == '__main__':
print('impQ is running now!')
双方向ハンドセンサーはサイエンスカフェえむしーじじょう様(https://sciencecafe-mc2.com/)で展示中、12月末まで
投稿者の人気記事
-
3duilab
さんが
2021/10/24
に
編集
をしました。
(メッセージ: 初版)
-
3duilab
さんが
2021/10/24
に
編集
をしました。
-
3duilab
さんが
2021/10/24
に
編集
をしました。
-
3duilab
さんが
2022/01/18
に
編集
をしました。
-
3duilab
さんが
2022/02/26
に
編集
をしました。
ログインしてコメントを投稿する