mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-08-06 07:53:41 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 349b5ca87e | |||
| c5dbe18be1 | |||
| 2bca84b8d1 | |||
| 690daebcd1 | |||
| 89770a7d05 | |||
| 8a39bb7aae | |||
| a106fd48ce | |||
| b2fdd3c333 | |||
| 42dc6eb068 | |||
| c00cb3abdd |
@@ -103,6 +103,8 @@ smoke-test:
|
|||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
|
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=microbit-v2 ./examples/microbitmatrix/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
|
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
|
||||||
@@ -209,6 +211,8 @@ endif
|
|||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
|
tinygo build -size short -o ./build/test.hex -target=pico ./examples/qmi8658c/main.go
|
||||||
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
||||||
@md5sum ./build/test.hex
|
@md5sum ./build/test.hex
|
||||||
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func main() {
|
|||||||
|
|
||||||
## Currently supported devices
|
## Currently supported devices
|
||||||
|
|
||||||
The following 83 devices are supported.
|
The following 90 devices are supported.
|
||||||
|
|
||||||
| Device Name | Interface Type |
|
| Device Name | Interface Type |
|
||||||
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
|
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
|
||||||
@@ -112,6 +112,7 @@ The following 83 devices are supported.
|
|||||||
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
|
| [P1AM-100 Base Controller](https://facts-engineering.github.io/modules/P1AM-100/P1AM-100.html) | SPI |
|
||||||
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
|
| [PCD8544 display](http://eia.udg.edu/~forest/PCD8544_1.pdf) | SPI |
|
||||||
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
|
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
|
||||||
|
| [QMI8658C accelerometer/gyroscope](https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf) | I2C |
|
||||||
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
||||||
| [RTL8720DN 2.4G/5G Dual Bands Wireless and BLE5.0](https://www.seeedstudio.com/Realtek8720DN-2-4G-5G-Dual-Bands-Wireless-and-BLE5-0-Combo-Module-p-4442.html) | UART |
|
| [RTL8720DN 2.4G/5G Dual Bands Wireless and BLE5.0](https://www.seeedstudio.com/Realtek8720DN-2-4G-5G-Dual-Bands-Wireless-and-BLE5-0-Combo-Module-p-4442.html) | UART |
|
||||||
| [SCD4x CO2 Sensor](https://sensirion.com/media/documents/C4B87CE6/627C2DCD/CD_DS_SCD40_SCD41_Datasheet_D1.pdf) | I2C |
|
| [SCD4x CO2 Sensor](https://sensirion.com/media/documents/C4B87CE6/627C2DCD/CD_DS_SCD40_SCD41_Datasheet_D1.pdf) | I2C |
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
package apds9960
|
package apds9960
|
||||||
|
|
||||||
import "tinygo.org/x/drivers"
|
|
||||||
|
|
||||||
// Configure sets up the APDS-9960 device.
|
// Configure sets up the APDS-9960 device.
|
||||||
func (d *Device) Configure(cfg Configuration) {
|
func (d *Device) Configure(cfg Configuration) {
|
||||||
// configure device
|
// configure device
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Connects to an QMI8658C I2C accelerometer/gyroscope and print the read data.
|
||||||
|
// This example was made with the "WaveShare RP2040 Round LCD 1.28in" in mind.
|
||||||
|
// For more infor about this development board:
|
||||||
|
// https://www.waveshare.com/wiki/RP2040-LCD-1.28
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"machine"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
imu "tinygo.org/x/drivers/qmi8658c"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
i2c := machine.I2C1
|
||||||
|
// This is the default pinout for the "WaveShare RP2040 Round LCD 1.28in"
|
||||||
|
err := i2c.Configure(machine.I2CConfig{
|
||||||
|
SDA: machine.GP6,
|
||||||
|
SCL: machine.GP7,
|
||||||
|
Frequency: 100000,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
println("unable to configure I2C:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Create a new device
|
||||||
|
d := imu.New(i2c)
|
||||||
|
|
||||||
|
// Check if the device is connected
|
||||||
|
if !d.Connected() {
|
||||||
|
println("unable to connect to sensor")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// This IMU has multiple configurations like output data rate, multiple
|
||||||
|
// measurements scales, low pass filters, low power modes, all the vailable
|
||||||
|
// values can be found in the datasheet and were defined at registers file.
|
||||||
|
// This is the default configuration which will be used if the `nil` value
|
||||||
|
// is passed do the `Configure` method.
|
||||||
|
config := imu.Config{
|
||||||
|
SPIMode: imu.SPI_4_WIRE,
|
||||||
|
SPIEndian: imu.SPI_BIG_ENDIAN,
|
||||||
|
SPIAutoInc: imu.SPI_AUTO_INC,
|
||||||
|
AccEnable: imu.ACC_ENABLE,
|
||||||
|
AccScale: imu.ACC_8G,
|
||||||
|
AccRate: imu.ACC_NORMAL_1000HZ,
|
||||||
|
AccLowPass: imu.ACC_LOW_PASS_2_62,
|
||||||
|
GyroEnable: imu.GYRO_FULL_ENABLE,
|
||||||
|
GyroScale: imu.GYRO_512DPS,
|
||||||
|
GyroRate: imu.GYRO_1000HZ,
|
||||||
|
GyroLowPass: imu.GYRO_LOW_PASS_2_62,
|
||||||
|
}
|
||||||
|
d.Configure(config)
|
||||||
|
|
||||||
|
// Read the accelation, rotation and temperature data and print them.
|
||||||
|
for {
|
||||||
|
acc_x, acc_y, acc_z := d.ReadAcceleration()
|
||||||
|
gyro_x, gyro_y, gyro_z := d.ReadRotation()
|
||||||
|
temp, _ := d.ReadTemperature()
|
||||||
|
println("-------------------------------")
|
||||||
|
println("acc:", acc_x, acc_y, acc_z)
|
||||||
|
println("gyro:", gyro_x, gyro_y, gyro_z)
|
||||||
|
println("temp:", temp)
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -137,7 +137,7 @@ func (d *Device) Configure(cfg Configuration) (err error) {
|
|||||||
// -1000000.
|
// -1000000.
|
||||||
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
func (d *Device) ReadAcceleration() (x, y, z int32, err error) {
|
||||||
data := d.buf[:6]
|
data := d.buf[:6]
|
||||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_X_L_A, data)
|
err = d.bus.ReadRegister(uint8(d.AccelAddress), ACCEL_OUT_AUTO_INC, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -190,7 +190,7 @@ func (d *Device) ReadMagneticField() (x, y, z int32, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data := d.buf[0:6]
|
data := d.buf[0:6]
|
||||||
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_X_L_M, data)
|
d.bus.ReadRegister(uint8(d.MagAddress), MAG_OUT_AUTO_INC, data)
|
||||||
|
|
||||||
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
|
x = int32(int16((uint16(data[1])<<8 | uint16(data[0]))))
|
||||||
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
|
y = int32(int16((uint16(data[3])<<8 | uint16(data[2]))))
|
||||||
@@ -219,7 +219,7 @@ func (d *Device) ReadCompass() (h int32, err error) {
|
|||||||
func (d *Device) ReadTemperature() (t int32, err error) {
|
func (d *Device) ReadTemperature() (t int32, err error) {
|
||||||
|
|
||||||
data := d.buf[:2]
|
data := d.buf[:2]
|
||||||
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_L_A, data)
|
err = d.bus.ReadRegister(uint8(d.AccelAddress), OUT_TEMP_AUTO_INC, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-20
@@ -6,31 +6,40 @@ const (
|
|||||||
ACCEL_ADDRESS = 0x19
|
ACCEL_ADDRESS = 0x19
|
||||||
MAG_ADDRESS = 0x1E
|
MAG_ADDRESS = 0x1E
|
||||||
|
|
||||||
|
// i2C 8-bit subaddress (SUB): the 7 LSb represent the actual register address
|
||||||
|
// while the MSB enables address auto increment.
|
||||||
|
// If the MSb of the SUB field is 1, the SUB (register address) is
|
||||||
|
// automatically increased to allow multiple data read/writes.
|
||||||
|
ADDR_AUTO_INC_MASK = 0x80
|
||||||
|
|
||||||
// accelerometer registers.
|
// accelerometer registers.
|
||||||
ACCEL_WHO_AM_I = 0x0F
|
ACCEL_WHO_AM_I = 0x0F
|
||||||
ACCEL_CTRL_REG1_A = 0x20
|
ACCEL_CTRL_REG1_A = 0x20
|
||||||
ACCEL_CTRL_REG4_A = 0x23
|
ACCEL_CTRL_REG4_A = 0x23
|
||||||
ACCEL_OUT_X_L_A = 0x28
|
ACCEL_OUT_X_L_A = 0x28
|
||||||
ACCEL_OUT_X_H_A = 0x29
|
ACCEL_OUT_X_H_A = 0x29
|
||||||
ACCEL_OUT_Y_L_A = 0x2A
|
ACCEL_OUT_Y_L_A = 0x2A
|
||||||
ACCEL_OUT_Y_H_A = 0x2B
|
ACCEL_OUT_Y_H_A = 0x2B
|
||||||
ACCEL_OUT_Z_L_A = 0x2C
|
ACCEL_OUT_Z_L_A = 0x2C
|
||||||
ACCEL_OUT_Z_H_A = 0x2D
|
ACCEL_OUT_Z_H_A = 0x2D
|
||||||
|
ACCEL_OUT_AUTO_INC = ACCEL_OUT_X_L_A | ADDR_AUTO_INC_MASK
|
||||||
|
|
||||||
// magnetic sensor registers.
|
// magnetic sensor registers.
|
||||||
MAG_WHO_AM_I = 0x4F
|
MAG_WHO_AM_I = 0x4F
|
||||||
MAG_MR_REG_M = 0x60
|
MAG_MR_REG_M = 0x60
|
||||||
MAG_OUT_X_L_M = 0x68
|
MAG_OUT_X_L_M = 0x68
|
||||||
MAG_OUT_X_H_M = 0x69
|
MAG_OUT_X_H_M = 0x69
|
||||||
MAG_OUT_Y_L_M = 0x6A
|
MAG_OUT_Y_L_M = 0x6A
|
||||||
MAG_OUT_Y_H_M = 0x6B
|
MAG_OUT_Y_H_M = 0x6B
|
||||||
MAG_OUT_Z_L_M = 0x6C
|
MAG_OUT_Z_L_M = 0x6C
|
||||||
MAG_OUT_Z_H_M = 0x6D
|
MAG_OUT_Z_H_M = 0x6D
|
||||||
|
MAG_OUT_AUTO_INC = MAG_OUT_X_L_M | ADDR_AUTO_INC_MASK
|
||||||
|
|
||||||
// temperature sensor registers.
|
// temperature sensor registers.
|
||||||
TEMP_CFG_REG_A = 0x1F
|
TEMP_CFG_REG_A = 0x1F
|
||||||
OUT_TEMP_L_A = 0x0C
|
OUT_TEMP_L_A = 0x0C
|
||||||
OUT_TEMP_H_A = 0x0D
|
OUT_TEMP_H_A = 0x0D
|
||||||
|
OUT_TEMP_AUTO_INC = OUT_TEMP_L_A | ADDR_AUTO_INC_MASK
|
||||||
|
|
||||||
// accelerometer power mode.
|
// accelerometer power mode.
|
||||||
ACCEL_POWER_NORMAL = 0x00 // default
|
ACCEL_POWER_NORMAL = 0x00 // default
|
||||||
|
|||||||
@@ -8,9 +8,12 @@ package microbitmatrix // import "tinygo.org/x/drivers/microbitmatrix"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 4 rotation orientations (0, 90, 180, 270), CW (clock wise)
|
||||||
|
// 5 rows
|
||||||
|
// 5 cols
|
||||||
|
// target coordinates in machine rows (y) and cols (x)
|
||||||
var matrixRotations = [4][5][5][2]uint8{
|
var matrixRotations = [4][5][5][2]uint8{
|
||||||
{ // 0
|
{ // 0
|
||||||
{{0, 0}, {1, 3}, {0, 1}, {1, 4}, {0, 2}},
|
{{0, 0}, {1, 3}, {0, 1}, {1, 4}, {0, 2}},
|
||||||
@@ -19,7 +22,7 @@ var matrixRotations = [4][5][5][2]uint8{
|
|||||||
{{0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}},
|
{{0, 7}, {0, 6}, {0, 5}, {0, 4}, {0, 3}},
|
||||||
{{2, 2}, {1, 6}, {2, 0}, {1, 5}, {2, 1}},
|
{{2, 2}, {1, 6}, {2, 0}, {1, 5}, {2, 1}},
|
||||||
},
|
},
|
||||||
{ // 90 CCW
|
{ // 90 CW
|
||||||
{{0, 2}, {2, 7}, {1, 0}, {0, 3}, {2, 1}},
|
{{0, 2}, {2, 7}, {1, 0}, {0, 3}, {2, 1}},
|
||||||
{{1, 4}, {2, 6}, {2, 8}, {0, 4}, {1, 5}},
|
{{1, 4}, {2, 6}, {2, 8}, {0, 4}, {1, 5}},
|
||||||
{{0, 1}, {2, 5}, {1, 2}, {0, 5}, {2, 0}},
|
{{0, 1}, {2, 5}, {1, 2}, {0, 5}, {2, 0}},
|
||||||
@@ -42,71 +45,33 @@ var matrixRotations = [4][5][5][2]uint8{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ledRows = 3
|
||||||
|
ledCols = 9
|
||||||
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
pin [12]machine.Pin
|
pin [ledCols + ledRows]machine.Pin
|
||||||
buffer [3][9]bool
|
buffer [ledRows][ledCols]int8
|
||||||
rotation uint8
|
rotation uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the device.
|
func (d *Device) assignPins() {
|
||||||
func (d *Device) Configure(cfg Config) {
|
d.pin[0] = machine.LED_COL_1
|
||||||
d.SetRotation(cfg.Rotation)
|
d.pin[1] = machine.LED_COL_2
|
||||||
|
d.pin[2] = machine.LED_COL_3
|
||||||
|
d.pin[3] = machine.LED_COL_4
|
||||||
|
d.pin[4] = machine.LED_COL_5
|
||||||
|
d.pin[5] = machine.LED_COL_6
|
||||||
|
d.pin[6] = machine.LED_COL_7
|
||||||
|
d.pin[7] = machine.LED_COL_8
|
||||||
|
d.pin[8] = machine.LED_COL_9
|
||||||
|
|
||||||
for i := machine.LED_COL_1; i <= machine.LED_ROW_3; i++ {
|
d.pin[9] = machine.LED_ROW_1
|
||||||
d.pin[i-machine.LED_COL_1] = i
|
d.pin[10] = machine.LED_ROW_2
|
||||||
d.pin[i-machine.LED_COL_1].Configure(machine.PinConfig{Mode: machine.PinOutput})
|
d.pin[11] = machine.LED_ROW_3
|
||||||
}
|
|
||||||
d.ClearDisplay()
|
|
||||||
d.DisableAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display sends the buffer (if any) to the screen.
|
for i := 0; i < len(d.pin); i++ {
|
||||||
func (d *Device) Display() error {
|
d.pin[i].Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
for row := 0; row < 3; row++ {
|
|
||||||
d.DisableAll()
|
|
||||||
d.pin[9+row].High()
|
|
||||||
|
|
||||||
for col := 0; col < 9; col++ {
|
|
||||||
if d.buffer[row][col] {
|
|
||||||
d.pin[col].Low()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
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() {
|
|
||||||
for i := machine.LED_COL_1; i <= machine.LED_COL_9; i++ {
|
|
||||||
d.pin[i-machine.LED_COL_1].High()
|
|
||||||
}
|
|
||||||
for i := machine.LED_ROW_1; i <= machine.LED_ROW_3; i++ {
|
|
||||||
d.pin[i-machine.LED_COL_1].Low()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableAll enables all the LEDs without modifying the buffer
|
|
||||||
func (d *Device) EnableAll() {
|
|
||||||
for i := machine.LED_COL_1; i <= machine.LED_COL_9; i++ {
|
|
||||||
d.pin[i-machine.LED_COL_1].Low()
|
|
||||||
}
|
|
||||||
for i := machine.LED_ROW_1; i <= machine.LED_ROW_3; i++ {
|
|
||||||
d.pin[i-machine.LED_COL_1].High()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the current size of the display.
|
|
||||||
func (d *Device) Size() (w, h int16) {
|
|
||||||
return 5, 5
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,55 +3,60 @@
|
|||||||
|
|
||||||
// Package microbitmatrix implements a driver for the BBC micro:bit version 2 LED matrix.
|
// Package microbitmatrix implements a driver for the BBC micro:bit version 2 LED matrix.
|
||||||
//
|
//
|
||||||
// Schematic:
|
// Schematic: https://github.com/microbit-foundation/microbit-v2-hardware/blob/main/V2.00/MicroBit_V2.0.0_S_schematic.PDF
|
||||||
package microbitmatrix // import "tinygo.org/x/drivers/microbitmatrix"
|
package microbitmatrix // import "tinygo.org/x/drivers/microbitmatrix"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"machine"
|
"machine"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 4 rotation orientations (0, 90, 180, 270), CW (clock wise)
|
||||||
|
// 5 rows
|
||||||
|
// 5 cols
|
||||||
|
// target coordinates in machine rows (y) and cols (x)
|
||||||
var matrixRotations = [4][5][5][2]uint8{
|
var matrixRotations = [4][5][5][2]uint8{
|
||||||
{ // 0
|
{ // 0
|
||||||
{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}},
|
|
||||||
{{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}},
|
|
||||||
{{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}},
|
|
||||||
{{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}},
|
|
||||||
{{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}},
|
|
||||||
},
|
|
||||||
{ // 90 CCW
|
|
||||||
{{4, 0}, {4, 1}, {4, 2}, {4, 3}, {4, 4}},
|
|
||||||
{{3, 0}, {3, 1}, {3, 2}, {3, 3}, {3, 4}},
|
|
||||||
{{2, 0}, {2, 1}, {2, 2}, {2, 3}, {2, 4}},
|
|
||||||
{{1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4}},
|
|
||||||
{{0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}},
|
{{0, 0}, {0, 1}, {0, 2}, {0, 3}, {0, 4}},
|
||||||
|
{{1, 0}, {1, 1}, {1, 2}, {1, 3}, {1, 4}},
|
||||||
|
{{2, 0}, {2, 1}, {2, 2}, {2, 3}, {2, 4}},
|
||||||
|
{{3, 0}, {3, 1}, {3, 2}, {3, 3}, {3, 4}},
|
||||||
|
{{4, 0}, {4, 1}, {4, 2}, {4, 3}, {4, 4}},
|
||||||
|
},
|
||||||
|
{ // 90 CW
|
||||||
|
{{0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}},
|
||||||
|
{{0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3}},
|
||||||
|
{{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}},
|
||||||
|
{{0, 1}, {1, 1}, {2, 1}, {3, 1}, {4, 1}},
|
||||||
|
{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}},
|
||||||
},
|
},
|
||||||
{ // 180
|
{ // 180
|
||||||
{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}},
|
{{4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}},
|
||||||
{{4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}},
|
{{3, 4}, {3, 3}, {3, 2}, {3, 1}, {3, 0}},
|
||||||
{{4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}},
|
{{2, 4}, {2, 3}, {2, 2}, {2, 1}, {2, 0}},
|
||||||
{{4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}},
|
{{1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}},
|
||||||
{{4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}},
|
{{0, 4}, {0, 3}, {0, 2}, {0, 1}, {0, 0}},
|
||||||
},
|
},
|
||||||
{ // 270
|
{ // 270
|
||||||
{{0, 4}, {0, 3}, {0, 2}, {0, 1}, {0, 0}},
|
{{4, 0}, {3, 0}, {2, 0}, {1, 0}, {0, 0}},
|
||||||
{{1, 4}, {1, 3}, {1, 2}, {1, 1}, {1, 0}},
|
{{4, 1}, {3, 1}, {2, 1}, {1, 1}, {0, 1}},
|
||||||
{{2, 4}, {2, 3}, {2, 2}, {2, 1}, {2, 0}},
|
{{4, 2}, {3, 2}, {2, 2}, {1, 2}, {0, 2}},
|
||||||
{{3, 4}, {3, 3}, {3, 2}, {3, 1}, {3, 0}},
|
{{4, 3}, {3, 3}, {2, 3}, {1, 3}, {0, 3}},
|
||||||
{{4, 4}, {4, 3}, {4, 2}, {4, 1}, {4, 0}},
|
{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 4}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
ledRows = 5
|
||||||
|
ledCols = 5
|
||||||
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
pin [10]machine.Pin
|
pin [ledCols + ledRows]machine.Pin
|
||||||
buffer [5][5]bool
|
buffer [ledRows][ledCols]int8
|
||||||
rotation uint8
|
rotation uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the device.
|
func (d *Device) assignPins() {
|
||||||
func (d *Device) Configure(cfg Config) {
|
|
||||||
d.SetRotation(cfg.Rotation)
|
|
||||||
|
|
||||||
d.pin[0] = machine.LED_COL_1
|
d.pin[0] = machine.LED_COL_1
|
||||||
d.pin[1] = machine.LED_COL_2
|
d.pin[1] = machine.LED_COL_2
|
||||||
d.pin[2] = machine.LED_COL_3
|
d.pin[2] = machine.LED_COL_3
|
||||||
@@ -64,59 +69,7 @@ func (d *Device) Configure(cfg Config) {
|
|||||||
d.pin[8] = machine.LED_ROW_4
|
d.pin[8] = machine.LED_ROW_4
|
||||||
d.pin[9] = machine.LED_ROW_5
|
d.pin[9] = machine.LED_ROW_5
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < len(d.pin); i++ {
|
||||||
d.pin[i].Configure(machine.PinConfig{Mode: machine.PinOutput})
|
d.pin[i].Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||||
}
|
}
|
||||||
|
|
||||||
d.ClearDisplay()
|
|
||||||
d.DisableAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display sends the buffer (if any) to the screen.
|
|
||||||
func (d *Device) Display() error {
|
|
||||||
for x := 0; x < 5; x++ {
|
|
||||||
d.DisableAll()
|
|
||||||
d.pin[x].Low()
|
|
||||||
|
|
||||||
for y := 0; y < 5; y++ {
|
|
||||||
if d.buffer[x][y] {
|
|
||||||
d.pin[5+y].High()
|
|
||||||
} else {
|
|
||||||
d.pin[5+y].Low()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
time.Sleep(time.Millisecond * 4)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClearDisplay erases the internal buffer
|
|
||||||
func (d *Device) ClearDisplay() {
|
|
||||||
for row := 0; row < 5; row++ {
|
|
||||||
for col := 0; col < 5; col++ {
|
|
||||||
d.buffer[row][col] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableAll disables all the LEDs without modifying the buffer
|
|
||||||
func (d *Device) DisableAll() {
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
d.pin[i].High()
|
|
||||||
d.pin[5+i].Low()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableAll enables all the LEDs without modifying the buffer
|
|
||||||
func (d *Device) EnableAll() {
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
d.pin[i].Low()
|
|
||||||
d.pin[5+i].High()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns the current size of the display.
|
|
||||||
func (d *Device) Size() (w, h int16) {
|
|
||||||
return 5, 5
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,213 @@
|
|||||||
// 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 "tinygo.org/x/drivers/microbitmatrix"
|
package microbitmatrix // import "tinygo.org/x/drivers/microbitmatrix"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image/color"
|
"image/color"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
// Rotation of the LED matrix.
|
||||||
|
//
|
||||||
|
// Valid values:
|
||||||
|
//
|
||||||
|
// 0: regular orientation, (0 degree rotation)
|
||||||
|
// 1: 90 degree rotation clock wise
|
||||||
|
// 2: 180 degree rotation clock wise
|
||||||
|
// 3: 270 degree rotation clock wise
|
||||||
Rotation uint8
|
Rotation uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
RotationNormal = 0
|
||||||
|
Rotation90 = 1
|
||||||
|
Rotation180 = 2
|
||||||
|
Rotation270 = 3
|
||||||
|
)
|
||||||
|
|
||||||
// New returns a new microbitmatrix driver.
|
// New returns a new microbitmatrix driver.
|
||||||
func New() Device {
|
func New() Device {
|
||||||
return Device{}
|
return Device{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetRotation changes the rotation of the LED matrix
|
// Configure sets up the device.
|
||||||
|
func (d *Device) Configure(cfg Config) {
|
||||||
|
d.SetRotation(cfg.Rotation)
|
||||||
|
|
||||||
|
d.assignPins()
|
||||||
|
|
||||||
|
d.ClearDisplay()
|
||||||
|
d.DisableAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetRotation changes the rotation of the LED matrix.
|
||||||
|
//
|
||||||
|
// Valid values for rotation:
|
||||||
|
//
|
||||||
|
// 0: regular orientation, (0 degree rotation)
|
||||||
|
// 1: 90 degree rotation clock wise
|
||||||
|
// 2: 180 degree rotation clock wise
|
||||||
|
// 3: 270 degree rotation clock wise
|
||||||
func (d *Device) SetRotation(rotation uint8) {
|
func (d *Device) SetRotation(rotation uint8) {
|
||||||
d.rotation = rotation % 4
|
d.rotation = rotation % 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Source:
|
||||||
|
// https://github.com/bbcmicrobit/micropython/blob/1252f887ddc790676bf9314a136bd17650b9c36c/source/microbit/microbitdisplay.cpp#L282
|
||||||
|
var renderTimings = []time.Duration{
|
||||||
|
0, // Bright, Ticks Duration, Relative power
|
||||||
|
2, // 1, 2, 32µs, inf
|
||||||
|
2, // 2, 4, 64µs, 200%
|
||||||
|
4, // 3, 8, 128µs, 200%
|
||||||
|
7, // 4, 15, 240µs, 187%
|
||||||
|
13, // 5, 28, 448µs, 187%
|
||||||
|
25, // 6, 53, 848µs, 189%
|
||||||
|
49, // 7, 102, 1632µs, 192%
|
||||||
|
97, // 8, 199, 3184µs, 195%
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source:
|
||||||
|
// https://github.com/bbcmicrobit/micropython/blob/1252f887ddc790676bf9314a136bd17650b9c36c/source/microbit/microbitdisplay.cpp#L368
|
||||||
|
const tickDuration = 16 * time.Microsecond
|
||||||
|
|
||||||
|
const (
|
||||||
|
rowIdx = 0
|
||||||
|
colIdx = 1
|
||||||
|
)
|
||||||
|
|
||||||
// SetPixel modifies the internal buffer in a single pixel.
|
// SetPixel modifies the internal buffer in a single pixel.
|
||||||
|
//
|
||||||
|
// The alpha channel of the RGBA is used to control the brightness of the LED
|
||||||
|
// in 9 different levels.
|
||||||
|
//
|
||||||
|
// alpha channel, brightness level
|
||||||
|
// 0 - 27, 9 (no transparency = highest brightness)
|
||||||
|
// 28 - 55, 8
|
||||||
|
// 56 - 83, 7
|
||||||
|
// 84 - 111, 6
|
||||||
|
// 112 - 139, 5
|
||||||
|
// 140 - 167, 4
|
||||||
|
// 168 - 195, 3
|
||||||
|
// 196 - 223, 2
|
||||||
|
// 224 - 251, 1 (very high transparency = lowest brightness)
|
||||||
|
// 252 - 255, 0 (full transparency = off)
|
||||||
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
func (d *Device) SetPixel(x int16, y int16, c color.RGBA) {
|
||||||
if x < 0 || x >= 5 || y < 0 || y >= 5 {
|
if x < 0 || x >= 5 || y < 0 || y >= 5 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
col := x
|
||||||
|
row := y
|
||||||
if c.R != 0 || c.G != 0 || c.B != 0 {
|
if c.R != 0 || c.G != 0 || c.B != 0 {
|
||||||
d.buffer[matrixRotations[d.rotation][y][x][0]][matrixRotations[d.rotation][y][x][1]] = true
|
d.buffer[matrixRotations[d.rotation][row][col][rowIdx]][matrixRotations[d.rotation][row][col][colIdx]] = brightness(c.A)
|
||||||
} else {
|
} else {
|
||||||
d.buffer[matrixRotations[d.rotation][y][x][0]][matrixRotations[d.rotation][y][x][1]] = false
|
d.buffer[matrixRotations[d.rotation][row][col][rowIdx]][matrixRotations[d.rotation][row][col][colIdx]] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPixel returns if the specific pixels is enabled
|
const (
|
||||||
|
brightnessLevels = 9
|
||||||
|
brightnessDivider = int8(255 / brightnessLevels)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Brightness0 = color.RGBA{R: 0, G: 0, B: 0, A: 0}
|
||||||
|
Brightness1 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*1}
|
||||||
|
Brightness2 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*2}
|
||||||
|
Brightness3 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*3}
|
||||||
|
Brightness4 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*4}
|
||||||
|
Brightness5 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*5}
|
||||||
|
Brightness6 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*6}
|
||||||
|
Brightness7 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*7}
|
||||||
|
Brightness8 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*8}
|
||||||
|
Brightness9 = color.RGBA{R: 255, G: 255, B: 255, A: 255 - uint8(brightnessDivider)*9}
|
||||||
|
|
||||||
|
BrightnessOff = Brightness0
|
||||||
|
BrightnessFull = Brightness9
|
||||||
|
)
|
||||||
|
|
||||||
|
func brightness(alpha uint8) int8 {
|
||||||
|
return brightnessLevels - int8(alpha/uint8(brightnessDivider))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPixel returns if the specific pixels is enabled.
|
||||||
func (d *Device) GetPixel(x int16, y int16) bool {
|
func (d *Device) GetPixel(x int16, y int16) bool {
|
||||||
if x < 0 || x >= 5 || y < 0 || y >= 5 {
|
if x < 0 || x >= 5 || y < 0 || y >= 5 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return d.buffer[matrixRotations[d.rotation][y][x][0]][matrixRotations[d.rotation][y][x][1]]
|
col := x
|
||||||
|
row := y
|
||||||
|
return d.buffer[matrixRotations[d.rotation][row][col][rowIdx]][matrixRotations[d.rotation][row][col][colIdx]] > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayRefreshDelay = 8 * time.Millisecond
|
||||||
|
|
||||||
|
// Display sends the buffer (if any) to the screen.
|
||||||
|
func (d *Device) Display() error {
|
||||||
|
var displayBuffer [ledRows][ledCols]int8
|
||||||
|
for row := 0; row < ledRows; row++ {
|
||||||
|
for col := 0; col < ledCols; col++ {
|
||||||
|
displayBuffer[row][col] = d.buffer[row][col]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for row := 0; row < ledRows; row++ {
|
||||||
|
d.DisableAll()
|
||||||
|
d.pin[ledCols+row].High()
|
||||||
|
|
||||||
|
for col := 0; col < ledCols; col++ {
|
||||||
|
if displayBuffer[row][col] > 0 {
|
||||||
|
d.pin[col].Low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
then := time.Now()
|
||||||
|
var offset time.Duration = 0
|
||||||
|
for _, ticks := range renderTimings {
|
||||||
|
for time.Since(then).Nanoseconds() < int64(ticks*tickDuration+offset) {
|
||||||
|
time.Sleep(offset / 10)
|
||||||
|
}
|
||||||
|
offset += ticks + tickDuration
|
||||||
|
for col := 0; col < ledCols; col++ {
|
||||||
|
displayBuffer[row][col]--
|
||||||
|
if displayBuffer[row][col] <= 0 {
|
||||||
|
d.pin[col].High()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(displayRefreshDelay)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClearDisplay erases the internal buffer.
|
||||||
|
func (d *Device) ClearDisplay() {
|
||||||
|
for row := 0; row < ledRows; row++ {
|
||||||
|
for col := 0; col < ledCols; col++ {
|
||||||
|
d.buffer[row][col] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableAll disables all the LEDs without modifying the buffer.
|
||||||
|
func (d *Device) DisableAll() {
|
||||||
|
for i := 0; i < ledCols; i++ {
|
||||||
|
d.pin[i].High()
|
||||||
|
}
|
||||||
|
for i := 0; i < ledRows; i++ {
|
||||||
|
d.pin[ledCols+i].Low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableAll enables all the LEDs without modifying the buffer.
|
||||||
|
func (d *Device) EnableAll() {
|
||||||
|
for i := 0; i < ledCols; i++ {
|
||||||
|
d.pin[i].Low()
|
||||||
|
}
|
||||||
|
for i := 0; i < ledRows; i++ {
|
||||||
|
d.pin[ledCols+i].High()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the current size of the display.
|
||||||
|
func (d *Device) Size() (w, h int16) {
|
||||||
|
return 5, 5
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -31,8 +31,8 @@ func (d Device) Connected() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Configure sets up the device for communication.
|
// Configure sets up the device for communication.
|
||||||
func (d Device) Configure() {
|
func (d Device) Configure() error {
|
||||||
d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{0})
|
return d.bus.WriteRegister(uint8(d.Address), PWR_MGMT_1, []uint8{0})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadAcceleration reads the current acceleration from the device and returns
|
// ReadAcceleration reads the current acceleration from the device and returns
|
||||||
|
|||||||
+43
-1
@@ -2,6 +2,8 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,7 +87,9 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefaultClient is the default Client and is used by Get, Head, and Post.
|
// DefaultClient is the default Client and is used by Get, Head, and Post.
|
||||||
var DefaultClient = &Client{}
|
var DefaultClient = &Client{
|
||||||
|
Transport: DefaultTransport,
|
||||||
|
}
|
||||||
|
|
||||||
// RoundTripper is an interface representing the ability to execute a
|
// RoundTripper is an interface representing the ability to execute a
|
||||||
// single HTTP transaction, obtaining the Response for a given Request.
|
// single HTTP transaction, obtaining the Response for a given Request.
|
||||||
@@ -211,3 +215,41 @@ func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response,
|
|||||||
req.Header.Set("Content-Type", contentType)
|
req.Header.Set("Content-Type", contentType)
|
||||||
return c.Do(req)
|
return c.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostForm issues a POST to the specified URL, with data's keys and
|
||||||
|
// values URL-encoded as the request body.
|
||||||
|
//
|
||||||
|
// The Content-Type header is set to application/x-www-form-urlencoded.
|
||||||
|
// To set other headers, use NewRequest and DefaultClient.Do.
|
||||||
|
//
|
||||||
|
// When err is nil, resp always contains a non-nil resp.Body.
|
||||||
|
// Caller should close resp.Body when done reading from it.
|
||||||
|
//
|
||||||
|
// PostForm is a wrapper around DefaultClient.PostForm.
|
||||||
|
//
|
||||||
|
// See the Client.Do method documentation for details on how redirects
|
||||||
|
// are handled.
|
||||||
|
//
|
||||||
|
// To make a request with a specified context.Context, use NewRequestWithContext
|
||||||
|
// and DefaultClient.Do.
|
||||||
|
func PostForm(url string, data url.Values) (resp *Response, err error) {
|
||||||
|
return DefaultClient.PostForm(url, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostForm issues a POST to the specified URL,
|
||||||
|
// with data's keys and values URL-encoded as the request body.
|
||||||
|
//
|
||||||
|
// The Content-Type header is set to application/x-www-form-urlencoded.
|
||||||
|
// To set other headers, use NewRequest and Client.Do.
|
||||||
|
//
|
||||||
|
// When err is nil, resp always contains a non-nil resp.Body.
|
||||||
|
// Caller should close resp.Body when done reading from it.
|
||||||
|
//
|
||||||
|
// See the Client.Do method documentation for details on how redirects
|
||||||
|
// are handled.
|
||||||
|
//
|
||||||
|
// To make a request with a specified context.Context, use NewRequestWithContext
|
||||||
|
// and Client.Do.
|
||||||
|
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
|
||||||
|
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||||
|
}
|
||||||
|
|||||||
+29
-18
@@ -25,17 +25,39 @@ func (c *Client) Do(req *Request) (*Response, error) {
|
|||||||
req.AddCookie(cookie)
|
req.AddCookie(cookie)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res, err := c.Transport.RoundTrip(req)
|
||||||
|
|
||||||
|
if c.Jar != nil {
|
||||||
|
if rc := res.Cookies(); len(rc) > 0 {
|
||||||
|
c.Jar.SetCookies(req.URL, rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type Transport struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var DefaultTransport RoundTripper
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
DefaultTransport = &Transport{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transport) RoundTrip(req *Request) (*Response, error) {
|
||||||
switch req.URL.Scheme {
|
switch req.URL.Scheme {
|
||||||
case "http":
|
case "http":
|
||||||
return c.doHTTP(req)
|
return t.doHTTP(req)
|
||||||
case "https":
|
case "https":
|
||||||
return c.doHTTPS(req)
|
return t.doHTTPS(req)
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid schemer : %s", req.URL.Scheme)
|
return nil, fmt.Errorf("invalid schemer : %s", req.URL.Scheme)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doHTTP(req *Request) (*Response, error) {
|
func (t *Transport) doHTTP(req *Request) (*Response, error) {
|
||||||
// make TCP connection
|
// make TCP connection
|
||||||
ip := net.ParseIP(req.URL.Hostname())
|
ip := net.ParseIP(req.URL.Hostname())
|
||||||
port := 80
|
port := 80
|
||||||
@@ -106,10 +128,10 @@ func (c *Client) doHTTP(req *Request) (*Response, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doResp(conn, req)
|
return t.doResp(conn, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doHTTPS(req *Request) (*Response, error) {
|
func (t *Transport) doHTTPS(req *Request) (*Response, error) {
|
||||||
conn, err := tls.Dial("tcp", req.URL.Host, nil)
|
conn, err := tls.Dial("tcp", req.URL.Host, nil)
|
||||||
retry := 0
|
retry := 0
|
||||||
for ; err != nil; conn, err = tls.Dial("tcp", req.URL.Host, nil) {
|
for ; err != nil; conn, err = tls.Dial("tcp", req.URL.Host, nil) {
|
||||||
@@ -167,10 +189,10 @@ func (c *Client) doHTTPS(req *Request) (*Response, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.doResp(conn, req)
|
return t.doResp(conn, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) {
|
func (t *Transport) doResp(conn net.Conn, req *Request) (*Response, error) {
|
||||||
resp := &Response{
|
resp := &Response{
|
||||||
Header: map[string][]string{},
|
Header: map[string][]string{},
|
||||||
}
|
}
|
||||||
@@ -256,11 +278,6 @@ func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) {
|
|||||||
remain -= int64(ofs)
|
remain -= int64(ofs)
|
||||||
if remain <= 0 {
|
if remain <= 0 {
|
||||||
resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs]))
|
resp.Body = io.NopCloser(bytes.NewReader(buf[:ofs]))
|
||||||
if c.Jar != nil {
|
|
||||||
if rc := resp.Cookies(); len(rc) > 0 {
|
|
||||||
c.Jar.SetCookies(req.URL, rc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resp, conn.Close()
|
return resp, conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,11 +315,5 @@ func (c *Client) doResp(conn net.Conn, req *Request) (*Response, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Jar != nil {
|
|
||||||
if rc := resp.Cookies(); len(rc) > 0 {
|
|
||||||
c.Jar.SetCookies(req.URL, rc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, conn.Close()
|
return resp, conn.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
// Package qmi8658c provides a driver for the QMI8658C accelerometer and gyroscope
|
||||||
|
// made by QST Solutions.
|
||||||
|
//
|
||||||
|
// Datasheet:
|
||||||
|
// https://www.qstcorp.com/upload/pdf/202202/%EF%BC%88%E5%B7%B2%E4%BC%A0%EF%BC%89QMI8658C%20datasheet%20rev%200.9.pdf
|
||||||
|
package qmi8656c
|
||||||
|
|
||||||
|
import "tinygo.org/x/drivers"
|
||||||
|
|
||||||
|
// Device wraps the I2C connection to the QMIC8658 sensor
|
||||||
|
type Device struct {
|
||||||
|
bus drivers.I2C
|
||||||
|
Address uint16
|
||||||
|
AccLsbDiv uint16
|
||||||
|
GyroLsbDiv uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
// SPI Config
|
||||||
|
SPIMode byte // One of SPI_X_WIRE
|
||||||
|
SPIEndian byte // One of SPI_XXX_ENDIAN
|
||||||
|
SPIAutoInc byte // One of SPI_NOT_AUTO_INC or SPI_AUTO_INC
|
||||||
|
// Accelerometer
|
||||||
|
AccEnable byte // One of ACC_ENABLE or ACC_DISABLE
|
||||||
|
AccScale byte // One of ACC_XG
|
||||||
|
AccRate byte // One of ACC_XX_YYHZ
|
||||||
|
AccLowPass byte // One of ACC_LOW_PASS_X
|
||||||
|
// Gyro
|
||||||
|
GyroEnable byte // One of GYRO_X_ENABLE or GYRO_DISABLE
|
||||||
|
GyroScale byte // One of GYRO_XDPS
|
||||||
|
GyroRate byte // One of GYRO_X_YHZ
|
||||||
|
GyroLowPass byte // One of GYRO_LOW_PASS_X
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new device with the I2C passed, correct address and nil values for
|
||||||
|
// AccLsbDiv and GyroLsbDiv, which will be corrected based on the config.
|
||||||
|
func New(bus drivers.I2C) Device {
|
||||||
|
return Device{
|
||||||
|
bus,
|
||||||
|
Address,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the device is connected by calling WHO_AM_I and checking the
|
||||||
|
// default identifier.
|
||||||
|
func (d *Device) Connected() bool {
|
||||||
|
data := []byte{0}
|
||||||
|
d.ReadRegister(WHO_AM_I, data)
|
||||||
|
return data[0] == IDENTIFIER
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a basic default configuration that works with the "WaveShare RP2040
|
||||||
|
// Round LCD 1.28in".
|
||||||
|
func DefaultConfig() (cfg Config) {
|
||||||
|
return Config{
|
||||||
|
SPIMode: SPI_4_WIRE,
|
||||||
|
SPIEndian: SPI_BIG_ENDIAN,
|
||||||
|
SPIAutoInc: SPI_AUTO_INC,
|
||||||
|
AccEnable: ACC_ENABLE,
|
||||||
|
AccScale: ACC_8G,
|
||||||
|
AccRate: ACC_NORMAL_1000HZ,
|
||||||
|
AccLowPass: ACC_LOW_PASS_2_62,
|
||||||
|
GyroEnable: GYRO_FULL_ENABLE,
|
||||||
|
GyroScale: GYRO_512DPS,
|
||||||
|
GyroRate: GYRO_1000HZ,
|
||||||
|
GyroLowPass: GYRO_LOW_PASS_2_62,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user has defined a desired configuration, if not uses the
|
||||||
|
// DefaultConfig, then defines the AccLsbDiv and GyroLsbDiv based on the
|
||||||
|
// configurations and, finally, send the commands and configure the IMU.
|
||||||
|
func (d *Device) Configure(cfg Config) {
|
||||||
|
if cfg == (Config{}) {
|
||||||
|
cfg = DefaultConfig()
|
||||||
|
}
|
||||||
|
var val uint16
|
||||||
|
// Setting accelerometer LSB
|
||||||
|
switch cfg.AccScale {
|
||||||
|
case ACC_2G:
|
||||||
|
d.AccLsbDiv = 1 << 14
|
||||||
|
case ACC_4G:
|
||||||
|
d.AccLsbDiv = 1 << 13
|
||||||
|
case ACC_8G:
|
||||||
|
d.AccLsbDiv = 1 << 12
|
||||||
|
case ACC_16G:
|
||||||
|
d.AccLsbDiv = 1 << 11
|
||||||
|
default:
|
||||||
|
d.AccLsbDiv = 1 << 12
|
||||||
|
}
|
||||||
|
// Setting gyro LSB
|
||||||
|
switch cfg.GyroScale {
|
||||||
|
case GYRO_16DPS:
|
||||||
|
d.GyroLsbDiv = 2048
|
||||||
|
case GYRO_32DPS:
|
||||||
|
d.GyroLsbDiv = 1024
|
||||||
|
case GYRO_64DPS:
|
||||||
|
d.GyroLsbDiv = 512
|
||||||
|
case GYRO_128DPS:
|
||||||
|
d.GyroLsbDiv = 256
|
||||||
|
case GYRO_256DPS:
|
||||||
|
d.GyroLsbDiv = 128
|
||||||
|
case GYRO_512DPS:
|
||||||
|
d.GyroLsbDiv = 64
|
||||||
|
case GYRO_1024DPS:
|
||||||
|
d.GyroLsbDiv = 32
|
||||||
|
case GYRO_2048DPS:
|
||||||
|
d.GyroLsbDiv = 16
|
||||||
|
default:
|
||||||
|
d.GyroLsbDiv = 64
|
||||||
|
}
|
||||||
|
// SPI Modes
|
||||||
|
val = uint16((cfg.SPIMode | cfg.SPIEndian | cfg.SPIAutoInc))
|
||||||
|
d.WriteRegister(CTRL1, val)
|
||||||
|
// Accelerometer config
|
||||||
|
val = uint16(cfg.AccScale | cfg.AccRate)
|
||||||
|
d.WriteRegister(CTRL2, val)
|
||||||
|
// Gyro config
|
||||||
|
val = uint16(cfg.GyroScale | cfg.GyroRate)
|
||||||
|
d.WriteRegister(CTRL3, val)
|
||||||
|
// Sensor DSP config
|
||||||
|
val = uint16(cfg.GyroLowPass | cfg.AccLowPass)
|
||||||
|
d.WriteRegister(CTRL5, val)
|
||||||
|
// Sensors config
|
||||||
|
val = uint16(cfg.GyroEnable | cfg.AccEnable)
|
||||||
|
d.WriteRegister(CTRL7, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the acceleration from the sensor, the values returned are in mg
|
||||||
|
// (milli gravity), which means that 1000 = 1g.
|
||||||
|
func (d *Device) ReadAcceleration() (x int32, y int32, z int32) {
|
||||||
|
data := make([]byte, 6)
|
||||||
|
raw := make([]int32, 3)
|
||||||
|
d.ReadRegister(ACC_XOUT_L, data)
|
||||||
|
for i := range raw {
|
||||||
|
raw[i] = int32(uint16(data[(2*i+1)])<<8 | uint16(data[i]))
|
||||||
|
if raw[i] >= 32767 {
|
||||||
|
raw[i] = raw[i] - 65535
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = -raw[0] * 1000 / int32(d.AccLsbDiv)
|
||||||
|
y = -raw[1] * 1000 / int32(d.AccLsbDiv)
|
||||||
|
z = -raw[2] * 1000 / int32(d.AccLsbDiv)
|
||||||
|
return x, y, z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the rotation from the sensor, the values returned are in mdeg/sec
|
||||||
|
// (milli degress/second), which means that a full rotation is 360000.
|
||||||
|
func (d *Device) ReadRotation() (x int32, y int32, z int32) {
|
||||||
|
data := make([]byte, 6)
|
||||||
|
raw := make([]int32, 3)
|
||||||
|
d.ReadRegister(GYRO_XOUT_L, data)
|
||||||
|
for i := range raw {
|
||||||
|
raw[i] = int32(uint16(data[(2*i+1)])<<8 | uint16(data[i]))
|
||||||
|
if raw[i] >= 32767 {
|
||||||
|
raw[i] = raw[i] - 65535
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = raw[0] * 1000 / int32(d.GyroLsbDiv)
|
||||||
|
y = raw[1] * 1000 / int32(d.GyroLsbDiv)
|
||||||
|
z = raw[2] * 1000 / int32(d.GyroLsbDiv)
|
||||||
|
return x, y, z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the temperature from the sensor, the values returned are in
|
||||||
|
// millidegrees Celsius.
|
||||||
|
func (d *Device) ReadTemperature() (int32, error) {
|
||||||
|
data := make([]byte, 2)
|
||||||
|
err := d.ReadRegister(TEMP_OUT_L, data)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
raw := uint16(data[1])<<8 | uint16(data[0])
|
||||||
|
t := int32(raw) * 1000 / 256
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method to read the register and avoid repetition.
|
||||||
|
func (d *Device) ReadRegister(reg uint8, buf []byte) error {
|
||||||
|
return d.bus.ReadRegister(uint8(d.Address), reg, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method to write the register and avoid repetition.
|
||||||
|
func (d *Device) WriteRegister(reg uint8, v uint16) error {
|
||||||
|
data := []byte{byte(v)}
|
||||||
|
err := d.bus.WriteRegister(uint8(d.Address), reg, data)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package qmi8656c
|
||||||
|
|
||||||
|
// The I2C address that the sensor listens to.
|
||||||
|
const Address = 0x6B
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Who am I
|
||||||
|
WHO_AM_I = 0x00
|
||||||
|
IDENTIFIER = 0x05
|
||||||
|
|
||||||
|
// Configuration registers
|
||||||
|
CTRL1 = 0x02 // SPI Modes
|
||||||
|
CTRL2 = 0x03 // Accelerometer config
|
||||||
|
CTRL3 = 0x04 // Gyro config
|
||||||
|
CTRL4 = 0x05 // Magnetometer config (ignored)
|
||||||
|
CTRL5 = 0x06 // Sensor DSP config
|
||||||
|
CTRL6 = 0x07 // Motion on Demand (ignored)
|
||||||
|
CTRL7 = 0x08 // Sensors config
|
||||||
|
|
||||||
|
// Interface config (CTRL1)
|
||||||
|
SPI_4_WIRE = 0x00
|
||||||
|
SPI_3_WIRE = 0x80
|
||||||
|
SPI_NOT_AUTO_INC = 0x00
|
||||||
|
SPI_AUTO_INC = 0x40
|
||||||
|
SPI_LITTLE_ENDIAN = 0x00
|
||||||
|
SPI_BIG_ENDIAN = 0x20
|
||||||
|
|
||||||
|
// Accelerometer scale config (CTRL2-H)
|
||||||
|
ACC_SELF_TEST = 0x80
|
||||||
|
|
||||||
|
// Accelerometer scale config (CTRL2-H)
|
||||||
|
ACC_2G = 0x00
|
||||||
|
ACC_4G = 0x10
|
||||||
|
ACC_8G = 0x20
|
||||||
|
ACC_16G = 0x30
|
||||||
|
|
||||||
|
// Accelerometer output data rate (ODR) config (CTRL2-L)
|
||||||
|
ACC_NORMAL_8000HZ = 0x00
|
||||||
|
ACC_NORMAL_4000HZ = 0x01
|
||||||
|
ACC_NORMAL_2000HZ = 0x02
|
||||||
|
ACC_NORMAL_1000HZ = 0x03
|
||||||
|
ACC_NORMAL_500HZ = 0x04
|
||||||
|
ACC_NORMAL_250HZ = 0x05
|
||||||
|
ACC_NORMAL_125HZ = 0x06
|
||||||
|
ACC_NORMAL_62HZ = 0x07
|
||||||
|
ACC_NORMAL_31HZ = 0x08
|
||||||
|
ACC_LOW_POWER_128HZ = 0x0C
|
||||||
|
ACC_LOW_POWER_21HZ = 0x0D
|
||||||
|
ACC_LOW_POWER_11HZ = 0x0E
|
||||||
|
ACC_LOW_POWER_3HZ = 0x0F
|
||||||
|
|
||||||
|
// Gyro scale config (CTRL3-H)
|
||||||
|
GYRO_SELF_TEST = 0x80
|
||||||
|
|
||||||
|
// Gyro scale config (CTRL3-H)
|
||||||
|
GYRO_16DPS = 0x00
|
||||||
|
GYRO_32DPS = 0x10
|
||||||
|
GYRO_64DPS = 0x20
|
||||||
|
GYRO_128DPS = 0x30
|
||||||
|
GYRO_256DPS = 0x40
|
||||||
|
GYRO_512DPS = 0x50
|
||||||
|
GYRO_1024DPS = 0x60
|
||||||
|
GYRO_2048DPS = 0x70
|
||||||
|
|
||||||
|
// Gyro output data rate (ODR) config (CTRL3-L)
|
||||||
|
GYRO_8000HZ = 0x00
|
||||||
|
GYRO_4000HZ = 0x01
|
||||||
|
GYRO_2000HZ = 0x02
|
||||||
|
GYRO_1000HZ = 0x03
|
||||||
|
GYRO_500HZ = 0x04
|
||||||
|
GYRO_250HZ = 0x05
|
||||||
|
GYRO_125HZ = 0x06
|
||||||
|
GYRO_62HZ = 0x07
|
||||||
|
GYRO_31HZ = 0x08
|
||||||
|
|
||||||
|
// Gyro DSP config (CTRL4-H)
|
||||||
|
GYRO_LOW_PASS_OFF = 0x00 // Disabled
|
||||||
|
GYRO_LOW_PASS_2_62 = 0x10 // 2.62% of output data rate (ODR)
|
||||||
|
GYRO_LOW_PASS_3_59 = 0x30 // 3.59% of output data rate (ODR)
|
||||||
|
GYRO_LOW_PASS_5_32 = 0x50 // 5.32% of output data rate (ODR)
|
||||||
|
GYRO_LOW_PASS_14 = 0x70 // 14% of output data rate (ODR)
|
||||||
|
|
||||||
|
// Accelerometer DSP config (CTRL4-L)
|
||||||
|
ACC_LOW_PASS_OFF = 0x00 // Disabled
|
||||||
|
ACC_LOW_PASS_2_62 = 0x01 // 2.62% of output data rate (ODR)
|
||||||
|
ACC_LOW_PASS_3_59 = 0x03 // 3.59% of output data rate (ODR)
|
||||||
|
ACC_LOW_PASS_5_32 = 0x05 // 5.32% of output data rate (ODR)
|
||||||
|
ACC_LOW_PASS_14 = 0x07 // 14% of output data rate (ODR)
|
||||||
|
|
||||||
|
// Motion on demand (MOD) (CTRL6)
|
||||||
|
MOD_DISABLE = 0x00
|
||||||
|
MOD_ENABLE = 0x80
|
||||||
|
|
||||||
|
// Enable sensors (CTRL7)
|
||||||
|
GYRO_DISABLE = 0x00
|
||||||
|
GYRO_FULL_ENABLE = 0x02
|
||||||
|
GYRO_SNOOZE_ENABLE = 0x12
|
||||||
|
ACC_DISABLE = 0x00
|
||||||
|
ACC_ENABLE = 0x01
|
||||||
|
|
||||||
|
// Timestamp Outputs Register Adresses
|
||||||
|
TIMESTAMP_OUT_L = 0x30
|
||||||
|
TIMESTAMP_OUT_M = 0x31
|
||||||
|
TIMESTAMP_OUT_H = 0x32
|
||||||
|
|
||||||
|
// Temperature Outputs Register Adresses
|
||||||
|
TEMP_OUT_L = 0x33
|
||||||
|
TEMP_OUT_H = 0x34
|
||||||
|
|
||||||
|
// Acceleration Outputs Register Adresses
|
||||||
|
ACC_XOUT_L = 0x35
|
||||||
|
ACC_XOUT_H = 0x36
|
||||||
|
ACC_YOUT_L = 0x37
|
||||||
|
ACC_YOUT_H = 0x38
|
||||||
|
ACC_ZOUT_L = 0x39
|
||||||
|
ACC_ZOUT_H = 0x3A
|
||||||
|
|
||||||
|
// Angular Rate Outputs Register Adresses
|
||||||
|
GYRO_XOUT_L = 0x3B
|
||||||
|
GYRO_XOUT_H = 0x3C
|
||||||
|
GYRO_YOUT_L = 0x3D
|
||||||
|
GYRO_YOUT_H = 0x3E
|
||||||
|
GYRO_ZOUT_L = 0x3F
|
||||||
|
GYRO_ZOUT_H = 0x40
|
||||||
|
|
||||||
|
// Quaternion Outputs Register Adresses
|
||||||
|
DELTA_QUAT_WOUT_L = 0x49
|
||||||
|
DELTA_QUAT_WOUT_H = 0x4A
|
||||||
|
DELTA_QUAT_XOUT_L = 0x4B
|
||||||
|
DELTA_QUAT_XOUT_H = 0x4C
|
||||||
|
DELTA_QUAT_YOUT_L = 0x4D
|
||||||
|
DELTA_QUAT_YOUT_H = 0x4E
|
||||||
|
DELTA_QUAT_ZOUT_L = 0x4F
|
||||||
|
DELTA_QUAT_ZOUT_H = 0x50
|
||||||
|
|
||||||
|
// Delta Velocity Outputs Register Adresses
|
||||||
|
DELTA_VEL_XOUT_L = 0x51
|
||||||
|
DELTA_VEL_XOUT_H = 0x52
|
||||||
|
DELTA_VEL_YOUT_L = 0x53
|
||||||
|
DELTA_VEL_YOUT_H = 0x54
|
||||||
|
DELTA_VEL_ZOUT_L = 0x55
|
||||||
|
DELTA_VEL_ZOUT_H = 0x56
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user