Compare commits

...

14 Commits

Author SHA1 Message Date
Ron Evans 850df0a25c release: update for version 0.10.0
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-31 18:43:12 +01:00
Ron Evans 5f4806f0f9 ws2812: work-arounds to allow Digispark to control WS2812 LEDs
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-30 14:24:39 +01:00
Daniel Esteban c8e62562b5 added scroll functionality to st7735 2020-01-30 10:20:45 +01:00
Ron Evans 6842bdb424 docs: update README to include list of all 44 drivers
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-28 19:27:24 +01:00
BCG d43263f764 Adding driver for four-wire resistive touchscreen (#118)
* resistive: Adding driver for four-wire resistive touchscreen, as used on the Adafruit PyPortal.
2020-01-28 18:55:31 +01:00
BCG 6716bb6c0a ILI9341 TFT driver (#115)
* ILI9341: TFT display implementation
2020-01-07 20:11:46 +01:00
BCG f4bccd1fed Added nrf52840 tag to ws2812 2020-01-06 07:37:21 +01:00
Ron Evans d5aa295b76 l9110x: add support for L9110x h-bridge motor driver
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-03 10:37:30 +01:00
Ron Evans c6e8af3057 l293x: added support for h-bridge motor controller
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2020-01-03 10:05:23 +01:00
Ron Evans 38076352eb wifinina: update docs and add Dockerfile to build firmware
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-29 17:45:41 +01:00
Ron Evans 086415605e wifinina: update docs and info on how to install WiFiNINA driver
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-26 16:34:35 +01:00
BCG 1d0f04af6b Support for ADT7410 temperature sensor (#109)
* ADT7410: add support for i2c temperature sensor
2019-12-26 13:53:06 +01:00
Daniel Esteban dc883d913d remove stxx defaults offsets 2019-12-24 17:36:20 +01:00
Ron Evans 3bb5b4519b docs: correct driver count in README
Signed-off-by: Ron Evans <ron@hybridgroup.com>
2019-12-23 17:32:07 +01:00
36 changed files with 3304 additions and 36 deletions
+19
View File
@@ -1,3 +1,22 @@
0.10.0
---
- **new devices**
- adt7410: Support for ADT7410 temperature sensor (#109)
- ili9341: ILI9341 TFT driver (#115)
- l293x: added support for h-bridge motor controller
- l9110x: add support for L9110x h-bridge motor driver
- resistive: Adding driver for four-wire resistive touchscreen (#118)
- **enhancements**
- st7735: added scroll functionality to st7735
- st7735: remove default offsets
- st7789: remove default offsets
- ws2812: Added nrf52840 tag to ws2812
- ws2812: work-arounds to allow Digispark to control WS2812 LEDs
- **docs**
- readme: update README to include list of all 44 drivers
- wifinina: update docs and add Dockerfile to build firmware
- wifinina: update docs and info on how to install WiFiNINA driver
0.9.0
---
- **new devices**
+19 -1
View File
@@ -9,6 +9,8 @@ fmt-check:
smoke-test:
@mkdir -p build
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
@@ -47,6 +49,8 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/hub75/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/ili9341/basic/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis3dh/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/lsm6ds3/main.go
@@ -81,6 +85,10 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/thermistor/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/fourwire/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/touch/resistive/pyportal_touchpaint/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/vl53l1x/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/waveshare-epd/epd2in13/main.go
@@ -91,7 +99,9 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/wifinina/webclient/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812/main.go
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/ws2812
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=digispark ./examples/ws2812
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bme280/main.go
@md5sum ./build/test.hex
@@ -101,5 +111,13 @@ smoke-test:
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/veml6070/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/simple/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l293x/speed/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/simple/main.go
@md5sum ./build/test.hex
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/l9110x/speed/main.go
@md5sum ./build/test.hex
test: clean fmt-check smoke-test
+8 -1
View File
@@ -52,10 +52,11 @@ func main() {
## Currently supported devices
The following 34 devices are supported.
The following 44 devices are supported.
| Device Name | Interface Type |
|----------|-------------|
| [ADT7410 I2C Temperature Sensor](https://www.analog.com/media/en/technical-documentation/data-sheets/ADT7410.pdf) | I2C |
| [ADXL345 accelerometer](http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf) | I2C |
| [APA102 RGB LED](https://cdn-shop.adafruit.com/product-files/2343/APA102C.pdf) | SPI |
| [AT24CX 2-wire serial EEPROM](https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf) | I2C |
@@ -70,7 +71,11 @@ The following 34 devices are supported.
| [ESP32 as WiFi Coprocessor with Arduino nina-fw](https://github.com/arduino/nina-fw) | SPI |
| [ESP8266/ESP32 AT Command set for WiFi/TCP/UDP](https://github.com/espressif/esp32-at) | UART |
| [GPS module](https://www.u-blox.com/en/product/neo-6-series) | I2C/UART |
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO |
| [HUB75 RGB led matrix](https://cdn-learn.adafruit.com/downloads/pdf/32x16-32x32-rgb-led-matrix.pdf) | SPI |
| [ILI9341 TFT color display](https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf) | SPI |
| [L293x motor driver](https://www.ti.com/lit/ds/symlink/l293d.pdf) | GPIO/PWM |
| [L9110x motor driver](https://www.elecrow.com/download/datasheet-l9110.pdf) | GPIO/PWM |
| [LIS3DH accelerometer](https://www.st.com/resource/en/datasheet/lis3dh.pdf) | I2C |
| [LSM6DS3 accelerometer](https://www.st.com/resource/en/datasheet/lsm6ds3.pdf) | I2C |
| [MAG3110 magnetometer](https://www.nxp.com/docs/en/data-sheet/MAG3110.pdf) | I2C |
@@ -80,6 +85,8 @@ The following 34 devices are supported.
| [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 |
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
| [Shift register](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
| [SHT3x Digital Humidity Sensor](https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/0_Datasheets/Humidity/Sensirion_Humidity_Sensors_SHT3x_Datasheet_digital.pdf) | I2C |
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
+90
View File
@@ -0,0 +1,90 @@
package adt7410
import (
"machine"
"time"
)
type Error uint8
const (
ErrInvalidID Error = 0x1
)
func (e Error) Error() string {
switch e {
case ErrInvalidID:
return "Invalid chip ID"
default:
return "Unknown error"
}
}
type Device struct {
bus *machine.I2C
buf []byte
addr uint8
}
// New returns ADT7410 device for the provided I2C bus and address. The ADT7410
// has a default address of 0x48 (1001000). The last 2 bits of the address
// can be set using by connecting to the A1 and A0 pins to VDD or GND (for a
// total of up to 4 devices on a I2C bus). Also note that 10k pullups are
// recommended for the SDA and SCL lines.
func New(i2c *machine.I2C, addressBits uint8) *Device {
return &Device{
bus: i2c,
buf: make([]byte, 2),
addr: Address | (addressBits & 0x3),
}
}
func (dev *Device) Configure() (err error) {
// verify the chip ID
// TODO: According to datasheet, the check below should work; however
// this does not seem to be working right, but is not exactly
// necessary, so can revisit later to see if there is a bug
//id := dev.ReadByte(RegID) & 0xF8
//if id != 0xC8 {
// err = ErrInvalidID
//}
// reset the chip
dev.writeByte(RegReset, 0xFF)
time.Sleep(10 * time.Millisecond)
return
}
// ReadTemperature returns the temperature in celsius milli degrees (ºC/1000)
func (d *Device) ReadTemperature() (temperature int32, err error) {
return (int32(d.readUint16(RegTempValueMSB)) * 1000) / 128, nil
}
// ReadTempC returns the value in the temperature value register, in Celcius
func (d *Device) ReadTempC() float32 {
t := d.readUint16(RegTempValueMSB)
return float32(int(t)) / 128.0
}
// ReadTempF returns the value in the temperature value register, in Fahrenheit
func (d *Device) ReadTempF() float32 {
return d.ReadTempC()*1.8 + 32.0
}
func (d *Device) writeByte(reg uint8, data byte) {
d.buf[0] = reg
d.buf[1] = data
d.bus.Tx(uint16(d.addr), d.buf, nil)
}
func (d *Device) readByte(reg uint8) byte {
d.bus.ReadRegister(d.addr, reg, d.buf)
return d.buf[0]
}
func (d *Device) readUint16(reg uint8) uint16 {
d.bus.ReadRegister(d.addr, reg, d.buf)
return uint16(d.buf[0])<<8 | uint16(d.buf[1])
}
+24
View File
@@ -0,0 +1,24 @@
package adt7410
const (
// Default I2C address
Address = 0x48
// Temperature Value MSB Register
RegTempValueMSB = 0x0
// Temperature Value LSB Register
RegTempValueLSB = 0x1
// Status Register
RegStatus = 0x2
// Config Register
RegConfig = 0x3
// ID Register
RegID = 0x0B
// Software Reset Register
RegReset = 0x2F
)
+27
View File
@@ -0,0 +1,27 @@
package main
import (
"fmt"
"machine"
"time"
"tinygo.org/x/drivers/adt7410"
)
var (
i2c = &machine.I2C0
sensor = adt7410.New(i2c, 0)
)
func main() {
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
sensor.Configure()
for {
temp := sensor.ReadTempF()
fmt.Printf("temperature: %f\r\n", temp)
time.Sleep(time.Second)
}
}
+47
View File
@@ -0,0 +1,47 @@
package main
import (
"image/color"
"machine"
"time"
"tinygo.org/x/drivers/ili9341"
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
black = color.RGBA{0, 0, 0, 255}
white = color.RGBA{255, 255, 255, 255}
red = color.RGBA{255, 0, 0, 255}
blue = color.RGBA{0, 0, 255, 255}
green = color.RGBA{0, 255, 0, 255}
)
func main() {
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
display.Configure(ili9341.Config{})
width, height := display.Size()
display.FillScreen(black)
machine.TFT_BACKLIGHT.High()
display.FillRectangle(0, 0, width/2, height/2, white)
display.FillRectangle(width/2, 0, width/2, height/2, red)
display.FillRectangle(0, height/2, width/2, height/2, green)
display.FillRectangle(width/2, height/2, width/2, height/2, blue)
display.FillRectangle(width/4, height/4, width/2, height/2, black)
for {
time.Sleep(time.Hour)
}
}
File diff suppressed because it is too large Load Diff
+234
View File
@@ -0,0 +1,234 @@
// Port of Adafruit's "pyportal_boing" demo found here:
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/examples/pyportal_boing
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/examples/ili9341/pyportal_boing/graphics"
"tinygo.org/x/drivers/ili9341"
)
const (
BGCOLOR = 0xAD75
GRIDCOLOR = 0xA815
BGSHADOW = 0x5285
GRIDSHADOW = 0x600C
RED = 0xF800
WHITE = 0xFFFF
YBOTTOM = 123 // Ball Y coord at bottom
YBOUNCE = -3.5 // Upward velocity on ball bounce
_debug = false
)
var (
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8)]uint16{}
startTime int64
frame int64
// Ball coordinates are stored floating-point because screen refresh
// is so quick, whole-pixel movements are just too fast!
ballx float32
bally float32
ballvx float32
ballvy float32
ballframe float32
balloldx float32
balloldy float32
// Color table for ball rotation effect
palette [16]uint16
)
func main() {
// configure backlight
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
// configure display
display.Configure(ili9341.Config{})
print("width, height == ")
width, height := display.Size()
println(width, height)
machine.TFT_BACKLIGHT.High()
display.SetRotation(ili9341.Rotation270)
DrawBackground()
startTime = time.Now().UnixNano()
frame = 0
ballx = 20.0
bally = YBOTTOM // Current ball position
ballvx = 0.8
ballvy = YBOUNCE // Ball velocity
ballframe = 3 // Ball animation frame #
balloldx = ballx
balloldy = bally // Prior ball position
for {
balloldx = ballx // Save prior position
balloldy = bally
ballx += ballvx // Update position
bally += ballvy
ballvy += 0.06 // Update Y velocity
if (ballx <= 15) || (ballx >= graphics.SCREENWIDTH-graphics.BALLWIDTH) {
ballvx *= -1 // Left/right bounce
}
if bally >= YBOTTOM { // Hit ground?
bally = YBOTTOM // Clip and
ballvy = YBOUNCE // bounce up
}
// Determine screen area to update. This is the bounds of the ball's
// prior and current positions, so the old ball is fully erased and new
// ball is fully drawn.
var minx, miny, maxx, maxy, width, height int16
// Determine bounds of prior and new positions
minx = int16(ballx)
if int16(balloldx) < minx {
minx = int16(balloldx)
}
miny = int16(bally)
if int16(balloldy) < miny {
miny = int16(balloldy)
}
maxx = int16(ballx + graphics.BALLWIDTH - 1)
if int16(balloldx+graphics.BALLWIDTH-1) > maxx {
maxx = int16(balloldx + graphics.BALLWIDTH - 1)
}
maxy = int16(bally + graphics.BALLHEIGHT - 1)
if int16(balloldy+graphics.BALLHEIGHT-1) > maxy {
maxy = int16(balloldy + graphics.BALLHEIGHT - 1)
}
width = maxx - minx + 1
height = maxy - miny + 1
// Ball animation frame # is incremented opposite the ball's X velocity
ballframe -= ballvx * 0.5
if ballframe < 0 {
ballframe += 14 // Constrain from 0 to 13
} else if ballframe >= 14 {
ballframe -= 14
}
// Set 7 palette entries to white, 7 to red, based on frame number.
// This makes the ball spin
for i := 0; i < 14; i++ {
if (int(ballframe)+i)%14 < 7 {
palette[i+2] = WHITE
} else {
palette[i+2] = RED
} // Palette entries 0 and 1 aren't used (clear and shadow, respectively)
}
// Only the changed rectangle is drawn into the 'renderbuf' array...
var c uint16 //, *destPtr;
bx := minx - int16(ballx) // X relative to ball bitmap (can be negative)
by := miny - int16(bally) // Y relative to ball bitmap (can be negative)
bgx := minx // X relative to background bitmap (>= 0)
bgy := miny // Y relative to background bitmap (>= 0)
var bx1, bgx1 int16 // Loop counters and working vars
var p uint8 // 'packed' value of 2 ball pixels
var bufIdx int8 = 0
//tft.setAddrWindow(minx, miny, width, height)
for y := 0; y < int(height); y++ { // For each row...
//destPtr = &renderbuf[bufIdx][0];
bx1 = bx // Need to keep the original bx and bgx values,
bgx1 = bgx // so copies of them are made here (and changed in loop below)
for x := 0; x < int(width); x++ {
var bgidx = int(bgy)*(graphics.SCREENWIDTH/8) + int(bgx1/8)
if (bx1 >= 0) && (bx1 < graphics.BALLWIDTH) && // Is current pixel row/column
(by >= 0) && (by < graphics.BALLHEIGHT) { // inside the ball bitmap area?
// Yes, do ball compositing math...
p = graphics.Ball[int(by*(graphics.BALLWIDTH/2))+int(bx1/2)] // Get packed value (2 pixels)
if (bx1 & 1) != 0 {
c = uint16(p & 0xF)
} else {
c = uint16(p >> 4)
} // Unpack high or low nybble
if c == 0 { // Outside ball - just draw grid
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDCOLOR
} else {
c = BGCOLOR
}
} else if c > 1 { // In ball area...
c = palette[c]
} else { // In shadow area...
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDSHADOW
} else {
c = BGSHADOW
}
}
} else { // Outside ball bitmap, just draw background bitmap...
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
c = GRIDCOLOR
} else {
c = BGCOLOR
}
}
frameBuffer[y*int(width)+x] = c
bx1++ // Increment bitmap position counters (X axis)
bgx1++
}
//tft.dmaWait(); // Wait for prior line to complete
//tft.writePixels(&renderbuf[bufIdx][0], width, false); // Non-blocking write
bufIdx = 1 - bufIdx
by++ // Increment bitmap position counters (Y axis)
bgy++
}
display.DrawRGBBitmap(minx, miny, frameBuffer[:width*height], width, height)
// Show approximate frame rate
frame++
if frame&255 == 0 { // Every 256 frames...
elapsed := (time.Now().UnixNano() - startTime) / int64(time.Second)
if elapsed > 0 {
println(frame/elapsed, " fps")
}
}
}
}
func DrawBackground() {
w, h := display.Size()
byteWidth := (w + 7) / 8 // Bitmap scanline pad = whole byte
var b uint8
for j := int16(0); j < h; j++ {
for k := int16(0); k < w; k++ {
if k&7 > 0 {
b <<= 1
} else {
b = graphics.Background[j*byteWidth+k/8]
}
if b&0x80 == 0 {
frameBuffer[k] = BGCOLOR
} else {
frameBuffer[k] = GRIDCOLOR
}
}
display.DrawRGBBitmap(0, j, frameBuffer[0:w], w, 1)
}
}
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/l293x"
)
func main() {
wheel := l293x.New(machine.D10, machine.D11, machine.D12)
wheel.Configure()
for i := 0; i <= 10; i++ {
println("Forward")
wheel.Forward()
time.Sleep(time.Millisecond * 1000)
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
println("Backward")
wheel.Backward()
time.Sleep(time.Millisecond * 1000)
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
}
println("Stop")
wheel.Stop()
}
+45
View File
@@ -0,0 +1,45 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/l293x"
)
const (
maxSpeed = 30000
)
func main() {
machine.InitPWM()
wheel := l293x.NewWithSpeed(machine.D10, machine.D11, machine.PWM{machine.D12})
wheel.Configure()
for i := 0; i <= 10; i++ {
println("Forward")
var i uint16
for i = 0; i < maxSpeed; i += 1000 {
wheel.Forward(i)
time.Sleep(time.Millisecond * 100)
}
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
println("Backward")
for i = 0; i < maxSpeed; i += 1000 {
wheel.Backward(i)
time.Sleep(time.Millisecond * 100)
}
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
}
println("Stop")
wheel.Stop()
}
+34
View File
@@ -0,0 +1,34 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/l9110x"
)
func main() {
wheel := l9110x.New(machine.D10, machine.D11)
wheel.Configure()
for i := 0; i <= 10; i++ {
println("Forward")
wheel.Forward()
time.Sleep(time.Millisecond * 1000)
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
println("Backward")
wheel.Backward()
time.Sleep(time.Millisecond * 1000)
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
}
println("Stop")
wheel.Stop()
}
+45
View File
@@ -0,0 +1,45 @@
package main
import (
"machine"
"time"
"tinygo.org/x/drivers/l9110x"
)
const (
maxSpeed = 30000
)
func main() {
machine.InitPWM()
wheel := l9110x.NewWithSpeed(machine.PWM{machine.D11}, machine.PWM{machine.D12})
wheel.Configure()
for i := 0; i <= 10; i++ {
println("Forward")
var i uint16
for i = 0; i < maxSpeed; i += 1000 {
wheel.Forward(i)
time.Sleep(time.Millisecond * 100)
}
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
println("Backward")
for i = 0; i < maxSpeed; i += 1000 {
wheel.Backward(i)
time.Sleep(time.Millisecond * 100)
}
println("Stop")
wheel.Stop()
time.Sleep(time.Millisecond * 1000)
}
println("Stop")
wheel.Stop()
}
+80
View File
@@ -0,0 +1,80 @@
// demo of 4-wire touchscreen as described in app note:
// http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf
package main
import (
"machine"
"math"
"tinygo.org/x/drivers/touch"
"tinygo.org/x/drivers/touch/resistive"
)
var (
resistiveTouch = new(resistive.FourWire)
)
const (
Xmin = 750
Xmax = 325
Ymin = 840
Ymax = 240
)
func main() {
// configure touchscreen
machine.InitADC()
resistiveTouch.Configure(&resistive.FourWireConfig{
YP: machine.TOUCH_YD, // y+
YM: machine.TOUCH_YU, // y-
XP: machine.TOUCH_XR, // x+
XM: machine.TOUCH_XL, // x-
})
last := touch.Point{}
// loop and poll for touches, including performing debouncing
debounce := 0
for {
point := resistiveTouch.ReadTouchPoint()
touch := touch.Point{}
if point.Z>>6 > 100 {
touch.X = mapval(point.X>>6, Xmin, Xmax, 0, 240)
touch.Y = mapval(point.Y>>6, Ymin, Ymax, 0, 320)
touch.Z = point.Z >> 6 / 100
} else {
touch.X = 0
touch.Y = 0
touch.Z = 0
}
if last.Z != touch.Z {
debounce = 0
last = touch
} else if math.Abs(float64(touch.X-last.X)) > 4 ||
math.Abs(float64(touch.Y-last.Y)) > 4 {
debounce = 0
last = touch
} else if debounce > 1 {
debounce = 0
HandleTouch(last)
} else if touch.Z > 0 {
debounce++
} else {
last = touch
debounce = 0
}
}
}
// based on Arduino's "map" function
func mapval(x int, inMin int, inMax int, outMin int, outMax int) int {
return (x-inMin)*(outMax-outMin)/(inMax-inMin) + outMin
}
func HandleTouch(touch touch.Point) {
println("touch point:", touch.X, touch.Y, touch.Z)
}
@@ -0,0 +1,185 @@
package main
import (
"image/color"
"machine"
"math"
"tinygo.org/x/drivers/ili9341"
"tinygo.org/x/drivers/touch"
"tinygo.org/x/drivers/touch/resistive"
)
var (
resistiveTouch = &resistive.FourWire{}
display = ili9341.NewParallel(
machine.LCD_DATA0,
machine.TFT_WR,
machine.TFT_DC,
machine.TFT_CS,
machine.TFT_RESET,
machine.TFT_RD,
)
white = color.RGBA{255, 255, 255, 255}
black = color.RGBA{0, 0, 0, 255}
red = color.RGBA{255, 0, 0, 255}
green = color.RGBA{0, 255, 0, 255}
blue = color.RGBA{0, 0, 255, 255}
magenta = color.RGBA{255, 0, 255, 255}
yellow = color.RGBA{255, 255, 0, 255}
cyan = color.RGBA{0, 255, 255, 255}
oldColor color.RGBA
currentColor color.RGBA
)
const (
penRadius = 3
boxSize = 30
Xmin = 750
Xmax = 325
Ymin = 840
Ymax = 240
)
func main() {
// configure backlight
machine.TFT_BACKLIGHT.Configure(machine.PinConfig{machine.PinOutput})
// configure touchscreen
machine.InitADC()
resistiveTouch.Configure(&resistive.FourWireConfig{
YP: machine.TOUCH_YD,
YM: machine.TOUCH_YU,
XP: machine.TOUCH_XR,
XM: machine.TOUCH_XL,
})
// configure display
display.Configure(ili9341.Config{})
// fill the background and activate the backlight
width, height := display.Size()
display.FillRectangle(0, 0, width, height, black)
machine.TFT_BACKLIGHT.High()
// make color selection boxes
display.FillRectangle(0, 0, boxSize, boxSize, red)
display.FillRectangle(boxSize, 0, boxSize, boxSize, yellow)
display.FillRectangle(boxSize*2, 0, boxSize, boxSize, green)
display.FillRectangle(boxSize*3, 0, boxSize, boxSize, cyan)
display.FillRectangle(boxSize*4, 0, boxSize, boxSize, blue)
display.FillRectangle(boxSize*5, 0, boxSize, boxSize, magenta)
display.FillRectangle(boxSize*6, 0, boxSize, boxSize, black)
display.FillRectangle(boxSize*7, 0, boxSize, boxSize, white)
// set the initial color to red and draw a box to highlight it
oldColor = red
currentColor = red
display.DrawRectangle(0, 0, boxSize, boxSize, white)
last := touch.Point{}
// loop and poll for touches, including performing debouncing
debounce := 0
for {
point := resistiveTouch.ReadTouchPoint()
touch := touch.Point{}
if point.Z>>6 > 100 {
rawX := mapval(point.X>>6, Xmin, Xmax, 0, 240)
rawY := mapval(point.Y>>6, Ymin, Ymax, 0, 320)
touch.X = rawX
touch.Y = rawY
touch.Z = 1
} else {
touch.X = 0
touch.Y = 0
touch.Z = 0
}
if last.Z != touch.Z {
debounce = 0
last = touch
} else if math.Abs(float64(touch.X-last.X)) > 4 ||
math.Abs(float64(touch.Y-last.Y)) > 4 {
debounce = 0
last = touch
} else if debounce > 1 {
debounce = 0
HandleTouch(last)
} else if touch.Z > 0 {
debounce++
} else {
last = touch
debounce = 0
}
}
}
// based on Arduino's "map" function
func mapval(x int, inMin int, inMax int, outMin int, outMax int) int {
return (x-inMin)*(outMax-outMin)/(inMax-inMin) + outMin
}
func HandleTouch(touch touch.Point) {
if int16(touch.Y) < boxSize {
oldColor = currentColor
x := int16(touch.X)
switch {
case x < boxSize:
currentColor = red
case x < boxSize*2:
currentColor = yellow
case x < boxSize*3:
currentColor = green
case x < boxSize*4:
currentColor = cyan
case x < boxSize*5:
currentColor = blue
case x < boxSize*6:
currentColor = magenta
case x < boxSize*7:
currentColor = black
case x < boxSize*8:
currentColor = white
}
if oldColor == currentColor {
return
}
display.DrawRectangle((x/boxSize)*boxSize, 0, boxSize, boxSize, white)
switch oldColor {
case red:
x = 0
case yellow:
x = boxSize
case green:
x = boxSize * 2
case cyan:
x = boxSize * 3
case blue:
x = boxSize * 4
case magenta:
x = boxSize * 5
case black:
x = boxSize * 6
case white:
x = boxSize * 7
}
display.FillRectangle(int16(x), 0, boxSize, boxSize, oldColor)
}
if (int16(touch.Y) - penRadius) > boxSize {
display.FillRectangle(
int16(touch.X), int16(touch.Y), penRadius*2, penRadius*2, currentColor)
}
}
+10
View File
@@ -0,0 +1,10 @@
// +build digispark
package main
import "machine"
// This is the pin assignment for the Digispark only.
// Replace neo in the code below to match the pin
// that you are using if different.
var neo machine.Pin = 0
+10 -7
View File
@@ -1,8 +1,7 @@
// Connects to an WS2812 RGB LED strip with 10 LEDS, such as
// on an Adafruit Circuit Playground Express board.
// Connects to an WS2812 RGB LED strip with 10 LEDS.
//
// Replace machine.NEOPIXELS in the code below to match the pin
// that you are using, if you have a different board.
// See either the others.go or digispark.go files in this directory
// for the neopixels pin assignments.
package main
import (
@@ -13,12 +12,15 @@ import (
"tinygo.org/x/drivers/ws2812"
)
var leds [10]color.RGBA
func main() {
neo := machine.NEOPIXELS
led := machine.LED
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
ws := ws2812.New(neo)
leds := make([]color.RGBA, 10)
rg := false
for {
@@ -33,7 +35,8 @@ func main() {
}
}
ws.WriteColors(leds)
ws.WriteColors(leds[:])
led.Set(rg)
time.Sleep(100 * time.Millisecond)
}
}
+9
View File
@@ -0,0 +1,9 @@
// +build !digispark
package main
import "machine"
// Replace neo in the code below to match the pin
// that you are using if different.
var neo machine.Pin = machine.NEOPIXELS
+19
View File
@@ -0,0 +1,19 @@
TinyGo driver for TFT displays using ILI9341 driver chips.
These displays support 8-bit parallel, 16-bit parallel, or SPI interfaces.
Examples of such displays include:
* [Adafruit PyPortal
](https://www.adafruit.com/product/4116)
* [Adafruit 2.8" Touch Shield V2 (SPI)](http://www.adafruit.com/products/1651)
* [Adafruit 2.4" TFT LCD with Touchscreen Breakout w/MicroSD Socket](https://www.adafruit.com/product/2478)
* [2.8" TFT LCD with Touchscreen Breakout Board w/MicroSD Socket](https://www.adafruit.com/product/1770)
* [2.2" 18-bit color TFT LCD display with microSD card breakout](https://www.adafruit.com/product/1770)
* [TFT FeatherWing - 2.4" 320x240 Touchscreen For All Feathers](https://www.adafruit.com/product/3315)
Currently this driver only supports an 8-bit parallel interface using ATSAMD51
(this is the default configuration on PyPortal). It should be relatively
straightforward to implement a more generic SPI-based interface as well.
Please see `parallel_atsamd51.go` for an example of what needs to be
implemented if you are interested in contributing.
+290
View File
@@ -0,0 +1,290 @@
package ili9341
import (
"errors"
"image/color"
"machine"
"time"
)
const _debug = false
type Config struct {
Width int16
Height int16
Rotation Rotation
}
type Device struct {
width int16
height int16
rotation Rotation
driver driver
dc machine.Pin
cs machine.Pin
rst machine.Pin
rd machine.Pin
}
func (d *Device) Configure(config Config) {
if config.Width == 0 {
config.Width = TFTWIDTH
}
if config.Height == 0 {
config.Height = TFTHEIGHT
}
d.width = config.Width
d.height = config.Height
output := machine.PinConfig{machine.PinOutput}
// configure chip select if there is one
if d.cs != machine.NoPin {
d.cs.Configure(output)
d.cs.High() // deselect
}
d.dc.Configure(output)
d.dc.High() // data mode
// driver-specific configuration
d.driver.configure(&config)
if d.rd != machine.NoPin {
d.rd.Configure(output)
d.rd.High()
}
// reset the display
if d.rst != machine.NoPin {
// configure hardware reset if there is one
d.rst.Configure(output)
d.rst.High()
delay(100)
d.rst.Low()
delay(100)
d.rst.High()
delay(200)
} else {
// if no hardware reset, send software reset
d.sendCommand(SWRESET, nil)
delay(150)
}
initCmd := []byte{
0xEF, 3, 0x03, 0x80, 0x02,
0xCF, 3, 0x00, 0xC1, 0x30,
0xED, 4, 0x64, 0x03, 0x12, 0x81,
0xE8, 3, 0x85, 0x00, 0x78,
0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
0xF7, 1, 0x20,
0xEA, 2, 0x00, 0x00,
PWCTR1, 1, 0x23, // Power control VRH[5:0]
PWCTR2, 1, 0x10, // Power control SAP[2:0];BT[3:0]
VMCTR1, 2, 0x3e, 0x28, // VCM control
VMCTR2, 1, 0x86, // VCM control2
MADCTL, 1, 0x48, // Memory Access Control
VSCRSADD, 1, 0x00, // Vertical scroll zero
PIXFMT, 1, 0x55,
FRMCTR1, 2, 0x00, 0x18,
DFUNCTR, 3, 0x08, 0x82, 0x27, // Display Function Control
0xF2, 1, 0x00, // 3Gamma Function Disable
GAMMASET, 1, 0x01, // Gamma curve selected
GMCTRP1, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, // Set Gamma
0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
GMCTRN1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, // Set Gamma
0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
SLPOUT, 0x80, // Exit Sleep
DISPON, 0x80, // Display on
0x00, // End of list
}
for i, c := 0, len(initCmd); i < c; {
cmd := initCmd[i]
if cmd == 0x00 {
break
}
x := initCmd[i+1]
numArgs := int(x & 0x7F)
d.sendCommand(cmd, initCmd[i+2:i+2+numArgs])
if x&0x80 > 0 {
delay(150)
}
i += numArgs + 2
}
}
// Size returns the current size of the display.
func (d *Device) Size() (x, y int16) {
if d.rotation == 1 || d.rotation == 3 {
return d.height, d.width
}
return d.width, d.height
}
// SetPixel modifies the internal buffer.
func (d *Device) SetPixel(x, y int16, c color.RGBA) {
d.setWindow(x, y, 1, 1)
c565 := RGBATo565(c)
d.startWrite()
d.driver.write16(c565)
d.endWrite()
}
// Display sends the buffer (if any) to the screen.
func (d *Device) Display() error {
return nil
}
func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
k, i := d.Size()
if x < 0 || y < 0 || w <= 0 || h <= 0 ||
x >= k || (x+w) > k || y >= i || (y+h) > i {
return errors.New("rectangle coordinates outside display area")
}
d.setWindow(x, y, w, h)
d.startWrite()
d.driver.write16sl(data)
d.endWrite()
return nil
}
// FillRectangle fills a rectangle at a given coordinates with a color
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
k, i := d.Size()
if x < 0 || y < 0 || width <= 0 || height <= 0 ||
x >= k || (x+width) > k || y >= i || (y+height) > i {
return errors.New("rectangle coordinates outside display area")
}
d.setWindow(x, y, width, height)
c565 := RGBATo565(c)
d.startWrite()
d.driver.write16n(c565, int(width)*int(height))
d.endWrite()
return nil
}
// DrawRectangle fills a rectangle at a given coordinates with a color
func (d *Device) DrawRectangle(x, y, w, h int16, c color.RGBA) error {
if err := d.DrawFastHLine(x, x+w-1, y, c); err != nil {
return err
}
if err := d.DrawFastHLine(x, x+w-1, y+h-1, c); err != nil {
return err
}
if err := d.DrawFastVLine(x, y, y+h-1, c); err != nil {
return err
}
if err := d.DrawFastVLine(x+w-1, y, y+h-1, c); err != nil {
return err
}
return nil
}
// DrawFastVLine draws a vertical line faster than using SetPixel
func (d *Device) DrawFastVLine(x, y0, y1 int16, c color.RGBA) error {
if y0 > y1 {
y0, y1 = y1, y0
}
return d.FillRectangle(x, y0, 1, y1-y0+1, c)
}
// DrawFastHLine draws a horizontal line faster than using SetPixel
func (d *Device) DrawFastHLine(x0, x1, y int16, c color.RGBA) error {
if x0 > x1 {
x0, x1 = x1, x0
}
return d.FillRectangle(x0, y, x1-x0+1, 1, c)
}
// FillScreen fills the screen with a given color
func (d *Device) FillScreen(c color.RGBA) {
if d.rotation == Rotation0 || d.rotation == Rotation180 {
d.FillRectangle(0, 0, d.width, d.height, c)
} else {
d.FillRectangle(0, 0, d.height, d.width, c)
}
}
func (d *Device) GetRotation() Rotation {
return d.rotation
}
// SetRotation changes the rotation of the device (clock-wise)
func (d *Device) SetRotation(rotation Rotation) {
madctl := uint8(0)
switch rotation % 4 {
case 0:
madctl = MADCTL_MX | MADCTL_BGR
case 1:
madctl = MADCTL_MV | MADCTL_BGR
case 2:
madctl = MADCTL_MY | MADCTL_BGR
case 3:
madctl = MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR
}
d.sendCommand(MADCTL, []uint8{madctl})
d.rotation = rotation
}
// setWindow prepares the screen to be modified at a given rectangle
func (d *Device) setWindow(x, y, w, h int16) {
//x += d.columnOffset
//y += d.rowOffset
d.sendCommand(CASET, []uint8{
uint8(x << 8), uint8(x), uint8((x + w - 1) >> 8), uint8(x + w - 1),
})
d.sendCommand(PASET, []uint8{
uint8(y >> 8), uint8(y), uint8((y + h - 1) >> 8), uint8(y + h - 1),
})
d.sendCommand(RAMWR, nil)
}
//go:inline
func (d *Device) startWrite() {
if d.cs != machine.NoPin {
d.cs.Low()
}
}
//go:inline
func (d *Device) endWrite() {
if d.cs != machine.NoPin {
d.cs.High()
}
}
func (d *Device) sendCommand(cmd byte, data []byte) {
d.startWrite()
d.dc.Low()
d.driver.write8(cmd)
d.dc.High()
for _, b := range data {
d.driver.write8(b)
}
d.endWrite()
}
type driver interface {
configure(config *Config)
write8(b byte)
write16(data uint16)
write16n(data uint16, n int)
write16sl(data []uint16)
}
func delay(m int) {
t := time.Now().UnixNano() + int64(time.Duration(m*1000)*time.Microsecond)
for time.Now().UnixNano() < t {
}
}
// RGBATo565 converts a color.RGBA to uint16 used in the display
func RGBATo565(c color.RGBA) uint16 {
r, g, b, _ := c.RGBA()
return uint16((r & 0xF800) +
((g & 0xFC00) >> 5) +
((b & 0xF800) >> 11))
}
+87
View File
@@ -0,0 +1,87 @@
// +build atsamd51
package ili9341
import (
"machine"
"runtime/volatile"
)
type parallelDriver struct {
d0 machine.Pin
wr machine.Pin
setPort *uint32
setMask uint32
clrPort *uint32
clrMask uint32
wrPortSet *uint32
wrMaskSet uint32
wrPortClr *uint32
wrMaskClr uint32
}
func NewParallel(d0, wr, dc, cs, rst, rd machine.Pin) *Device {
return &Device{
dc: dc,
cs: cs,
rd: rd,
rst: rst,
driver: &parallelDriver{
d0: d0,
wr: wr,
},
}
}
func (pd *parallelDriver) configure(config *Config) {
output := machine.PinConfig{machine.PinOutput}
for pin := pd.d0; pin < pd.d0+8; pin++ {
pin.Configure(output)
pin.Low()
}
pd.wr.Configure(output)
pd.wr.High()
pd.setPort, _ = pd.d0.PortMaskSet()
pd.setMask = uint32(pd.d0) & 0x1f
pd.clrPort, _ = (pd.d0).PortMaskClear()
pd.clrMask = 0xFF << uint32(pd.d0)
pd.wrPortSet, pd.wrMaskSet = pd.wr.PortMaskSet()
pd.wrPortClr, pd.wrMaskClr = pd.wr.PortMaskClear()
}
//go:inline
func (pd *parallelDriver) write8(b byte) {
volatile.StoreUint32(pd.clrPort, pd.clrMask)
volatile.StoreUint32(pd.setPort, uint32(b)<<pd.setMask)
volatile.StoreUint32(pd.wrPortClr, pd.wrMaskClr)
volatile.StoreUint32(pd.wrPortSet, pd.wrMaskSet)
}
//go:inline
func (pd *parallelDriver) write16(data uint16) {
pd.write8(byte(data >> 8))
pd.write8(byte(data))
}
//go:inline
func (pd *parallelDriver) write16n(data uint16, n int) {
for i := 0; i < n; i++ {
pd.write8(byte(data >> 8))
pd.write8(byte(data))
}
}
//go:inline
func (pd *parallelDriver) write16sl(data []uint16) {
for i, c := 0, len(data); i < c; i++ {
pd.write8(byte(data[i] >> 8))
pd.write8(byte(data[i]))
}
}
+84
View File
@@ -0,0 +1,84 @@
package ili9341
type Rotation uint8
const (
// register constants based on source:
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.h
TFTWIDTH = 240 ///< ILI9341 max TFT width
TFTHEIGHT = 320 ///< ILI9341 max TFT height
NOP = 0x00 ///< No-op register
SWRESET = 0x01 ///< Software reset register
RDDID = 0x04 ///< Read display identification information
RDDST = 0x09 ///< Read Display Status
SLPIN = 0x10 ///< Enter Sleep Mode
SLPOUT = 0x11 ///< Sleep Out
PTLON = 0x12 ///< Partial Mode ON
NORON = 0x13 ///< Normal Display Mode ON
RDMODE = 0x0A ///< Read Display Power Mode
RDMADCTL = 0x0B ///< Read Display MADCTL
RDPIXFMT = 0x0C ///< Read Display Pixel Format
RDIMGFMT = 0x0D ///< Read Display Image Format
RDSELFDIAG = 0x0F ///< Read Display Self-Diagnostic Result
INVOFF = 0x20 ///< Display Inversion OFF
INVON = 0x21 ///< Display Inversion ON
GAMMASET = 0x26 ///< Gamma Set
DISPOFF = 0x28 ///< Display OFF
DISPON = 0x29 ///< Display ON
CASET = 0x2A ///< Column Address Set
PASET = 0x2B ///< Page Address Set
RAMWR = 0x2C ///< Memory Write
RAMRD = 0x2E ///< Memory Read
PTLAR = 0x30 ///< Partial Area
VSCRDEF = 0x33 ///< Vertical Scrolling Definition
MADCTL = 0x36 ///< Memory Access Control
VSCRSADD = 0x37 ///< Vertical Scrolling Start Address
PIXFMT = 0x3A ///< COLMOD: Pixel Format Set
FRMCTR1 = 0xB1 ///< Frame Rate Control (In Normal Mode/Full Colors)
FRMCTR2 = 0xB2 ///< Frame Rate Control (In Idle Mode/8 colors)
FRMCTR3 = 0xB3 ///< Frame Rate control (In Partial Mode/Full Colors)
INVCTR = 0xB4 ///< Display Inversion Control
DFUNCTR = 0xB6 ///< Display Function Control
PWCTR1 = 0xC0 ///< Power Control 1
PWCTR2 = 0xC1 ///< Power Control 2
PWCTR3 = 0xC2 ///< Power Control 3
PWCTR4 = 0xC3 ///< Power Control 4
PWCTR5 = 0xC4 ///< Power Control 5
VMCTR1 = 0xC5 ///< VCOM Control 1
VMCTR2 = 0xC7 ///< VCOM Control 2
RDID1 = 0xDA ///< Read ID 1
RDID2 = 0xDB ///< Read ID 2
RDID3 = 0xDC ///< Read ID 3
RDID4 = 0xDD ///< Read ID 4
GMCTRP1 = 0xE0 ///< Positive Gamma Correction
GMCTRN1 = 0xE1 ///< Negative Gamma Correction
//PWCTR6 0xFC
MADCTL_MY = 0x80 ///< Bottom to top
MADCTL_MX = 0x40 ///< Right to left
MADCTL_MV = 0x20 ///< Reverse Mode
MADCTL_ML = 0x10 ///< LCD refresh Bottom to top
MADCTL_RGB = 0x00 ///< Red-Green-Blue pixel order
MADCTL_BGR = 0x08 ///< Blue-Green-Red pixel order
MADCTL_MH = 0x04 ///< LCD refresh right to left
)
const (
Rotation0 Rotation = 0
Rotation90 Rotation = 1 // 90 degrees clock-wise rotation
Rotation180 Rotation = 2
Rotation270 Rotation = 3
)
+104
View File
@@ -0,0 +1,104 @@
// Package l293x provides a driver to the L293/L293D H-bridge chip
// typically used to control DC motors.
//
// Datasheet: https://www.ti.com/lit/ds/symlink/l293d.pdf
//
package l293x // import "tinygo.org/x/drivers/l293x"
import (
"machine"
)
// Device is a motor without speed control.
// a1 and a2 are the directional pins.
// en is the pin turns the motor on/off.
type Device struct {
a1, a2 machine.Pin
en machine.Pin
}
// New returns a new Motor driver for GPIO-only operation.
func New(direction1, direction2, enablePin machine.Pin) Device {
return Device{
a1: direction1,
a2: direction2,
en: enablePin,
}
}
// Configure configures the Device.
func (d *Device) Configure() {
d.a1.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.a2.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.en.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.Stop()
}
// Forward turns motor on in forward direction.
func (d *Device) Forward() {
d.a1.High()
d.a2.Low()
d.en.High()
}
// Backward turns motor on in backward direction.
func (d *Device) Backward() {
d.a1.Low()
d.a2.High()
d.en.High()
}
// Stop turns motor off.
func (d *Device) Stop() {
d.a1.Low()
d.a2.Low()
d.en.Low()
}
// PWMDevice is a motor with speed control.
// a1 and a2 are the directional GPIO pins.
// en is the PWM pin that controls the motor speed.
type PWMDevice struct {
a1, a2 machine.Pin
en machine.PWM
}
// NewWithSpeed returns a new PWMMotor driver that uses a PWM pin to control speed.
func NewWithSpeed(direction1, direction2 machine.Pin, speedPin machine.PWM) PWMDevice {
return PWMDevice{
a1: direction1,
a2: direction2,
en: speedPin,
}
}
// Configure configures the PWMDevice.
func (d *PWMDevice) Configure() {
d.a1.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.a2.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.en.Configure()
d.Stop()
}
// Forward turns motor on in forward direction at specific speed.
func (d *PWMDevice) Forward(speed uint16) {
d.a1.High()
d.a2.Low()
d.en.Set(speed)
}
// Backward turns motor on in backward direction at specific speed.
func (d *PWMDevice) Backward(speed uint16) {
d.a1.Low()
d.a2.High()
d.en.Set(speed)
}
// Stop turns motor off.
func (d *PWMDevice) Stop() {
d.a1.Low()
d.a2.Low()
d.en.Set(0)
}
+90
View File
@@ -0,0 +1,90 @@
// Package l9110x provides a driver to the L9110/L9110S H-bridge chip
// typically used to control DC motors.
//
// Datasheet: https://www.elecrow.com/download/datasheet-l9110.pdf
//
package l9110x // import "tinygo.org/x/drivers/l9110x"
import (
"machine"
)
// Device is a motor without speed control.
// ia and ib are the directional pins.
type Device struct {
ia, ib machine.Pin
}
// New returns a new Motor driver for GPIO-only operation.
func New(direction1, direction2 machine.Pin) Device {
return Device{
ia: direction1,
ib: direction2,
}
}
// Configure configures the Device.
func (d *Device) Configure() {
d.ia.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.ib.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.Stop()
}
// Forward turns motor on in forward direction.
func (d *Device) Forward() {
d.ia.High()
d.ib.Low()
}
// Backward turns motor on in backward direction.
func (d *Device) Backward() {
d.ia.Low()
d.ib.High()
}
// Stop turns motor off.
func (d *Device) Stop() {
d.ia.Low()
d.ib.Low()
}
// PWMDevice is a motor with speed control.
// ia and ib are the directional/speed PWM pins.
type PWMDevice struct {
ia, ib machine.PWM
}
// NewWithSpeed returns a new PWMMotor driver that uses 2 PWM pins to control both direction and speed.
func NewWithSpeed(direction1, direction2 machine.PWM) PWMDevice {
return PWMDevice{
ia: direction1,
ib: direction2,
}
}
// Configure configures the PWMDevice.
func (d *PWMDevice) Configure() {
d.ia.Configure()
d.ib.Configure()
d.Stop()
}
// Forward turns motor on in forward direction at specific speed.
func (d *PWMDevice) Forward(speed uint16) {
d.ia.Set(speed)
d.ib.Set(0)
}
// Backward turns motor on in backward direction at specific speed.
func (d *PWMDevice) Backward(speed uint16) {
d.ia.Set(0)
d.ib.Set(speed)
}
// Stop turns motor off.
func (d *PWMDevice) Stop() {
d.ia.Set(0)
d.ib.Set(0)
}
+2
View File
@@ -46,6 +46,8 @@ const (
PWCTR6 = 0xFC
GMCTRP1 = 0xE0
GMCTRN1 = 0xE1
VSCRDEF = 0x33
VSCRSADD = 0x37
GREENTAB Model = 0
MINI80x160 Model = 1
+23 -15
View File
@@ -76,21 +76,8 @@ func (d *Device) Configure(cfg Config) {
d.height = 160
}
d.rotation = cfg.Rotation
if cfg.RowOffset != 0 {
d.rowOffset = cfg.RowOffset
} else {
d.rowOffset = 1
}
if cfg.ColumnOffset != 0 {
d.columnOffset = cfg.ColumnOffset
} else {
if d.model == MINI80x160 {
d.columnOffset = 26
} else {
d.columnOffset = 2
}
}
d.rowOffset = cfg.RowOffset
d.columnOffset = cfg.ColumnOffset
d.batchLength = d.width
if d.height > d.width {
@@ -237,6 +224,27 @@ func (d *Device) setWindow(x, y, w, h int16) {
d.Command(RAMWR)
}
// SetScrollWindow sets an area to scroll with fixed top and bottom parts of the display
func (d *Device) SetScrollArea(topFixedArea, bottomFixedArea int16) {
d.Command(VSCRDEF)
d.Tx([]uint8{
uint8(topFixedArea >> 8), uint8(topFixedArea),
uint8(d.height - topFixedArea - bottomFixedArea>>8), uint8(d.height - topFixedArea - bottomFixedArea),
uint8(bottomFixedArea >> 8), uint8(bottomFixedArea)},
false)
}
// SetScroll sets the vertical scroll address of the display.
func (d *Device) SetScroll(line int16) {
d.Command(VSCRSADD)
d.Tx([]uint8{uint8(line >> 8), uint8(line)}, false)
}
// SpotScroll returns the display to its normal state
func (d *Device) StopScroll() {
d.Command(NORON)
}
// FillRectangle fills a rectangle at a given coordinates with a color
func (d *Device) FillRectangle(x, y, width, height int16, c color.RGBA) error {
k, i := d.Size()
+2 -9
View File
@@ -66,15 +66,8 @@ func (d *Device) Configure(cfg Config) {
d.height = 240
}
d.rotation = cfg.Rotation
if cfg.RowOffset != 0 {
d.rowOffsetCfg = cfg.RowOffset
} else {
d.rowOffsetCfg = 80
}
if cfg.ColumnOffset != 0 {
d.columnOffsetCfg = cfg.ColumnOffset
}
d.rowOffsetCfg = cfg.RowOffset
d.columnOffsetCfg = cfg.ColumnOffset
d.batchLength = int32(d.width)
if d.height > d.width {
+17
View File
@@ -0,0 +1,17 @@
package touch
// Pointer is a device that is capable of reading a single touch point
type Pointer interface {
ReadTouchPoint() Point
}
// Point represents the result of reading a single touch point from a screen.
// X and Y are the horizontal and vertical coordinates of the touch, while Z
// represents the touch pressure. In general, client code will want to inspect
// the value of Z to see if it is above some threshold to determine if a touch
// is detected at all.
type Point struct {
X int
Y int
Z int
}
+124
View File
@@ -0,0 +1,124 @@
package resistive
import (
"machine"
"tinygo.org/x/drivers/touch"
)
// FourWire represents a resistive touchscreen with a four-wire interface as
// described in http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf
type FourWire struct {
yp machine.ADC
ym machine.ADC
xp machine.ADC
xm machine.ADC
readSamples int
}
// FourWireConfig is passed to the Configure method. All of the pins must be
// specified for this to be a valid configuration. ReadSamples is optional, and
// if not set with default to 2.
type FourWireConfig struct {
// Y+ pin, must be capable of analog reads
YP machine.Pin
// Y- pin, must be capable of analog reads
YM machine.Pin
// X+ pin, must be capable of analog reads
XP machine.Pin
// X- pin, must be capable of analog reads
XM machine.Pin
// If set, each call to ReadTouchPoint() will sample the X, Y, and Z values
// and average them. This can help smooth out spurious readings, for example
// ones that result from the capacitance of a TFT under the touchscreen
ReadSamples int
}
// Configure should be called once before starting to read the device
func (res *FourWire) Configure(config *FourWireConfig) error {
res.yp = machine.ADC{Pin: config.YP}
res.ym = machine.ADC{Pin: config.YM}
res.xp = machine.ADC{Pin: config.XP}
res.xm = machine.ADC{Pin: config.XM}
if config.ReadSamples < 1 {
res.readSamples = 2
} else {
res.readSamples = config.ReadSamples
}
return nil
}
// ReadTouchPoint reads a single touch.Point from the device. If the device
// was configured with ReadSamples > 1, each value will be sampled that many
// times and averaged to smooth over spurious results of the analog reads.
func (res *FourWire) ReadTouchPoint() (p touch.Point) {
p.X = int(sample(res.ReadX, res.readSamples))
p.Y = int(sample(res.ReadY, res.readSamples))
p.Z = int(sample(res.ReadZ, res.readSamples))
return
}
// sample the results of the provided function and average the results
func sample(fn func() uint16, numSamples int) (v uint16) {
sum := 0
for n := 0; n < numSamples; n++ {
sum += int(fn())
}
return uint16(sum / numSamples)
}
// ReadX reads the "raw" X-value on a 16-bit scale without multiple sampling
func (res *FourWire) ReadX() uint16 {
res.ym.Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
res.xp.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
res.xp.Pin.High()
res.xm.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
res.xm.Pin.Low()
res.yp.Configure()
return 0xFFFF - res.yp.Get()
}
// ReadY reads the "raw" Y-value on a 16-bit scale without multiple sampling
func (res *FourWire) ReadY() uint16 {
res.xm.Pin.Configure(machine.PinConfig{Mode: machine.PinInputPulldown})
res.yp.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
res.yp.Pin.High()
res.ym.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
res.ym.Pin.Low()
res.xp.Configure()
return 0xFFFF - res.xp.Get()
}
// ReadZ reads the "raw" Z-value on a 16-bit scale without multiple sampling
func (res *FourWire) ReadZ() uint16 {
res.xp.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
res.xp.Pin.Low()
res.ym.Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
res.ym.Pin.High()
res.xm.Configure()
res.yp.Configure()
z1 := res.xm.Get()
z2 := res.yp.Get()
return 0xFFFF - (z2 - z1)
}
+1 -1
View File
@@ -2,4 +2,4 @@ package drivers
// Version returns a user-readable string showing the version of the drivers package for support purposes.
// Update this value before release of new version of software.
const Version = "0.9.0"
const Version = "0.10.0"
+28
View File
@@ -0,0 +1,28 @@
# docker build -t wifinina .
# docker run wifinina -v "../build/wifinina:/src/build"
FROM debian:stable-slim AS esp
WORKDIR /src
RUN apt-get clean && apt-get update && \
apt-get install -y sudo wget gcc git wget libncurses-dev flex bison gperf build-essential \
python python-pip python-setuptools python-serial python-cryptography python-future python-pyparsing make
RUN mkdir /src/wifinina && \
cd /src/wifinina && \
wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz && \
mkdir -p /src/esp && \
cd /src/esp && \
tar -xzf /src/wifinina/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz
RUN cd /src/esp && \
git clone --branch v3.3.1 --recursive https://github.com/espressif/esp-idf.git
FROM esp AS nina
RUN cd /src/esp && \
git clone https://github.com/arduino/nina-fw.git
COPY ./firmware.sh /src
RUN chmod +x /src/firmware.sh
ENTRYPOINT ["/src/firmware.sh"]
+58
View File
@@ -0,0 +1,58 @@
# WifiNINA Driver
This package provides a driver to use a separate connected WiFi processor ESP32 for TCP/UDP communication.
The way this driver works is by using the SPI interface of your microcontroller to communicate with the WiFi chip using the Arduino SPI command set.
## Using the WiFiNINA Driver
For information on how to use this driver, please take a look at the examples located in the [examples/wifinina](../examples/wifinina) directory.
## WiFiNINA Firmware Installation
**PLEASE NOTE: New Arduino Nano33 IoT boards already have the WiFiNINA firmware pre-installed, so you should not need to install the firmware yourself.**
In order to use this driver, you must have the WiFiNINA firmware installed on the ESP32 chip. If it is already installed, you can just use it. You do not need to build and flash the firmware again.
### Building the WifiNINA firmware
We have provided a Dockerfile that can build the needed firmware.
```shell
docker build -t wifinina ./wifinina/
docker run -v "$(pwd)/build:/src/build" wifinina
```
This will put the firmware files into the `build` directory. Now you can flash them to the ESP32 chip.
### Installing esptool to flash WifiNINA firmware
In order to flash the firmware, you need to use Python to install the `esptool` package.
```shell
pip install esptool
```
Once you have installed `esptool` you can follow the correct procedure for flashing your board.
### Installing on Arduino Nano33 IoT
The Arduino Nano33 IoT board has the WiFiNINA firmware flashed onto the onboard NINA-W102 chip out of the box.
Flashing the firmware is only necessary on the Arduino Nano33 IoT in order to upgrade or if other firmware was installed previously.
If you do want to install the firmware on the Arduino Nano33 IoT board's built-in NINA-W102 chip, you will need to first build the firmware as described above.
To flash this firmware on the Arduino Nano33 IoT you will need to follow the following procedure using the Arduino IDE software:
- Install _Arduino SAMD Boards_ from the Boards Manager.
- Install _WiFiNINA_ from the Library Manager.
- Using the normal Arduino software, load the `SerialNINAPassthrough` sketch on to the board (in File -> Examples -> WiFiNINA-> Tools).
Now you can flash the WifiNINA firmware using the `esptool` script:
```shell
python esptool.py --chip esp32 --port /dev/ttyACM0 --baud 115200 --before no_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 build/bootloader.bin 0xf000 build/phy_init_data.bin 0x30000 build/nina-fw.bin 0x8000 build/partitions.bin
```
You only need to do this one time, and then the correct WiFiNINA firmware will be on the NINA chip, and you can just flash the Arduino Nano33 IoT board using TinyGo. We should be able to remove some of these steps in a future release of this software.
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
cd /src/esp/nina-fw
export PATH=/src/esp/xtensa-esp32-elf/bin:$PATH
export IDF_PATH=/src/esp/esp-idf
make firmware
cp /src/esp/nina-fw/build/bootloader/bootloader.bin /src/build/
cp /src/esp/nina-fw/build/phy_init_data.bin /src/build/
cp /src/esp/nina-fw/build/nina-fw.bin /src/build/
cp /src/esp/nina-fw/build/partitions.bin /src/build/
cd -
+7 -1
View File
@@ -1,4 +1,10 @@
package wifinina
// Package wifinina implements TCP wireless communication over SPI
// with an attached separate ESP32 board using the Arduino WiFiNINA protocol.
//
// In order to use this driver, the ESP32 must be flashed with specific firmware from Arduino.
// For more information: https://github.com/arduino/nina-fw
//
package wifinina // import "tinygo.org/x/drivers/wifinina"
import (
"encoding/binary"
+50
View File
@@ -0,0 +1,50 @@
// +build digispark
package ws2812
// This file implements the WS2812 protocol for 16.5MHz Digispark AVR microcontrollers.
// This is a slightly different implementation than the one for the atmega to work around a compiler bug.
import (
"device/avr"
)
// Send a single byte using the WS2812 protocol.
func (d Device) WriteByte(c byte) error {
// For the AVR at 16MHz
portSet, maskSet := d.Pin.PortMaskSet()
portClear, maskClear := d.Pin.PortMaskClear()
// See:
// https://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/
// T0H: 4 cycles or 250ns
// T0L: 14 cycles or 875ns -> together 18 cycles or 1125ns
// T1H: 9 cycles or 562ns
// T1L: 8 cycles or 500ns -> together 17 cycles or 1062ns
avr.AsmFull(`
send_bit:
st {portSet}, {maskSet} ; [2] set output high
lsl {value} ; [1] shift off the next bit, store it in C
brcs skip_store ; [1/2] branch if this bit is high (long pulse)
st {portClear}, {maskClear} ; [2] set output low (short pulse)
skip_store:
nop ; [4] wait before changing the output again
nop
nop
nop
st {portClear}, {maskClear} ; [2] set output low (end of pulse)
nop ; [3]
nop
nop
subi {i}, 1 ; [1] subtract one (for the loop)
brne send_bit ; [1/2] send the next bit, if not at the end of the loop
`, map[string]interface{}{
"value": c,
"i": byte(8),
"maskSet": maskSet,
"portSet": portSet,
"maskClear": maskClear,
"portClear": portClear,
})
return nil
}
+1 -1
View File
@@ -1,4 +1,4 @@
// +build nrf52
// +build nrf52 nrf52840
package ws2812