├── helloworld └── main.go ├── LICENSE ├── i2c.go ├── tinyterm └── main.go └── lcd.go /helloworld/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/davecheney/i2c" 4 | import "log" 5 | import "fmt" 6 | import "time" 7 | 8 | func check(err error) { 9 | if err != nil { log.Fatal(err) } 10 | } 11 | 12 | func main() { 13 | i, err := i2c.New(0x27, 1) 14 | check(err) 15 | lcd, err := i2c.NewLcd(i, 2, 1, 0, 4, 5, 6, 7, 3) 16 | check(err) 17 | lcd.BacklightOn() 18 | lcd.Clear() 19 | for { 20 | lcd.Home() 21 | t := time.Now() 22 | lcd.SetPosition(1, 0) 23 | fmt.Fprint(lcd, t.Format("Monday Jan 2")) 24 | lcd.SetPosition(2, 0) 25 | fmt.Fprint(lcd, t.Format("15:04:05 2006")) 26 | lcd.SetPosition(4, 0) 27 | fmt.Fprint(lcd, "i2c, VGA, and Go") 28 | time.Sleep(333 * time.Millisecond) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Dave Cheney 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /i2c.go: -------------------------------------------------------------------------------- 1 | // Package i2c provides low level control over the linux i2c bus. 2 | // 3 | // Before usage you should load the i2c-dev kenel module 4 | // 5 | // sudo modprobe i2c-dev 6 | // 7 | // Each i2c bus can address 127 independent i2c devices, and most 8 | // linux systems contain several buses. 9 | package i2c 10 | 11 | import ( 12 | "fmt" 13 | "os" 14 | "syscall" 15 | ) 16 | 17 | const ( 18 | i2c_SLAVE = 0x0703 19 | ) 20 | 21 | // I2C represents a connection to an i2c device. 22 | type I2C struct { 23 | rc *os.File 24 | } 25 | 26 | // New opens a connection to an i2c device. 27 | func New(addr uint8, bus int) (*I2C, error) { 28 | f, err := os.OpenFile(fmt.Sprintf("/dev/i2c-%d", bus), os.O_RDWR, 0600) 29 | if err != nil { 30 | return nil, err 31 | } 32 | if err := ioctl(f.Fd(), i2c_SLAVE, uintptr(addr)); err != nil { 33 | return nil, err 34 | } 35 | return &I2C{f}, nil 36 | } 37 | 38 | // Write sends buf to the remote i2c device. The interpretation of 39 | // the message is implementation dependant. 40 | func (i2c *I2C) Write(buf []byte) (int, error) { 41 | return i2c.rc.Write(buf) 42 | } 43 | 44 | func (i2c *I2C) WriteByte(b byte) (int, error) { 45 | var buf [1]byte 46 | buf[0] = b 47 | return i2c.rc.Write(buf[:]) 48 | } 49 | 50 | func (i2c *I2C) Read(p []byte) (int, error) { 51 | return i2c.rc.Read(p) 52 | } 53 | 54 | func (i2c *I2C) Close() error { 55 | return i2c.rc.Close() 56 | } 57 | 58 | func ioctl(fd, cmd, arg uintptr) (err error) { 59 | _, _, e1 := syscall.Syscall6(syscall.SYS_IOCTL, fd, cmd, arg, 0, 0, 0) 60 | if e1 != 0 { 61 | err = e1 62 | } 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /tinyterm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "os/exec" 8 | "sync" 9 | 10 | "github.com/davecheney/i2c" 11 | ) 12 | 13 | var ( 14 | flags = flag.NewFlagSet("", flag.ContinueOnError) 15 | stderr = flags.Bool("e", true, "redirect stderr") 16 | ) 17 | 18 | func init() { 19 | flags.Parse(os.Args[1:]) 20 | if len(flags.Args()) < 1 { 21 | log.Fatal("command missing") 22 | } 23 | } 24 | 25 | func check(err error) { 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | 31 | type lcdWriter struct { 32 | sync.Mutex 33 | pos byte // screen position, 4 x 16 chars 34 | buf [0x60]byte // hd44780 ram, arranged by lunacy 35 | lcd *i2c.Lcd 36 | } 37 | 38 | // maps from linear memory locations to crackful hd44780 memory locations 39 | var lcdtab = [...]byte{ 40 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 41 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 42 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 43 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 44 | } 45 | 46 | func (w *lcdWriter) Write(buf []byte) (int, error) { 47 | w.Lock() 48 | defer w.Unlock() 49 | var n int 50 | for ; n < len(buf); n++ { 51 | switch c := buf[n]; c { 52 | case '\r': 53 | continue 54 | case '\t': 55 | w.pos += 4 56 | w.pos -= (w.pos % 0x4) 57 | case '\n': 58 | w.pos += 0x10 59 | w.pos -= (w.pos % 0x10) 60 | default: 61 | index := lcdtab[w.pos] 62 | w.buf[index] = c 63 | w.lcd.Command(i2c.CMD_DDRAM_Set + index) 64 | w.lcd.Write(w.buf[index : index+1]) 65 | w.pos++ 66 | } 67 | w.scrollup() 68 | } 69 | return n, nil 70 | } 71 | 72 | func (w *lcdWriter) scrollup() { 73 | redraw := false 74 | for ; w.pos >= 0x40; w.pos -= 0x10 { 75 | copy(w.buf[0x00:0x10], w.buf[0x40:0x50]) 76 | copy(w.buf[0x40:0x50], w.buf[0x10:0x20]) 77 | copy(w.buf[0x10:0x20], w.buf[0x50:0x60]) 78 | fill(w.buf[0x50:0x60], 0x20) 79 | redraw = true 80 | } 81 | if redraw { 82 | w.redraw() 83 | } 84 | } 85 | 86 | func (w *lcdWriter) redraw() { 87 | w.lcd.Command(i2c.CMD_DDRAM_Set + 0x00) // home 88 | w.lcd.Write(w.buf[0x00:0x20]) 89 | w.lcd.Command(i2c.CMD_DDRAM_Set + 0x40) 90 | w.lcd.Write(w.buf[0x40:0x60]) 91 | } 92 | 93 | func fill(b []byte, c byte) { 94 | for i := range b { 95 | b[i] = c 96 | } 97 | } 98 | 99 | func main() { 100 | dev, err := i2c.New(0x27, 1) // vga port 101 | check(err) 102 | 103 | lcd, err := i2c.NewLcd(dev, 2, 1, 0, 4, 5, 6, 7, 3) 104 | check(err) 105 | lcd.BacklightOn() 106 | lcd.Clear() 107 | 108 | w := lcdWriter{lcd: lcd} 109 | fill(w.buf[:], 0x20) 110 | cmd := exec.Command(flags.Args()[0], flags.Args()[1:]...) 111 | cmd.Stdin = os.Stdin 112 | cmd.Stdout = &w 113 | if *stderr { 114 | cmd.Stderr = &w 115 | } 116 | 117 | cmd.Run() 118 | } 119 | -------------------------------------------------------------------------------- /lcd.go: -------------------------------------------------------------------------------- 1 | package i2c 2 | 3 | // i2c LCD adapter 4 | // Adapted from http://think-bowl.com/raspberry-pi/installing-the-think-bowl-i2c-libraries-for-python/ 5 | 6 | import "time" 7 | 8 | const ( 9 | // Commands 10 | CMD_Clear_Display = 0x01 11 | CMD_Return_Home = 0x02 12 | CMD_Entry_Mode = 0x04 13 | CMD_Display_Control = 0x08 14 | CMD_Cursor_Display_Shift = 0x10 15 | CMD_Function_Set = 0x20 16 | CMD_DDRAM_Set = 0x80 17 | 18 | // Options 19 | OPT_Increment = 0x02 // CMD_Entry_Mode 20 | // OPT_Display_Shift = 0x01 // CMD_Entry_Mode 21 | OPT_Enable_Display = 0x04 // CMD_Display_Control 22 | OPT_Enable_Cursor = 0x02 // CMD_Display_Control 23 | OPT_Enable_Blink = 0x01 // CMD_Display_Control 24 | OPT_Display_Shift = 0x08 // CMD_Cursor_Display_Shift 25 | OPT_Shift_Right = 0x04 // CMD_Cursor_Display_Shift 0 = Left 26 | OPT_2_Lines = 0x08 // CMD_Function_Set 0 = 1 line 27 | OPT_5x10_Dots = 0x04 // CMD_Function_Set 0 = 5x7 dots 28 | ) 29 | 30 | type Lcd struct { 31 | i2c *I2C 32 | en, rw, rs, d4, d5, d6, d7, backlight byte 33 | backlight_state bool 34 | } 35 | 36 | func NewLcd(i2c *I2C, en, rw, rs, d4, d5, d6, d7, backlight byte) (*Lcd, error) { 37 | lcd := Lcd{ 38 | i2c: i2c, 39 | en: en, 40 | rw: rw, 41 | rs: rs, 42 | d4: d4, 43 | d5: d5, 44 | d6: d6, 45 | d7: d7, 46 | backlight: backlight, 47 | } 48 | // Activate LCD 49 | var data byte 50 | data = pinInterpret(lcd.d4, data, true) 51 | data = pinInterpret(lcd.d5, data, true) 52 | lcd.enable(data) 53 | time.Sleep(200 * time.Millisecond) 54 | lcd.enable(data) 55 | time.Sleep(100 * time.Millisecond) 56 | lcd.enable(data) 57 | time.Sleep(100 * time.Millisecond) 58 | 59 | // Initialize 4-bit mode 60 | data = pinInterpret(lcd.d4, data, false) 61 | lcd.enable(data) 62 | time.Sleep(10 * time.Millisecond) 63 | 64 | lcd.command(CMD_Function_Set | OPT_2_Lines) 65 | lcd.command(CMD_Display_Control | OPT_Enable_Display | OPT_Enable_Cursor) 66 | lcd.command(CMD_Clear_Display) 67 | lcd.command(CMD_Entry_Mode | OPT_Increment | OPT_Display_Shift) 68 | 69 | return &lcd, nil 70 | } 71 | 72 | func (lcd *Lcd) enable(data byte) { 73 | // Determine if black light is on and insure it does not turn off or on 74 | if lcd.backlight_state { 75 | data = pinInterpret(lcd.backlight, data, true) 76 | } else { 77 | data = pinInterpret(lcd.backlight, data, false) 78 | } 79 | lcd.i2c.WriteByte(data) 80 | lcd.i2c.WriteByte(pinInterpret(lcd.en, data, true)) 81 | lcd.i2c.WriteByte(data) 82 | } 83 | 84 | func (lcd *Lcd) command(data byte) { 85 | lcd.write(data, true) 86 | } 87 | 88 | func (lcd *Lcd) write(data byte, command bool) { 89 | var i2c_data byte 90 | 91 | // Add data for high nibble 92 | hi_nibble := data >> 4 93 | i2c_data = pinInterpret(lcd.d4, i2c_data, (hi_nibble&0x01 == 0x01)) 94 | i2c_data = pinInterpret(lcd.d5, i2c_data, ((hi_nibble>>1)&0x01 == 0x01)) 95 | i2c_data = pinInterpret(lcd.d6, i2c_data, ((hi_nibble>>2)&0x01 == 0x01)) 96 | i2c_data = pinInterpret(lcd.d7, i2c_data, ((hi_nibble>>3)&0x01 == 0x01)) 97 | 98 | // # Set the register selector to 1 if this is data 99 | if !command { 100 | i2c_data = pinInterpret(lcd.rs, i2c_data, true) 101 | } 102 | 103 | // Toggle Enable 104 | lcd.enable(i2c_data) 105 | 106 | i2c_data = 0x00 107 | 108 | // Add data for high nibble 109 | low_nibble := data & 0x0F 110 | i2c_data = pinInterpret(lcd.d4, i2c_data, (low_nibble&0x01 == 0x01)) 111 | i2c_data = pinInterpret(lcd.d5, i2c_data, ((low_nibble>>1)&0x01 == 0x01)) 112 | i2c_data = pinInterpret(lcd.d6, i2c_data, ((low_nibble>>2)&0x01 == 0x01)) 113 | i2c_data = pinInterpret(lcd.d7, i2c_data, ((low_nibble>>3)&0x01 == 0x01)) 114 | 115 | // Set the register selector to 1 if this is data 116 | if !command { 117 | i2c_data = pinInterpret(lcd.rs, i2c_data, true) 118 | } 119 | 120 | lcd.enable(i2c_data) 121 | } 122 | 123 | func (lcd *Lcd) BacklightOn() { 124 | lcd.i2c.WriteByte(pinInterpret(lcd.backlight, 0x00, true)) 125 | lcd.backlight_state = true 126 | } 127 | 128 | func (lcd *Lcd) BacklightOff() { 129 | lcd.i2c.WriteByte(pinInterpret(lcd.backlight, 0x00, false)) 130 | lcd.backlight_state = false 131 | } 132 | 133 | func (lcd *Lcd) Clear() { 134 | lcd.command(CMD_Clear_Display) 135 | } 136 | 137 | func (lcd *Lcd) Home() { 138 | lcd.command(CMD_Return_Home) 139 | } 140 | 141 | func (lcd *Lcd) SetPosition(line, pos byte) { 142 | var address byte 143 | switch line { 144 | case 1: 145 | address = pos 146 | case 2: 147 | address = 0x40 + pos 148 | case 3: 149 | address = 0x10 + pos 150 | case 4: 151 | address = 0x50 + pos 152 | } 153 | lcd.command(CMD_DDRAM_Set + address) 154 | } 155 | 156 | func (lcd *Lcd) Command(cmd byte) { 157 | lcd.command(cmd) 158 | } 159 | 160 | func (lcd *Lcd) writeChar(char byte) { 161 | lcd.write(char, false) 162 | } 163 | 164 | func (lcd *Lcd) Write(buf []byte) (int, error) { 165 | for _, c := range buf { 166 | lcd.writeChar(c) 167 | } 168 | return len(buf), nil 169 | } 170 | 171 | func pinInterpret(pin, data byte, value bool) byte { 172 | if value { 173 | // Construct mask using pin 174 | var mask byte = 0x01 << (pin) 175 | data = data | mask 176 | } else { 177 | // Construct mask using pin 178 | var mask byte = 0x01<<(pin) ^ 0xFF 179 | data = data & mask 180 | } 181 | return data 182 | } 183 | --------------------------------------------------------------------------------