diff --git a/.circleci/config.yml b/.circleci/config.yml index 3586b53..f61cf00 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,6 +28,7 @@ jobs: - run: tinygo build -size short -o test.elf -target=microbit ./examples/hub75/main.go - run: tinygo build -size short -o test.elf -target=circuitplay-express ./examples/lis3dh/main.go - run: tinygo build -size short -o test.elf -target=itsybitsy-m0 ./examples/mag3110/main.go + - run: tinygo build -size short -o test.elf -target=microbit ./examples/microbitmatrix/main.go - run: tinygo build -size short -o test.elf -target=itsybitsy-m0 ./examples/mma8653/main.go - run: tinygo build -size short -o test.elf -target=itsybitsy-m0 ./examples/mpu6050/main.go - run: tinygo build -size short -o test.elf -target=microbit ./examples/pcd8544/setbuffer/main.go diff --git a/README.md b/README.md index 93a8699..8784932 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ func main() { | [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI | | [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C | | [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C | +| [BBC micro:bit LED matrix](https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf) | GPIO | | [MMA8653 accelerometer](https://www.nxp.com/docs/en/data-sheet/MMA8653FC.pdf) | I2C | | [MPU6050 accelerometer/gyroscope](https://store.invensense.com/datasheets/invensense/MPU-6050_DataSheet_V3%204.pdf) | I2C | | [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI | diff --git a/examples/microbitmatrix/main.go b/examples/microbitmatrix/main.go new file mode 100644 index 0000000..5dc5767 --- /dev/null +++ b/examples/microbitmatrix/main.go @@ -0,0 +1,59 @@ +package main + +import ( + "image/color" + "math/rand" + "time" + + "github.com/tinygo-org/drivers/microbitmatrix" +) + +var display microbitmatrix.Device + +func main() { + display = microbitmatrix.New() + display.Configure(microbitmatrix.Config{}) + + display.ClearDisplay() + + x := int16(1) + y := int16(2) + deltaX := int16(1) + deltaY := int16(1) + then := time.Now() + c := color.RGBA{255, 255, 255, 255} + + for { + if time.Since(then).Nanoseconds() > 80000000 { + then = time.Now() + + pixel := display.GetPixel(x, y) + if pixel { + display.ClearDisplay() + x = 1 + int16(rand.Int31n(3)) + y = 1 + int16(rand.Int31n(3)) + deltaX = 1 + deltaY = 1 + if rand.Int31n(2) == 0 { + deltaX = -1 + } + if rand.Int31n(2) == 0 { + deltaY = -1 + } + } + display.SetPixel(x, y, c) + + x += deltaX + y += deltaY + + if x == 0 || x == 4 { + deltaX = -deltaX + } + + if y == 0 || y == 4 { + deltaY = -deltaY + } + } + display.Display() + } +} diff --git a/microbitmatrix/microbitmatrix.go b/microbitmatrix/microbitmatrix.go new file mode 100644 index 0000000..c771627 --- /dev/null +++ b/microbitmatrix/microbitmatrix.go @@ -0,0 +1,157 @@ +// Package microbitmatrix implements a driver for the BBC micro:bit's LED matrix. +// +// Schematic: https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf +package microbitmatrix + +import ( + "device/nrf" + "image/color" + "machine" + "time" +) + +var matrixRotations = [4][5][5][2]uint8{ + { // 0 + {{0, 0}, {1, 3}, {0, 1}, {1, 4}, {0, 2}}, + {{2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7}}, + {{1, 1}, {0, 8}, {1, 2}, {2, 8}, {1, 0}}, + {{0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}}, + {{2, 2}, {1, 6}, {2, 0}, {1, 5}, {2, 1}}, + }, + { // 90 CCW + {{0, 2}, {2, 7}, {1, 0}, {0, 3}, {2, 1}}, + {{1, 4}, {2, 6}, {2, 8}, {0, 4}, {1, 5}}, + {{0, 1}, {2, 5}, {1, 2}, {0, 5}, {2, 0}}, + {{1, 3}, {2, 4}, {0, 8}, {0, 6}, {1, 6}}, + {{0, 0}, {2, 3}, {1, 1}, {0, 7}, {2, 2}}, + }, + { // 180 + {{2, 1}, {1, 5}, {2, 0}, {1, 6}, {2, 2}}, + {{0, 3}, {0, 4}, {0, 5}, {0, 6}, {0, 7}}, + {{1, 0}, {2, 8}, {1, 2}, {0, 8}, {1, 1}}, + {{2, 7}, {2, 6}, {2, 5}, {2, 4}, {2, 3}}, + {{0, 2}, {1, 4}, {0, 1}, {1, 3}, {0, 0}}, + }, + { // 270 + {{2, 2}, {0, 7}, {1, 1}, {2, 3}, {0, 0}}, + {{1, 6}, {0, 6}, {0, 8}, {2, 4}, {1, 3}}, + {{2, 0}, {0, 5}, {1, 2}, {2, 5}, {0, 1}}, + {{1, 5}, {0, 4}, {2, 8}, {2, 6}, {1, 4}}, + {{2, 1}, {0, 3}, {1, 0}, {2, 7}, {0, 2}}, + }, +} + +type Config struct { + Rotation uint8 +} + +type Device struct { + buffer [3][9]bool + rotation uint8 +} + +// New returns a new microbitmatrix driver. +func New() Device { + return Device{} +} + +// Configure sets up the device. +func (d *Device) Configure(cfg Config) { + d.SetRotation(cfg.Rotation) + + set := 0 + for i := machine.LED_COL_1; i <= machine.LED_ROW_3; i++ { + set |= 1 << uint8(i) + } + nrf.GPIO.DIRSET = nrf.RegValue(set) + d.ClearDisplay() + d.DisableAll() +} + +// SetRotation changes the rotation of the LED matrix +func (d *Device) SetRotation(rotation uint8) { + d.rotation = rotation % 4 +} + +// SetPixel modifies the internal buffer in a single pixel. +func (d *Device) SetPixel(x int16, y int16, c color.RGBA) { + if x < 0 || x >= 5 || y < 0 || y >= 5 { + return + } + if c.R != 0 || c.G != 0 || c.B != 0 { + d.buffer[matrixRotations[d.rotation][x][y][0]][matrixRotations[d.rotation][x][y][1]] = true + } else { + d.buffer[matrixRotations[d.rotation][x][y][0]][matrixRotations[d.rotation][x][y][1]] = false + } +} + +// GetPixel returns if the specific pixels is enabled +func (d *Device) GetPixel(x int16, y int16) bool { + if x < 0 || x >= 5 || y < 0 || y >= 5 { + return false + } + return d.buffer[matrixRotations[d.rotation][x][y][0]][matrixRotations[d.rotation][x][y][1]] +} + +// Display sends the buffer (if any) to the screen. +func (d *Device) Display() error { + for row := 0; row < 3; row++ { + d.DisableAll() + set := 0 + set |= 1 << uint8(machine.LED_ROW_1+row) + nrf.GPIO.OUTSET = nrf.RegValue(set) + + set = 0 + for col := 0; col < 9; col++ { + + if d.buffer[row][col] { + set |= 1 << uint8(machine.LED_COL_1+col) + } + + } + nrf.GPIO.OUTCLR = nrf.RegValue(set) + time.Sleep(time.Millisecond * 2) + } + return nil +} + +// ClearDisplay erases the internal buffer +func (d *Device) ClearDisplay() { + for row := 0; row < 3; row++ { + for col := 0; col < 9; col++ { + d.buffer[row][col] = false + } + } +} + +// DisableAll disables all the LEDs without modifying the buffer +func (d *Device) DisableAll() { + set := 0 + for i := machine.LED_COL_1; i <= machine.LED_COL_9; i++ { + set |= 1 << uint8(i) + } + nrf.GPIO.OUTSET = nrf.RegValue(set) + nrf.GPIO.OUTCLR = (1 << machine.LED_ROW_1) | (1 << machine.LED_ROW_2) | (1 << machine.LED_ROW_3) +} + +// EnableAll enables all the LEDs without modifying the buffer +func EnableAll() error { + set := 0 + for i := machine.LED_ROW_1; i <= machine.LED_ROW_3; i++ { + set |= 1 << uint8(i) + } + nrf.GPIO.OUTSET = nrf.RegValue(set) + + set = 0 + for i := machine.LED_COL_1; i <= machine.LED_COL_9; i++ { + set |= 1 << uint8(i) + } + nrf.GPIO.OUTCLR = nrf.RegValue(set) + + return nil +} + +// Size returns the current size of the display. +func (d *Device) Size() (w, h int16) { + return 5, 5 +}