Made column differential work for SSD1306 (#444)

pull/1/head
Denis Luchkin-Zhou 6 years ago committed by GitHub
parent 0fbde4244b
commit 8fe00338c0

@ -25,6 +25,36 @@ import (
"periph.io/x/periph/devices/ssd1306/image1bit" "periph.io/x/periph/devices/ssd1306/image1bit"
) )
const (
_CHARGEPUMP = 0x8D
_COLUMNADDR = 0x21
_COMSCANDEC = 0xC8
_COMSCANINC = 0xC0
_DISPLAYALLON = 0xA5
_DISPLAYALLON_RESUME = 0xA4
_DISPLAYOFF = 0xAE
_DISPLAYON = 0xAF
_EXTERNALVCC = 0x1
_INVERTDISPLAY = 0xA7
_MEMORYMODE = 0x20
_NORMALDISPLAY = 0xA6
_PAGEADDR = 0x22
_PAGESTARTADDRESS = 0xB0
_SEGREMAP = 0xA0
_SETCOMPINS = 0xDA
_SETCONTRAST = 0x81
_SETDISPLAYCLOCKDIV = 0xD5
_SETDISPLAYOFFSET = 0xD3
_SETHIGHCOLUMN = 0x10
_SETLOWCOLUMN = 0x00
_SETMULTIPLEX = 0xA8
_SETPRECHARGE = 0xD9
_SETSEGMENTREMAP = 0xA1
_SETSTARTLINE = 0x40
_SETVCOMDETECT = 0xDB
_SWITCHCAPVCC = 0x2
)
// FrameRate determines scrolling speed. // FrameRate determines scrolling speed.
type FrameRate byte type FrameRate byte
@ -387,9 +417,7 @@ func (d *Dev) calculateSubset(next []byte) (int, int, int, int, bool) {
// Early exit, the image is exactly the same. // Early exit, the image is exactly the same.
return 0, 0, 0, 0, true return 0, 0, 0, 0, true
} }
// TODO(maruel): This currently corrupts the screen. Likely a small error
// in the way the commands are sent.
/*
// Left. // Left.
for ; startCol < endCol; startCol++ { for ; startCol < endCol; startCol++ {
for i := startPage; i < endPage; i++ { for i := startPage; i < endPage; i++ {
@ -400,6 +428,7 @@ func (d *Dev) calculateSubset(next []byte) (int, int, int, int, bool) {
} }
} }
breakLeft: breakLeft:
// Right. // Right.
for ; endCol > startCol; endCol-- { for ; endCol > startCol; endCol-- {
for i := startPage; i < endPage; i++ { for i := startPage; i < endPage; i++ {
@ -410,7 +439,6 @@ func (d *Dev) calculateSubset(next []byte) (int, int, int, int, bool) {
} }
} }
breakRight: breakRight:
*/
} }
return startPage, endPage, startCol, endCol, false return startPage, endPage, startCol, endCol, false
} }
@ -428,18 +456,25 @@ func (d *Dev) drawInternal(next []byte) error {
d.endPage = endPage d.endPage = endPage
d.startCol = startCol d.startCol = startCol
d.endCol = endCol d.endCol = endCol
cmd := []byte{
0x21, uint8(d.startCol), uint8(d.endCol - 1), // Set column address (Width)
0x22, uint8(d.startPage), uint8(d.endPage - 1), // Set page address (Pages)
} }
if err := d.sendCommand(cmd); err != nil {
pageSize := d.rect.Dx()
for page := d.startPage; page < d.endPage; page++ {
err := d.sendCommand([]byte{
_PAGESTARTADDRESS | byte(page),
_SETLOWCOLUMN | (byte(d.startCol) & 0x0F),
_SETHIGHCOLUMN | (byte(d.startCol) >> 4),
})
if err != nil {
return err
}
pageStart := page * pageSize
err = d.sendData(d.buffer[pageStart+d.startCol : pageStart+d.endCol])
if err != nil {
return err return err
} }
} }
return nil
// Write the subset of the data as needed.
pageSize := d.rect.Dx()
return d.sendData(d.buffer[startPage*pageSize+startCol : (endPage-1)*pageSize+endCol])
} }
func (d *Dev) sendData(c []byte) error { func (d *Dev) sendData(c []byte) error {

@ -68,15 +68,42 @@ func TestI2C_String(t *testing.T) {
func TestI2C_Draw_VerticalLSD_fast(t *testing.T) { func TestI2C_Draw_VerticalLSD_fast(t *testing.T) {
// Exercise the fast path. // Exercise the fast path.
buf := make([]byte, 1025) buf := make([]byte, 129)
buf[0] = i2cData buf[0] = i2cData
buf[23] = 1 buf[23] = 1
emptyBuf := make([]byte, 129)
emptyBuf[0] = i2cData
bus := i2ctest.Playback{ bus := i2ctest.Playback{
Ops: []i2ctest.IO{ Ops: []i2ctest.IO{
// Startup initialization. // Startup initialization.
{Addr: 0x3c, W: initCmdI2C()}, {Addr: 0x3c, W: initCmdI2C()},
// Actual draw buffer.
// Page 1
{Addr: 0x3c, W: []byte{0x00, 0xB0, 0x00, 0x10}},
{Addr: 0x3c, W: buf}, {Addr: 0x3c, W: buf},
// Page 2
{Addr: 0x3c, W: []byte{0x00, 0xB1, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 3
{Addr: 0x3c, W: []byte{0x00, 0xB2, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 4
{Addr: 0x3c, W: []byte{0x00, 0xB3, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 5
{Addr: 0x3c, W: []byte{0x00, 0xB4, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 6
{Addr: 0x3c, W: []byte{0x00, 0xB5, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 7
{Addr: 0x3c, W: []byte{0x00, 0xB6, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 8
{Addr: 0x3c, W: []byte{0x00, 0xB7, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
}, },
} }
dev, err := NewI2C(&bus, &DefaultOpts) dev, err := NewI2C(&bus, &DefaultOpts)
@ -95,19 +122,43 @@ func TestI2C_Draw_VerticalLSD_fast(t *testing.T) {
func TestI2C_Halt_Write(t *testing.T) { func TestI2C_Halt_Write(t *testing.T) {
// Exercise the fast path. // Exercise the fast path.
buf := make([]byte, 1025) buf := make([]byte, 129)
buf[0] = i2cData buf[0] = i2cData
buf[23] = 1 buf[23] = 1
emptyBuf := make([]byte, 129)
emptyBuf[0] = i2cData
bus := i2ctest.Playback{ bus := i2ctest.Playback{
Ops: []i2ctest.IO{ Ops: []i2ctest.IO{
// Startup initialization. // Startup initialization.
{Addr: 0x3c, W: initCmdI2C()}, {Addr: 0x3c, W: initCmdI2C()},
// Halt() // Halt()
{Addr: 0x3c, W: []byte{0x0, 0xae}}, {Addr: 0x3c, W: []byte{0x0, 0xae}},
// transparent resume // transparent resume & page 1 setup
{Addr: 0x3c, W: []byte{0x0, 0xaf}}, {Addr: 0x3c, W: []byte{0x0, 0xaf, 0xB0, 0x00, 0x10}},
// Actual draw buffer. // Page 1
{Addr: 0x3c, W: buf}, {Addr: 0x3c, W: buf},
// Page 2
{Addr: 0x3c, W: []byte{0x00, 0xB1, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 3
{Addr: 0x3c, W: []byte{0x00, 0xB2, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 4
{Addr: 0x3c, W: []byte{0x00, 0xB3, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 5
{Addr: 0x3c, W: []byte{0x00, 0xB4, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 6
{Addr: 0x3c, W: []byte{0x00, 0xB5, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 7
{Addr: 0x3c, W: []byte{0x00, 0xB6, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
// Page 8
{Addr: 0x3c, W: []byte{0x00, 0xB7, 0x00, 0x10}},
{Addr: 0x3c, W: emptyBuf},
}, },
} }
dev, err := NewI2C(&bus, &DefaultOpts) dev, err := NewI2C(&bus, &DefaultOpts)
@ -212,12 +263,36 @@ func TestI2C_Draw_fail(t *testing.T) {
} }
func TestI2C_DrawGray(t *testing.T) { func TestI2C_DrawGray(t *testing.T) {
buf := append([]byte{i2cData}, grayCheckboard()...)
bus := i2ctest.Playback{ bus := i2ctest.Playback{
Ops: []i2ctest.IO{ Ops: []i2ctest.IO{
// Startup initialization. // Startup initialization.
{Addr: 0x3c, W: initCmdI2C()}, {Addr: 0x3c, W: initCmdI2C()},
// Actual draw buffer. // Page 1
{Addr: 0x3c, W: append([]byte{i2cData}, grayCheckboard()...)}, {Addr: 0x3c, W: []byte{0x00, 0xB0, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 2
{Addr: 0x3c, W: []byte{0x00, 0xB1, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 3
{Addr: 0x3c, W: []byte{0x00, 0xB2, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 4
{Addr: 0x3c, W: []byte{0x00, 0xB3, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 5
{Addr: 0x3c, W: []byte{0x00, 0xB4, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 6
{Addr: 0x3c, W: []byte{0x00, 0xB5, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 7
{Addr: 0x3c, W: []byte{0x00, 0xB6, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
// Page 8
{Addr: 0x3c, W: []byte{0x00, 0xB7, 0x00, 0x10}},
{Addr: 0x3c, W: buf},
}, },
} }
dev, err := NewI2C(&bus, &DefaultOpts) dev, err := NewI2C(&bus, &DefaultOpts)
@ -402,8 +477,8 @@ func TestSPI_4wire_String(t *testing.T) {
} }
func TestSPI_4wire_Write_differential(t *testing.T) { func TestSPI_4wire_Write_differential(t *testing.T) {
buf1 := make([]byte, 1024) buf1 := make([]byte, 128)
buf1[130] = 1 buf1[29] = 1
buf2 := make([]byte, 128) buf2 := make([]byte, 128)
buf2[130-128] = 1 buf2[130-128] = 1
buf2[131-128] = 2 buf2[131-128] = 2
@ -411,10 +486,35 @@ func TestSPI_4wire_Write_differential(t *testing.T) {
Playback: conntest.Playback{ Playback: conntest.Playback{
Ops: []conntest.IO{ Ops: []conntest.IO{
{W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})}, {W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})},
// Page 1
{W: []byte{0xB0, 0x00, 0x10}},
{W: buf1}, {W: buf1},
// Reset to write only to the first page. // Page 2
{W: []byte{0x21, 0x0, 0x7f, 0x22, 0x1, 0x1}}, {W: []byte{0xB1, 0x00, 0x10}},
{W: buf2}, {W: make([]byte, 128)},
// Page 3
{W: []byte{0xB2, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 4
{W: []byte{0xB3, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 5
{W: []byte{0xB4, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 6
{W: []byte{0xB5, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 7
{W: []byte{0xB6, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 8
{W: []byte{0xB7, 0x00, 0x10}},
{W: make([]byte, 128)},
// Only write to column 3 of page 1
{W: []byte{0xB1, 0x03, 0x10}},
{W: []byte{0x02}},
}, },
}, },
} }
@ -423,7 +523,7 @@ func TestSPI_4wire_Write_differential(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
pix := make([]byte, 1024) pix := make([]byte, 1024)
pix[130] = 1 pix[29] = 1
if n, err := dev.Write(pix); n != len(pix) || err != nil { if n, err := dev.Write(pix); n != len(pix) || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
} }
@ -437,13 +537,36 @@ func TestSPI_4wire_Write_differential(t *testing.T) {
} }
func TestSPI_4wire_Write_differential_fail(t *testing.T) { func TestSPI_4wire_Write_differential_fail(t *testing.T) {
buf1 := make([]byte, 1024) buf1 := make([]byte, 128)
buf1[130] = 1 buf1[29] = 1
port := spitest.Playback{ port := spitest.Playback{
Playback: conntest.Playback{ Playback: conntest.Playback{
Ops: []conntest.IO{ Ops: []conntest.IO{
{W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})}, {W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})},
// Page 1
{W: []byte{0xB0, 0x00, 0x10}},
{W: buf1}, {W: buf1},
// Page 2
{W: []byte{0xB1, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 3
{W: []byte{0xB2, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 4
{W: []byte{0xB3, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 5
{W: []byte{0xB4, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 6
{W: []byte{0xB5, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 7
{W: []byte{0xB6, 0x00, 0x10}},
{W: make([]byte, 128)},
// Page 8
{W: []byte{0xB7, 0x00, 0x10}},
{W: make([]byte, 128)},
}, },
DontPanic: true, DontPanic: true,
}, },
@ -453,11 +576,11 @@ func TestSPI_4wire_Write_differential_fail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
pix := make([]byte, 1024) pix := make([]byte, 1024)
pix[130] = 1 pix[29] = 1
if n, err := dev.Write(pix); n != len(pix) || err != nil { if n, err := dev.Write(pix); n != len(pix) || err != nil {
t.Fatal(n, err) t.Fatal(n, err)
} }
pix[131] = 2 pix[29] = 2
if n, err := dev.Write(pix); n != 0 || !conntest.IsErr(err) { if n, err := dev.Write(pix); n != 0 || !conntest.IsErr(err) {
t.Fatalf("expected conntest error: %v", err) t.Fatalf("expected conntest error: %v", err)
} }
@ -529,7 +652,7 @@ func getI2CPlayback() *i2ctest.Playback {
} }
func grayCheckboard() []byte { func grayCheckboard() []byte {
buf := make([]byte, 1024) buf := make([]byte, 128)
for i := range buf { for i := range buf {
if i&1 == 0 { if i&1 == 0 {
buf[i] = 0xaa buf[i] = 0xaa

Loading…
Cancel
Save