# Library for interfacing with an MCP23008 chip, as used on the jeelabs
# expander and output plugs
namespace eval jeeio {
package require piio
namespace path ::twowire
variable slaveaddr 0x20
variable regs {
iodir 0 ipol 1 gpinten 2 defval 3
intcon 4 iocon 5 gppu 6 intf 7
intcap 8 gpio 9 olat 10
}
namespace ensemble create -subcommands {
expanderplug outputplug setoutput setinput readpin writepin
writeddr writegpio readgpio
}
proc readreg {fd reg} {
variable regs
return [readregbyte $fd [dict get $regs $reg]]
}
proc writereg {fd reg val} {
variable regs
writeregbyte $fd [dict get $regs $reg] $val
}
proc expanderplug {bus {num 0}} {
variable slaveaddr
return [twowire $bus [expr {$slaveaddr + $num}]]
}
proc outputplug {bus {num 0}} {
variable slaveaddr
set fd [twowire $bus [expr {$slaveaddr + 6 + $num}]]
# Configure all pins as outputs
writeddr $fd 0
return $fd
}
proc motorplug {bus {num 0}} {
variable slaveaddr
set fd [twowire $bus [expr {$slaveaddr + 6 + $num}]]
# Configure pin 0 through 3 as outputs
set ddr [readreg $fd iodir]
writereg $fd iodir [expr {$ddr & ~0xf}]
return $fd
}
# Configure the function of all 8 pins
proc writeddr {fd val} {
writereg $fd iodir $val
}
# Set all outputs at once
proc writegpio {fd val} {
writereg $fd olat $val
}
# Read the state of all pins
proc readgpio {fd} {
return [readreg $fd gpio]
}
# Configure a single pin as output
proc setoutput {fd pin} {
set ddr [readreg $fd iodir]
writereg $fd iodir [expr {$ddr & ~(1 << $pin)}]
}
# Configure a single pin as input
proc setinput {fd pin} {
set ddr [readreg $fd iodir]
writereg $fd iodir [expr {$ddr | (1 << $pin)}]
}
# Read the state of a single pin
proc readpin {fd pin} {
return [expr {([readreg $fd gpio] >> $pin) & 1}]
}
# Set the state of a single pin
proc writepin {fd pin state} {
set olat [readreg $fd olat]
set new [expr {$state ? $olat | (1 << $pin) : $olat & ~(1 << $pin)}]
writereg $fd gpio $new
}
### LCD plug related commands
# Pins 0-3 are the data bits
# Pin 4 is RS (Register select)
# Pin 5 is SW (Switch)
# Pin 6 is E (Enable)
# Pin 7 is BL (Backlight)
proc lcdplug {bus {rows 2} {cols 16}} {
variable slaveaddr
variable lcd
set fd [twowire $bus [expr {$slaveaddr + 4}]]
# Prepare all outputs to start out low
writereg $fd olat 0
# Set all pins as output, except for pin 5
writereg $fd iodir [expr {1 << 5}]
# There's no way to know what state the device is in, so we have to do
# a full "initialization by instruction"
# Set the device to 8-bit interface
lcdwrite $fd 0x03
piio usleep 4100
lcdwrite $fd 0x03
piio usleep 100
lcdwrite $fd 0x03
piio usleep 37
# Set the device to 4-bit interface
lcdwrite $fd 0x02
piio usleep 37
# Configure the number of display lines and character font
command $fd 0x2C
piio usleep 37
# Store the display size
dict set lcd $fd [dict create rows $rows cols $cols]
return $fd
}
proc lcdwrite {fd val} {
# Write the data with the enable bit set
writereg $fd gpio [expr {$val | 0x40}]
# Clear the enable bit
writereg $fd gpio [expr {$val & ~0x40}]
}
proc command {fd byte} {
lcdwrite $fd [expr {$byte >> 4}]
lcdwrite $fd [expr {$byte & 0xf}]
}
proc clear {fd} {
command $fd 1
}
proc home {fd} {
command $fd 2
}
proc cursor {fd row col} {
set offset [expr {($row & 1 ? 64 : 0) + ($row & 2 ? 20 : 0)}]
command $fd [expr {0x80 + $offset + $col}]
}
proc print {fd str} {
binary scan $str cu* chars
foreach byte $chars {
lcdwrite $fd [expr {($byte >> 4) | 0x10}]
lcdwrite $fd [expr {($byte & 0xf) | 0x10}]
# Wait the required time between characters
piio usleep 41
}
}
}