mirror of
https://github.com/tinygo-org/drivers.git
synced 2026-07-27 19:18:41 +00:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c59a3ff90 | |||
| 915ca320de | |||
| 5645eb3f91 | |||
| 6b914f7af7 | |||
| 5a02fe068b | |||
| 5ecefde991 | |||
| 9adbde99a2 | |||
| a09ec31e7a | |||
| d07ad40373 | |||
| aa17ffd1cb | |||
| ce03bebc34 | |||
| dfd652ca1f | |||
| c765ef3970 | |||
| 428db3cd12 | |||
| d7f619ca21 | |||
| d1ace44754 | |||
| 6b58fdc95a | |||
| bd2530abee | |||
| 3bcde1485c | |||
| 351700e48d | |||
| cbcb62af01 | |||
| 19bb773e5b | |||
| 69cc0b22c4 | |||
| 2a48b5c25d | |||
| e6a822f68b | |||
| f65bce6d36 | |||
| 67b8a341a6 | |||
| b869d27170 | |||
| 107932a201 | |||
| d2db0c28e2 | |||
| 761bcfc4db | |||
| 91dadd5535 | |||
| f7dc106fc8 | |||
| df343190c2 | |||
| 6bc466f79b | |||
| 23776bf906 | |||
| e77e9249cd | |||
| 1345bc2161 | |||
| ab795cc186 | |||
| 36213a1cdc | |||
| 963c903d71 | |||
| 2cec480fb8 | |||
| 68da7f437b | |||
| d0632fccfa | |||
| 2c0a74df4d | |||
| c6228a0677 | |||
| 33ec7fc98f | |||
| 75c15d5d0b | |||
| e1deb45aac | |||
| d1cafbc2b2 | |||
| 0b6bfda8cf | |||
| 84408279de | |||
| de1e6a626a | |||
| 7531c9d334 | |||
| ee44900c25 | |||
| b9a70aeb6f | |||
| cad0a320b7 | |||
| a53a39922b | |||
| 41d6a4c3fa | |||
| 60ba0e3b30 | |||
| 2b5f43029c | |||
| a771641339 | |||
| 5741ceb9d1 |
@@ -1 +1,2 @@
|
||||
build
|
||||
.vscode/
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
0.16.0
|
||||
---
|
||||
- **new devices**
|
||||
- aht20: add device
|
||||
- ina260: add new i2c device
|
||||
- keypad: add 4x4 keypad driver (#226)
|
||||
- max7219: add driver support
|
||||
- mcp2515: add support for mcp2515 CAN device
|
||||
- p1am: support the P1AM-100 hardware watchdog
|
||||
- pcf8563: add support for pcf8563 real time clock
|
||||
- servo: add driver using PWM
|
||||
- tm1637: add support for tm1637 7-segment LED
|
||||
- tone: add package for producing tones using the PWM interface
|
||||
- **enhancements**
|
||||
- pwm: update drivers with PWM to use new interface
|
||||
- wifinina: Make TLS work over WiFiNINA Verified on Arduino Nano33 IoT and nina fw v1.4.5
|
||||
- ssd1306: Enable reset screen for SSD1306 via I2C
|
||||
- st7789: add scrolling functions to match st7735
|
||||
- **bugfixes**
|
||||
- wifinina:
|
||||
- fix getMACAddress and getTime
|
||||
- fix println + cleanup
|
||||
- remove debug flag and remove unnecessary padding call
|
||||
- fix padding and implement missing functions
|
||||
- flash: fix EraseBlocks method which is erasing sectors instead
|
||||
- **core**
|
||||
- all: use interfaces for UART objects
|
||||
- all: do not take the pointer of an I2C object
|
||||
- adc: update drivers with ADC to use new config struct
|
||||
- **testing**
|
||||
- tester:
|
||||
- add a mock for command-oriented i2c devices
|
||||
- add 16-bit register mock device
|
||||
|
||||
- **docs**
|
||||
- ssd1306: example of ssd1306 with 128x64 display over I2C
|
||||
- wifinina:
|
||||
- add information about Adafruit boards with ESP32 wifi coprocessors, and modify examples to remove code that was both not being used, and also prevented many Adafruit boards from being able to be targeted by the examples
|
||||
- update docs to simplify the nina-fw update process
|
||||
- example that connects to AP and prints ip addresses, time and mac
|
||||
- p1am: documentation and example program
|
||||
- add missing new drivers added since last release
|
||||
|
||||
0.15.0
|
||||
---
|
||||
- **new devices**
|
||||
|
||||
@@ -89,16 +89,22 @@ smoke-test:
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp3008/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mcp2515/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/microbitmatrix/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mma8653/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/mpu6050/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=p1am-100 ./examples/p1am/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setbuffer/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/pcd8544/setpixel/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/servo
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/shifter/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=microbit ./examples/sht3x/main.go
|
||||
@@ -115,6 +121,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=circuitplay-bluefruit ./examples/tone
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/tm1637/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
|
||||
@@ -165,13 +175,34 @@ endif
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=circuitplay-express ./examples/lis2mdl/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino-nano33 ./examples/max72xx/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/dht/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=arduino ./examples/keypad4x4/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/alarm/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/clkout/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/time/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=xiao ./examples/pcf8563/timer/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m0 ./examples/ina260/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=nucleo-l432kc ./examples/aht20/main.go
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/console/
|
||||
@md5sum ./build/test.hex
|
||||
tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/sdcard/tinyfs/
|
||||
@md5sum ./build/test.hex
|
||||
|
||||
DRIVERS = $(wildcard */)
|
||||
NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \
|
||||
hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht
|
||||
hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \
|
||||
pcf8563 mcp2515 servo sdcard
|
||||
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
|
||||
|
||||
unit-test:
|
||||
|
||||
@@ -52,12 +52,13 @@ func main() {
|
||||
|
||||
## Currently supported devices
|
||||
|
||||
The following 56 devices are supported.
|
||||
The following 66 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 |
|
||||
| [AHT20 I2C Temperature and Humidity Sensor](http://www.aosong.com/userfiles/files/media/AHT20%20%E8%8B%B1%E6%96%87%E7%89%88%E8%AF%B4%E6%98%8E%E4%B9%A6%20A0%2020201222.pdf) | I2C |
|
||||
| [AMG88xx 8x8 Thermal camera sensor](https://cdn-learn.adafruit.com/assets/assets/000/043/261/original/Grid-EYE_SPECIFICATIONS%28Reference%29.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 |
|
||||
@@ -80,24 +81,32 @@ The following 56 devices are supported.
|
||||
| [HD44780 LCD controller](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) | GPIO/I2C |
|
||||
| [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 |
|
||||
| [INA260 Volt/Amp/Power meter](https://www.ti.com/lit/ds/symlink/ina260.pdf) | I2C |
|
||||
| [4x4 Membrane Keypad](https://cdn.sparkfun.com/assets/f/f/a/5/0/DS-16038.pdf) | GPIO |
|
||||
| [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 |
|
||||
| [LIS2MDL magnetometer](https://www.st.com/resource/en/datasheet/lis2mdl.pdf) | I2C |
|
||||
| [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 |
|
||||
| [MAX7219 & MAX7221 display driver](https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf) | SPI |
|
||||
| [MCP23017 port expander](https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf) | I2C |
|
||||
| [MCP3008 analog to digital converter (ADC)](http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf) | SPI |
|
||||
| [MCP2515 Stand-Alone CAN Controller with SPI Interface](https://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Family-Data-Sheet-DS20001801K.pdf) | SPI |
|
||||
| [Microphone - PDM](https://cdn-learn.adafruit.com/assets/assets/000/049/977/original/MP34DT01-M.pdf) | I2S/PDM |
|
||||
| [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 |
|
||||
| [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 |
|
||||
| [PCF8563 real time clock](https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf) | I2C |
|
||||
| [Resistive Touchscreen (4-wire)](http://ww1.microchip.com/downloads/en/Appnotes/doc8091.pdf) | GPIO |
|
||||
| [Semihosting](https://wiki.segger.com/Semihosting) | Debug |
|
||||
| [Servo](https://learn.sparkfun.com/tutorials/hobby-servo-tutorial/all) | PWM |
|
||||
| [Shift register (PISO)](https://en.wikipedia.org/wiki/Shift_register#Parallel-in_serial-out_\(PISO\)) | GPIO |
|
||||
| [Shift registers (SIPO)](https://en.wikipedia.org/wiki/Shift_register#Serial-in_parallel-out_(SIPO)) | 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 |
|
||||
| [SPI NOR Flash Memory](https://en.wikipedia.org/wiki/Flash_memory#NOR_flash) | SPI/QSPI |
|
||||
| [SPI SDCARD/MMC](https://en.wikipedia.org/wiki/SD_card) | SPI |
|
||||
| [SSD1306 OLED display](https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf) | I2C / SPI |
|
||||
| [SSD1331 TFT color display](https://www.crystalfontz.com/controllers/SolomonSystech/SSD1331/381/) | SPI |
|
||||
| [SSD1351 OLED display](https://download.mikroe.com/documents/datasheets/ssd1351-revision-1.3.pdf) | SPI |
|
||||
@@ -105,6 +114,7 @@ The following 56 devices are supported.
|
||||
| [ST7789 TFT color display](https://cdn-shop.adafruit.com/product-files/3787/3787_tft_QT154H2201__________20190228182902.pdf) | SPI |
|
||||
| [Stepper motor "Easystepper" controller](https://en.wikipedia.org/wiki/Stepper_motor) | GPIO |
|
||||
| [Thermistor](https://www.farnell.com/datasheets/33552.pdf) | ADC |
|
||||
| [TM1637 7-segment LED display](https://www.mcielectronics.cl/website_MCI/static/documents/Datasheet_TM1637.pdf) | I2C |
|
||||
| [TMP102 I2C Temperature Sensor](https://download.mikroe.com/documents/datasheets/tmp102-data-sheet.pdf) | I2C |
|
||||
| [VEML6070 UV light sensor](https://www.vishay.com/docs/84277/veml6070.pdf) | I2C |
|
||||
| [VL53L1X time-of-flight distance sensor](https://www.st.com/resource/en/datasheet/vl53l1x.pdf) | I2C |
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package aht20
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to an AHT20 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
humidity uint32
|
||||
temp uint32
|
||||
}
|
||||
|
||||
// New creates a new AHT20 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: Address,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the device
|
||||
func (d *Device) Configure() {
|
||||
// Check initialization state
|
||||
status := d.Status()
|
||||
if status&0x08 == 1 {
|
||||
// Device is initialized
|
||||
return
|
||||
}
|
||||
|
||||
// Force initialization
|
||||
d.bus.Tx(d.Address, []byte{CMD_INITIALIZE, 0x08, 0x00}, nil)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Reset the device
|
||||
func (d *Device) Reset() {
|
||||
d.bus.Tx(d.Address, []byte{CMD_SOFTRESET}, nil)
|
||||
}
|
||||
|
||||
// Status of the device
|
||||
func (d *Device) Status() byte {
|
||||
data := []byte{0}
|
||||
|
||||
d.bus.Tx(d.Address, []byte{CMD_STATUS}, data)
|
||||
|
||||
return data[0]
|
||||
}
|
||||
|
||||
// Read the temperature and humidity
|
||||
//
|
||||
// The actual temperature and humidity are stored
|
||||
// and can be accessed using `Temp` and `Humidity`.
|
||||
func (d *Device) Read() error {
|
||||
d.bus.Tx(d.Address, []byte{CMD_TRIGGER, 0x33, 0x00}, nil)
|
||||
|
||||
data := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
for retry := 0; retry < 3; retry++ {
|
||||
time.Sleep(80 * time.Millisecond)
|
||||
err := d.bus.Tx(d.Address, nil, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If measurement complete, store values
|
||||
if data[0]&0x04 != 0 && data[0]&0x80 == 0 {
|
||||
d.humidity = uint32(data[1])<<12 | uint32(data[2])<<4 | uint32(data[3])>>4
|
||||
d.temp = (uint32(data[3])&0xF)<<16 | uint32(data[4])<<8 | uint32(data[5])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return ErrTimeout
|
||||
}
|
||||
|
||||
func (d *Device) RawHumidity() uint32 {
|
||||
return d.humidity
|
||||
}
|
||||
|
||||
func (d *Device) RawTemp() uint32 {
|
||||
return d.temp
|
||||
}
|
||||
|
||||
func (d *Device) RelHumidity() float32 {
|
||||
return (float32(d.humidity) * 100) / 0x100000
|
||||
}
|
||||
|
||||
func (d *Device) DeciRelHumidity() int32 {
|
||||
return (int32(d.humidity) * 1000) / 0x100000
|
||||
}
|
||||
|
||||
// Temperature in degrees celsius
|
||||
func (d *Device) Celsius() float32 {
|
||||
return (float32(d.temp*200.0) / 0x100000) - 50
|
||||
}
|
||||
|
||||
// Temperature in mutiples of one tenth of a degree celsius
|
||||
//
|
||||
// Using this method avoids floating point calculations.
|
||||
func (d *Device) DeciCelsius() int32 {
|
||||
return ((int32(d.temp) * 2000) / 0x100000) - 500
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package aht20
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"tinygo.org/x/drivers/tester"
|
||||
)
|
||||
|
||||
func TestDefaultI2CAddress(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
dev := New(bus)
|
||||
c.Assert(uint8(dev.Address), qt.Equals, uint8(Address))
|
||||
}
|
||||
|
||||
func TestInitialization(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fdev := tester.NewI2CDeviceCmd(c, Address)
|
||||
fdev.Commands = defaultCommands()
|
||||
bus.AddDevice(fdev)
|
||||
|
||||
// Set status to uninitialized to force initialization
|
||||
fdev.Commands[CMD_STATUS].Response[0] = 0x0C
|
||||
|
||||
dev := New(bus)
|
||||
dev.Configure()
|
||||
|
||||
// Check initialization command invoked
|
||||
c.Assert(fdev.Commands[CMD_INITIALIZE].Invocations > 0, qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestRead(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fdev := tester.NewI2CDeviceCmd(c, Address)
|
||||
fdev.Commands = defaultCommands()
|
||||
bus.AddDevice(fdev)
|
||||
|
||||
dev := New(bus)
|
||||
dev.Read()
|
||||
|
||||
// Should be 25deg (250 decidegrees)
|
||||
c.Assert(dev.DeciCelsius(), qt.Equals, int32(250))
|
||||
|
||||
// Should be 36.3% (363 decipercent)
|
||||
c.Assert(dev.DeciRelHumidity(), qt.Equals, int32(363))
|
||||
}
|
||||
|
||||
func defaultCommands() map[uint8]*tester.Cmd {
|
||||
return map[uint8]*tester.Cmd{
|
||||
CMD_INITIALIZE: {
|
||||
Command: []byte{0xBE},
|
||||
Mask: []byte{0xFF},
|
||||
Response: []byte{},
|
||||
},
|
||||
CMD_TRIGGER: {
|
||||
Command: []byte{0xAC, 0x33, 0x00},
|
||||
Mask: []byte{0xFF, 0xFF, 0xFF},
|
||||
Response: []byte{0x1C, 0x5D, 0x10, 0x66, 0x01, 0xD2, 0x93},
|
||||
},
|
||||
CMD_SOFTRESET: {
|
||||
Command: []byte{0xBA},
|
||||
Mask: []byte{0xFF},
|
||||
Response: []byte{},
|
||||
},
|
||||
CMD_STATUS: {
|
||||
Command: []byte{0x71},
|
||||
Mask: []byte{0xFF},
|
||||
Response: []byte{0x1C},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package aht20
|
||||
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
Address = 0x38
|
||||
|
||||
CMD_INITIALIZE = 0xBE
|
||||
CMD_STATUS = 0x71
|
||||
CMD_TRIGGER = 0xAC
|
||||
CMD_SOFTRESET = 0xBA
|
||||
|
||||
STATUS_BUSY = 0x80
|
||||
STATUS_CALIBRATED = 0x08
|
||||
)
|
||||
|
||||
var (
|
||||
ErrBusy = errors.New("device busy")
|
||||
ErrTimeout = errors.New("timeout")
|
||||
)
|
||||
+19
-17
@@ -27,44 +27,46 @@ var startFrame = []byte{0x00, 0x00, 0x00, 0x00}
|
||||
type Device struct {
|
||||
bus drivers.SPI
|
||||
Order int
|
||||
buf [4]byte
|
||||
}
|
||||
|
||||
// New returns a new APA102 driver. Pass in a fully configured SPI bus.
|
||||
func New(b drivers.SPI) Device {
|
||||
return Device{bus: b, Order: BGR}
|
||||
func New(b drivers.SPI) *Device {
|
||||
return &Device{bus: b, Order: BGR}
|
||||
}
|
||||
|
||||
// NewSoftwareSPI returns a new APA102 driver that will use a software based
|
||||
// implementation of the SPI protocol.
|
||||
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) Device {
|
||||
func NewSoftwareSPI(sckPin, sdoPin machine.Pin, delay uint32) *Device {
|
||||
return New(&bbSPI{SCK: sckPin, SDO: sdoPin, Delay: delay})
|
||||
}
|
||||
|
||||
// WriteColors writes the given RGBA color slice out using the APA102 protocol.
|
||||
// The A value (Alpha channel) is used for brightness, set to 0xff (255) for maximum.
|
||||
func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
|
||||
func (d *Device) WriteColors(cs []color.RGBA) (n int, err error) {
|
||||
d.startFrame()
|
||||
|
||||
// write data
|
||||
for _, c := range cs {
|
||||
// brightness is scaled to 5 bit value
|
||||
d.bus.Transfer(0xe0 | (c.A >> 3))
|
||||
d.buf[0] = 0xe0 | (c.A >> 3)
|
||||
|
||||
// set the colors
|
||||
switch d.Order {
|
||||
case BRG:
|
||||
d.bus.Transfer(c.B)
|
||||
d.bus.Transfer(c.R)
|
||||
d.bus.Transfer(c.G)
|
||||
d.buf[1] = c.B
|
||||
d.buf[2] = c.R
|
||||
d.buf[3] = c.G
|
||||
case GRB:
|
||||
d.bus.Transfer(c.G)
|
||||
d.bus.Transfer(c.R)
|
||||
d.bus.Transfer(c.B)
|
||||
d.buf[1] = c.G
|
||||
d.buf[2] = c.R
|
||||
d.buf[3] = c.B
|
||||
case BGR:
|
||||
d.bus.Transfer(c.B)
|
||||
d.bus.Transfer(c.G)
|
||||
d.bus.Transfer(c.R)
|
||||
d.buf[1] = c.B
|
||||
d.buf[2] = c.G
|
||||
d.buf[3] = c.R
|
||||
}
|
||||
d.bus.Tx(d.buf[:], nil)
|
||||
}
|
||||
|
||||
d.endFrame(len(cs))
|
||||
@@ -73,7 +75,7 @@ func (d Device) WriteColors(cs []color.RGBA) (n int, err error) {
|
||||
}
|
||||
|
||||
// Write the raw bytes using the APA102 protocol.
|
||||
func (d Device) Write(buf []byte) (n int, err error) {
|
||||
func (d *Device) Write(buf []byte) (n int, err error) {
|
||||
d.startFrame()
|
||||
d.bus.Tx(buf, nil)
|
||||
d.endFrame(len(buf) / 4)
|
||||
@@ -82,14 +84,14 @@ func (d Device) Write(buf []byte) (n int, err error) {
|
||||
}
|
||||
|
||||
// startFrame sends the start bytes for a strand of LEDs.
|
||||
func (d Device) startFrame() {
|
||||
func (d *Device) startFrame() {
|
||||
d.bus.Tx(startFrame, nil)
|
||||
}
|
||||
|
||||
// endFrame sends the end frame marker with one extra bit per LED so
|
||||
// long strands of LEDs receive the necessary termination for updates.
|
||||
// See https://cpldcpu.wordpress.com/2014/11/30/understanding-the-apa102-superled/
|
||||
func (d Device) endFrame(count int) {
|
||||
func (d *Device) endFrame(count int) {
|
||||
for i := 0; i < count/16; i++ {
|
||||
d.bus.Transfer(0xff)
|
||||
}
|
||||
|
||||
+24
-5
@@ -13,6 +13,8 @@ type DeviceSPI struct {
|
||||
// Chip select pin
|
||||
CSB machine.Pin
|
||||
|
||||
buf [7]byte
|
||||
|
||||
// SPI bus (requires chip select to be usable).
|
||||
Bus drivers.SPI
|
||||
}
|
||||
@@ -80,7 +82,10 @@ func (d *DeviceSPI) Reset() error {
|
||||
|
||||
// ReadTemperature returns the temperature in celsius milli degrees (°C/1000).
|
||||
func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
|
||||
data := []byte{0x80 | reg_TEMPERATURE_0, 0, 0}
|
||||
data := d.buf[:3]
|
||||
data[0] = 0x80 | reg_TEMPERATURE_0
|
||||
data[1] = 0
|
||||
data[2] = 0
|
||||
d.CSB.Low()
|
||||
err = d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
@@ -113,7 +118,11 @@ func (d *DeviceSPI) ReadTemperature() (temperature int32, err error) {
|
||||
// and the sensor is not moving the returned value will be around 1000000 or
|
||||
// -1000000.
|
||||
func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
data := []byte{0x80 | reg_ACC_XL, 0, 0, 0, 0, 0, 0}
|
||||
data := d.buf[:7]
|
||||
data[0] = 0x80 | reg_ACC_XL
|
||||
for i := 1; i < len(data); i++ {
|
||||
data[i] = 0
|
||||
}
|
||||
d.CSB.Low()
|
||||
err = d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
@@ -139,7 +148,11 @@ func (d *DeviceSPI) ReadAcceleration() (x int32, y int32, z int32, err error) {
|
||||
// rotation along one axis and while doing so integrate all values over time,
|
||||
// you would get a value close to 360000000.
|
||||
func (d *DeviceSPI) ReadRotation() (x int32, y int32, z int32, err error) {
|
||||
data := []byte{0x80 | reg_GYR_XL, 0, 0, 0, 0, 0, 0}
|
||||
data := d.buf[:7]
|
||||
data[0] = 0x80 | reg_GYR_XL
|
||||
for i := 1; i < len(data); i++ {
|
||||
data[i] = 0
|
||||
}
|
||||
d.CSB.Low()
|
||||
err = d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
@@ -185,7 +198,9 @@ func (d *DeviceSPI) readRegister(address uint8) uint8 {
|
||||
// I don't know why but it appears necessary to sleep for a bit here.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
data := []byte{0x80 | address, 0}
|
||||
data := d.buf[:2]
|
||||
data[0] = 0x80 | address
|
||||
data[1] = 0
|
||||
d.CSB.Low()
|
||||
d.Bus.Tx(data, data)
|
||||
d.CSB.High()
|
||||
@@ -198,7 +213,11 @@ func (d *DeviceSPI) writeRegister(address, data uint8) {
|
||||
// I don't know why but it appears necessary to sleep for a bit here.
|
||||
time.Sleep(time.Millisecond)
|
||||
|
||||
buf := d.buf[:2]
|
||||
buf[0] = address
|
||||
buf[1] = data
|
||||
|
||||
d.CSB.Low()
|
||||
d.Bus.Tx([]byte{address, data}, []byte{0, 0})
|
||||
d.Bus.Tx(buf, buf)
|
||||
d.CSB.High()
|
||||
}
|
||||
|
||||
+3
-3
@@ -20,17 +20,17 @@ package espat // import "tinygo.org/x/drivers/espat"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"machine"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
"tinygo.org/x/drivers/net"
|
||||
)
|
||||
|
||||
// Device wraps UART connection to the ESP8266/ESP32.
|
||||
type Device struct {
|
||||
bus machine.UART
|
||||
bus drivers.UART
|
||||
|
||||
// command responses that come back from the ESP8266/ESP32
|
||||
response []byte
|
||||
@@ -43,7 +43,7 @@ type Device struct {
|
||||
var ActiveDevice *Device
|
||||
|
||||
// New returns a new espat driver. Pass in a fully configured UART bus.
|
||||
func New(b machine.UART) *Device {
|
||||
func New(b drivers.UART) *Device {
|
||||
return &Device{bus: b, response: make([]byte, 512), socketdata: make([]byte, 0, 1024)}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = &machine.I2C0
|
||||
i2c = machine.I2C0
|
||||
sensor = adt7410.New(i2c)
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/aht20"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
dev := aht20.New(machine.I2C0)
|
||||
dev.Configure()
|
||||
|
||||
dev.Reset()
|
||||
for {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
err := dev.Read()
|
||||
if err != nil {
|
||||
println("Error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
println("temp ", fmtD(dev.DeciCelsius(), 3, 1), "C")
|
||||
println("humidity", fmtD(dev.DeciRelHumidity(), 3, 1), "%")
|
||||
}
|
||||
}
|
||||
|
||||
func fmtD(val int32, i int, f int) string {
|
||||
result := make([]byte, i+f+1)
|
||||
neg := false
|
||||
|
||||
if val < 0 {
|
||||
val = -val
|
||||
neg = true
|
||||
}
|
||||
|
||||
for p := len(result) - 1; p >= 0; p-- {
|
||||
result[p] = byte(int32('0') + (val % 10))
|
||||
val = val / 10
|
||||
|
||||
if p == i+1 && p > 0 {
|
||||
p--
|
||||
result[p] = '.'
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
result[0] = '-'
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
@@ -13,9 +13,9 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
apa apa102.Device
|
||||
apa *apa102.Device
|
||||
|
||||
led = machine.PWM{machine.LED}
|
||||
pwm = machine.TCC0
|
||||
leds = make([]color.RGBA, 1)
|
||||
wheel = &Wheel{Brightness: 0x10}
|
||||
)
|
||||
@@ -27,12 +27,19 @@ func init() {
|
||||
apa = apa102.NewSoftwareSPI(machine.PA00, machine.PA01, 1)
|
||||
|
||||
// Configure the regular on-board LED for PWM fading
|
||||
machine.InitPWM()
|
||||
led.Configure()
|
||||
|
||||
err := pwm.Configure(machine.PWMConfig{})
|
||||
if err != nil {
|
||||
println("failed to configure PWM")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
channelLED, err := pwm.Channel(machine.LED)
|
||||
if err != nil {
|
||||
println("failed to configure LED PWM channel")
|
||||
return
|
||||
}
|
||||
|
||||
// We'll fade the on-board LED in a goroutine to show/ensure that the APA102
|
||||
// works fine with the scheduler enabled. Comment this out to test this code
|
||||
@@ -43,11 +50,11 @@ func main() {
|
||||
brightening = !brightening
|
||||
continue
|
||||
}
|
||||
var brightness uint16 = uint16(i) << 8
|
||||
var brightness uint32 = uint32(i)
|
||||
if !brightening {
|
||||
brightness = 0xFFFF - brightness
|
||||
brightness = 256 - brightness
|
||||
}
|
||||
led.Set(brightness)
|
||||
pwm.Set(channelLED, pwm.Top()*brightness/256)
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -30,7 +30,7 @@ var (
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
console = machine.UART0
|
||||
console = machine.Serial
|
||||
|
||||
adaptor *espat.Device
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ var (
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
console = machine.UART0
|
||||
console = machine.Serial
|
||||
|
||||
adaptor *espat.Device
|
||||
topic = "tinygo"
|
||||
|
||||
@@ -37,7 +37,7 @@ var (
|
||||
tx = machine.PA22
|
||||
rx = machine.PA23
|
||||
|
||||
console = machine.UART0
|
||||
console = machine.Serial
|
||||
|
||||
adaptor *espat.Device
|
||||
cl mqtt.Client
|
||||
|
||||
@@ -20,7 +20,7 @@ var (
|
||||
input [consoleBufLen]byte
|
||||
store [storageBufLen]byte
|
||||
|
||||
console = machine.UART0
|
||||
console = machine.Serial
|
||||
|
||||
dev *flash.Device
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
println("GPS I2C Example")
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
ublox := gps.NewI2C(&machine.I2C0)
|
||||
ublox := gps.NewI2C(machine.I2C0)
|
||||
parser := gps.NewParser()
|
||||
var fix gps.Fix
|
||||
for {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func main() {
|
||||
println("GPS UART Example")
|
||||
machine.UART1.Configure(machine.UARTConfig{BaudRate: 9600})
|
||||
ublox := gps.NewUART(&machine.UART1)
|
||||
ublox := gps.NewUART(machine.UART1)
|
||||
parser := gps.NewParser()
|
||||
var fix gps.Fix
|
||||
for {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,25 @@ import (
|
||||
"tinygo.org/x/drivers/ili9341"
|
||||
)
|
||||
|
||||
const (
|
||||
BALLWIDTH = 136
|
||||
BALLHEIGHT = 100
|
||||
)
|
||||
|
||||
const (
|
||||
SCREENHEIGHT = 240
|
||||
SCREENWIDTH = 320
|
||||
)
|
||||
|
||||
const (
|
||||
invBGCOLOR = 0x75AD
|
||||
invGRIDCOLOR = 0x15A8
|
||||
invBGSHADOW = 0x8552
|
||||
invGRIDSHADOW = 0x0C60
|
||||
invRED = 0x00F8
|
||||
invWHITE = 0xFFFF
|
||||
)
|
||||
|
||||
const (
|
||||
BGCOLOR = 0xAD75
|
||||
GRIDCOLOR = 0xA815
|
||||
@@ -25,7 +44,12 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
frameBuffer = [(graphics.BALLHEIGHT + 8) * (graphics.BALLWIDTH + 8)]uint16{}
|
||||
dbg5 = machine.D5
|
||||
dbg6 = machine.D6
|
||||
)
|
||||
|
||||
var (
|
||||
frameBuffer = [2][(BALLHEIGHT + 8) * (BALLWIDTH + 8)]uint16{}
|
||||
|
||||
startTime int64
|
||||
frame int64
|
||||
@@ -71,14 +95,18 @@ func main() {
|
||||
balloldx = ballx
|
||||
balloldy = bally // Prior ball position
|
||||
|
||||
var bufIdx int8 = 0
|
||||
|
||||
for {
|
||||
dbg5.High()
|
||||
bufIdx = 1 - bufIdx
|
||||
|
||||
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) {
|
||||
if (ballx <= 15) || (ballx >= SCREENWIDTH-BALLWIDTH) {
|
||||
ballvx *= -1 // Left/right bounce
|
||||
}
|
||||
if bally >= YBOTTOM { // Hit ground?
|
||||
@@ -100,13 +128,13 @@ func main() {
|
||||
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)
|
||||
maxx = int16(ballx + BALLWIDTH - 1)
|
||||
if int16(balloldx+BALLWIDTH-1) > maxx {
|
||||
maxx = int16(balloldx + BALLWIDTH - 1)
|
||||
}
|
||||
maxy = int16(bally + graphics.BALLHEIGHT - 1)
|
||||
if int16(balloldy+graphics.BALLHEIGHT-1) > maxy {
|
||||
maxy = int16(balloldy + graphics.BALLHEIGHT - 1)
|
||||
maxy = int16(bally + BALLHEIGHT - 1)
|
||||
if int16(balloldy+BALLHEIGHT-1) > maxy {
|
||||
maxy = int16(balloldy + BALLHEIGHT - 1)
|
||||
}
|
||||
|
||||
width = maxx - minx + 1
|
||||
@@ -120,13 +148,13 @@ func main() {
|
||||
ballframe -= 14
|
||||
}
|
||||
|
||||
// Set 7 palette entries to white, 7 to red, based on frame number.
|
||||
// This makes the ball spin
|
||||
//// 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
|
||||
palette[i+2] = invWHITE
|
||||
} else {
|
||||
palette[i+2] = RED
|
||||
palette[i+2] = invRED
|
||||
} // Palette entries 0 and 1 aren't used (clear and shadow, respectively)
|
||||
}
|
||||
|
||||
@@ -136,61 +164,100 @@ func main() {
|
||||
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
|
||||
//var bufIdx int8 = 0
|
||||
|
||||
//tft.setAddrWindow(minx, miny, width, height)
|
||||
//dbg5.Low()
|
||||
dbg6.High()
|
||||
//fmt.Printf("%d < %d < %d < %d\r\n", by, 0, BALLHEIGHT, 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?
|
||||
y := 0
|
||||
if by < 0 {
|
||||
max := -1 * int(by)
|
||||
for y = 0; y < max; y++ { // For each row...
|
||||
var bgidxBase = int(bgy)*(SCREENWIDTH) + int(bgx)
|
||||
var yBase = y * int(width)
|
||||
for x := 0; x < int(width); x++ {
|
||||
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
|
||||
}
|
||||
bgy++
|
||||
}
|
||||
}
|
||||
|
||||
y2 := y
|
||||
max := 0
|
||||
if bx < 0 {
|
||||
max = -1 * int(bx)
|
||||
bgy2 := bgy
|
||||
for y = y2; y < y2+int(BALLHEIGHT); y++ { // For each row...
|
||||
var bgidxBase = int(bgy2)*(SCREENWIDTH) + int(bgx)
|
||||
var yBase = y * int(width)
|
||||
//fmt.Printf("- %d %d %d %d %d %d\r\n", bgy, y, bgx, max, yBase, bgidxBase)
|
||||
for x := 0; x < int(max); x++ {
|
||||
//fmt.Printf(" %d %d\r\n", yBase+x, bgidxBase+x)
|
||||
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
|
||||
}
|
||||
bgy2++
|
||||
}
|
||||
//fmt.Printf("(%d, %d) - (%d, %d)\r\n", bx, 0, -1, BALLHEIGHT-1)
|
||||
}
|
||||
|
||||
{
|
||||
bgy2 := bgy
|
||||
//fmt.Printf("(%d, %d) - (%d, %d)\r\n", 0, 0, BALLWIDTH-1, BALLHEIGHT-1)
|
||||
for y = y2; y < y2+int(BALLHEIGHT); y++ { // For each row...
|
||||
var bgidxBase = int(bgy2)*(SCREENWIDTH) + int(bgx)
|
||||
var byBase = (y - y2) * BALLWIDTH
|
||||
var yBase = y * int(width)
|
||||
for x := max; x < int(BALLWIDTH)+max; x++ {
|
||||
//fmt.Printf("%d %d %d %d\r\n", byBase, x, bgidxBase, yBase)
|
||||
//time.Sleep(1 * time.Millisecond)
|
||||
// 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
|
||||
c = uint16(graphics.Ball[int(byBase)+x-max]) // Get packed value (2 pixels)
|
||||
|
||||
if c == 0 { // Outside ball - just draw grid
|
||||
if graphics.Background[bgidx]&(0x80>>(bgx1&7)) != 0 {
|
||||
c = GRIDCOLOR
|
||||
} else {
|
||||
c = BGCOLOR
|
||||
}
|
||||
c = graphics.Background[bgidxBase+x]
|
||||
} 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
|
||||
c = graphics.BackgroundShadow[bgidxBase+x]
|
||||
}
|
||||
frameBuffer[bufIdx][yBase+x] = c
|
||||
}
|
||||
frameBuffer[y*int(width)+x] = c
|
||||
bx1++ // Increment bitmap position counters (X axis)
|
||||
bgx1++
|
||||
bgy2++
|
||||
}
|
||||
//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)
|
||||
{
|
||||
bgy2 := bgy
|
||||
for y = y2; y < y2+int(BALLHEIGHT); y++ { // For each row...
|
||||
var bgidxBase = int(bgy2)*(SCREENWIDTH) + int(bgx)
|
||||
var yBase = y * int(width)
|
||||
//fmt.Printf("+ %d %d %d %d %d\r\n", bgy, y, bgx, yBase, bgidxBase)
|
||||
for x := int(BALLWIDTH) + max; x < int(width); x++ {
|
||||
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
|
||||
}
|
||||
bgy2++
|
||||
}
|
||||
}
|
||||
|
||||
y = y2 + int(BALLHEIGHT)
|
||||
bgy += BALLHEIGHT
|
||||
{
|
||||
for ; y < int(height); y++ { // For each row...
|
||||
//destPtr = &renderbuf[bufIdx][0];
|
||||
var bgidxBase = int(bgy)*(SCREENWIDTH) + int(bgx)
|
||||
var yBase = y * int(width)
|
||||
for x := 0; x < int(width); x++ {
|
||||
frameBuffer[bufIdx][yBase+x] = graphics.Background[bgidxBase+x]
|
||||
}
|
||||
bgy++
|
||||
}
|
||||
}
|
||||
dbg6.Low()
|
||||
|
||||
display.DrawRGBBitmap(minx, miny, frameBuffer[bufIdx][:width*height], width, height)
|
||||
//time.Sleep(10 * time.Millisecond)
|
||||
|
||||
// Show approximate frame rate
|
||||
frame++
|
||||
@@ -205,21 +272,13 @@ func main() {
|
||||
|
||||
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
|
||||
}
|
||||
var bufIdx int8 = 0
|
||||
for j := 0; j < int(h); j++ {
|
||||
bufIdx = 1 - bufIdx
|
||||
for k := 0; k < int(w); k++ {
|
||||
frameBuffer[bufIdx][k] = graphics.Background[j*int(w)+k]
|
||||
}
|
||||
display.DrawRGBBitmap(0, j, frameBuffer[0:w], w, 1)
|
||||
display.DrawRGBBitmap(0, int16(j), frameBuffer[bufIdx][0:w], w, 1)
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ina260"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{})
|
||||
|
||||
dev := ina260.New(machine.I2C0)
|
||||
dev.Configure(ina260.Config{
|
||||
AverageMode: ina260.AVGMODE_16,
|
||||
VoltConvTime: ina260.CONVTIME_140USEC,
|
||||
CurrentConvTime: ina260.CONVTIME_140USEC,
|
||||
Mode: ina260.MODE_CONTINUOUS | ina260.MODE_VOLTAGE | ina260.MODE_CURRENT,
|
||||
})
|
||||
|
||||
if dev.Connected() {
|
||||
println("INA260 detected")
|
||||
} else {
|
||||
println("INA260 NOT detected")
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
microvolts := dev.Voltage()
|
||||
microamps := dev.Current()
|
||||
microwatts := dev.Power()
|
||||
|
||||
println(fmtD(microvolts, 4, 3), "mV,", fmtD(microamps, 4, 3), "mA,", fmtD(microwatts, 4, 3), "mW")
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func fmtD(val int32, i int, f int) string {
|
||||
result := make([]byte, i+f+1)
|
||||
neg := false
|
||||
|
||||
if val < 0 {
|
||||
val = -val
|
||||
neg = true
|
||||
}
|
||||
|
||||
for p := len(result) - 1; p >= 0; p-- {
|
||||
result[p] = byte(int32('0') + (val % 10))
|
||||
val = val / 10
|
||||
|
||||
if p == i+1 && p > 0 {
|
||||
p--
|
||||
result[p] = '.'
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
result[0] = '-'
|
||||
}
|
||||
|
||||
return string(result)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"tinygo.org/x/drivers/keypad4x4"
|
||||
)
|
||||
|
||||
func main() {
|
||||
mapping := map[uint8]string{
|
||||
1: "1",
|
||||
2: "2",
|
||||
3: "3",
|
||||
4: "A",
|
||||
5: "4",
|
||||
6: "5",
|
||||
7: "6",
|
||||
8: "B",
|
||||
9: "7",
|
||||
10: "8",
|
||||
11: "9",
|
||||
12: "C",
|
||||
13: "*",
|
||||
14: "0",
|
||||
15: "#",
|
||||
16: "D",
|
||||
}
|
||||
|
||||
keypadDevice := keypad4x4.NewDevice(machine.D2, machine.D3, machine.D4, machine.D5, machine.D6, machine.D7, machine.D8, machine.D9)
|
||||
keypadDevice.Configure()
|
||||
|
||||
for {
|
||||
key := keypadDevice.GetKey()
|
||||
if key != keypad4x4.NoKeyPressed {
|
||||
println("Button: ", mapping[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,31 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxSpeed = 30000
|
||||
maxSpeed = 100
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.InitPWM()
|
||||
err := machine.TCC0.Configure(machine.PWMConfig{
|
||||
Period: 16384e3, // 16.384ms
|
||||
})
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
wheel := l293x.NewWithSpeed(machine.D10, machine.D11, machine.PWM{machine.D12})
|
||||
spc, err := machine.TCC0.Channel(machine.D12)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
wheel := l293x.NewWithSpeed(machine.D10, machine.D11, spc, machine.TCC0)
|
||||
wheel.Configure()
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
println("Forward")
|
||||
var i uint16
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
var i uint32
|
||||
for i = 0; i < maxSpeed; i += 10 {
|
||||
wheel.Forward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
@@ -30,7 +42,7 @@ func main() {
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Backward")
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
for i = 0; i < maxSpeed; i += 10 {
|
||||
wheel.Backward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
@@ -8,19 +8,38 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
maxSpeed = 30000
|
||||
maxSpeed = 100
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.InitPWM()
|
||||
machine.D11.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
machine.D12.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
wheel := l9110x.NewWithSpeed(machine.PWM{machine.D11}, machine.PWM{machine.D12})
|
||||
err := machine.TCC0.Configure(machine.PWMConfig{})
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ca, err := machine.TCC0.Channel(machine.D11)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
cb, err := machine.TCC0.Channel(machine.D12)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
wheel := l9110x.NewWithSpeed(ca, cb, machine.TCC0)
|
||||
wheel.Configure()
|
||||
|
||||
for i := 0; i <= 10; i++ {
|
||||
println("Forward")
|
||||
var i uint16
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
var i uint32
|
||||
for i = 0; i < maxSpeed; i += 10 {
|
||||
wheel.Forward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
@@ -30,7 +49,7 @@ func main() {
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
println("Backward")
|
||||
for i = 0; i < maxSpeed; i += 1000 {
|
||||
for i = 0; i < maxSpeed; i += 10 {
|
||||
wheel.Backward(i)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/max72xx"
|
||||
)
|
||||
|
||||
// example for a 4 digit 7 segment display
|
||||
func main() {
|
||||
// Pins for Arduino Nano 33 IOT
|
||||
err := machine.SPI0.Configure(machine.SPIConfig{
|
||||
SDO: machine.D11, // default SDO pin
|
||||
SCK: machine.D13, // default sck pin
|
||||
LSBFirst: false,
|
||||
Frequency: 10000000,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
}
|
||||
|
||||
driver := max72xx.NewDevice(machine.SPI0, machine.D6)
|
||||
driver.Configure()
|
||||
|
||||
digitNumber := 4
|
||||
|
||||
driver.StopDisplayTest()
|
||||
driver.SetDecodeMode(4)
|
||||
driver.SetScanLimit(4)
|
||||
driver.StopShutdownMode()
|
||||
|
||||
for i := 1; i < int(digitNumber); i++ {
|
||||
driver.WriteCommand(byte(i), byte(Blank))
|
||||
}
|
||||
|
||||
for {
|
||||
for _, character := range characters {
|
||||
println("writing", "characterValue:", character.String())
|
||||
driver.WriteCommand(byte(4), byte(character))
|
||||
driver.WriteCommand(byte(3), byte(character))
|
||||
driver.WriteCommand(byte(2), byte(character))
|
||||
driver.WriteCommand(byte(1), byte(character))
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var characters = []Character{
|
||||
Zero,
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Dash,
|
||||
E,
|
||||
H,
|
||||
L,
|
||||
P,
|
||||
Blank,
|
||||
Dot,
|
||||
}
|
||||
|
||||
// Each bit translates to a pin, which is driven high or low
|
||||
type Character byte
|
||||
|
||||
func (char Character) String() string {
|
||||
switch char {
|
||||
case Zero:
|
||||
return "0"
|
||||
case One:
|
||||
return "1"
|
||||
case Two:
|
||||
return "2"
|
||||
case Three:
|
||||
return "3"
|
||||
case Four:
|
||||
return "4"
|
||||
case Five:
|
||||
return "5"
|
||||
case Six:
|
||||
return "6"
|
||||
case Seven:
|
||||
return "7"
|
||||
case Eight:
|
||||
return "8"
|
||||
case Nine:
|
||||
return "9"
|
||||
case Dash:
|
||||
return "-"
|
||||
case E:
|
||||
return "E"
|
||||
case H:
|
||||
return "H"
|
||||
case L:
|
||||
return "L"
|
||||
case P:
|
||||
return "P"
|
||||
case Blank:
|
||||
return ""
|
||||
case Dot:
|
||||
return "."
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
Zero Character = 0 //126
|
||||
One Character = 1 //48
|
||||
Two Character = 2 // 109
|
||||
Three Character = 3 // 121
|
||||
Four Character = 4
|
||||
Five Character = 5
|
||||
Six Character = 6
|
||||
Seven Character = 7
|
||||
Eight Character = 8
|
||||
Nine Character = 9
|
||||
Dash Character = 10
|
||||
E Character = 11
|
||||
H Character = 12
|
||||
L Character = 13
|
||||
P Character = 14
|
||||
Blank Character = 15
|
||||
Dot Character = 128
|
||||
)
|
||||
@@ -0,0 +1,55 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/mcp2515"
|
||||
)
|
||||
|
||||
var (
|
||||
spi = machine.SPI0
|
||||
csPin = machine.D5
|
||||
)
|
||||
|
||||
func main() {
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 115200,
|
||||
SCK: machine.SPI0_SCK_PIN,
|
||||
SDO: machine.SPI0_SDO_PIN,
|
||||
SDI: machine.SPI0_SDI_PIN,
|
||||
Mode: 0})
|
||||
can := mcp2515.New(spi, csPin)
|
||||
can.Configure()
|
||||
err := can.Begin(mcp2515.CAN500kBps, mcp2515.Clock8MHz)
|
||||
if err != nil {
|
||||
failMessage(err.Error())
|
||||
}
|
||||
|
||||
for {
|
||||
err := can.Tx(0x111, 8, []byte{0x00, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA})
|
||||
if err != nil {
|
||||
failMessage(err.Error())
|
||||
}
|
||||
if can.Received() {
|
||||
msg, err := can.Rx()
|
||||
if err != nil {
|
||||
failMessage(err.Error())
|
||||
}
|
||||
fmt.Printf("CAN-ID: %03X dlc: %d data: ", msg.ID, msg.Dlc)
|
||||
for _, b := range msg.Data {
|
||||
fmt.Printf("%02X ", b)
|
||||
}
|
||||
fmt.Print("\r\n")
|
||||
}
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
}
|
||||
}
|
||||
|
||||
func failMessage(msg string) {
|
||||
for {
|
||||
println(msg)
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/p1am"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for {
|
||||
if err := loop(); err != nil {
|
||||
fmt.Printf("loop failed, retrying: %v\n", err)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
func loop() error {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
sw := machine.SWITCH
|
||||
sw.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
|
||||
if err := p1am.Controller.Initialize(); err != nil {
|
||||
return fmt.Errorf("initializing controller: %w", err)
|
||||
}
|
||||
|
||||
version, err := p1am.Controller.Version()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fetching base controller version: %w", err)
|
||||
}
|
||||
fmt.Printf("Base controller version: %d.%d.%d\n", version[0], version[1], version[2])
|
||||
|
||||
for i := 1; i <= p1am.Controller.Slots; i++ {
|
||||
slot := p1am.Controller.Slot(i)
|
||||
fmt.Printf("Slot %d: ID 0x%08x, Props %+v\n", i, slot.ID, slot.Props)
|
||||
}
|
||||
|
||||
slot1 := p1am.Controller.Slot(1)
|
||||
var lastInput uint32
|
||||
state := sw.Get()
|
||||
for {
|
||||
if active, err := p1am.Controller.Active(); err != nil || !active {
|
||||
return fmt.Errorf("controller active %v: %v", active, err)
|
||||
}
|
||||
if state != sw.Get() {
|
||||
state = sw.Get()
|
||||
fmt.Printf("New switch state: %v\n", state)
|
||||
if slot1.Props.DO > 0 {
|
||||
if err := slot1.Channel(1).WriteDiscrete(state); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if slot1.Props.DI > 0 {
|
||||
sstate, err := slot1.ReadDiscrete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading slot: %w", err)
|
||||
}
|
||||
if sstate != lastInput {
|
||||
lastInput = sstate
|
||||
fmt.Printf("new DI state: %#b\n", sstate)
|
||||
}
|
||||
}
|
||||
if state {
|
||||
led.High()
|
||||
} else {
|
||||
led.Low()
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/pcf8563"
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = machine.I2C0
|
||||
rtc = pcf8563.New(i2c)
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
|
||||
rtc.Reset()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
rtc.SetTime(time.Date(2006, 1, 2, 15, 4, 50, 0, time.UTC))
|
||||
|
||||
rtc.SetAlarm(time.Date(2006, 1, 2, 15, 5, 0, 0, time.UTC))
|
||||
rtc.EnableAlarmInterrupt()
|
||||
|
||||
prev := -1
|
||||
|
||||
for {
|
||||
for {
|
||||
t, _ := rtc.ReadTime()
|
||||
if prev != t.Second() {
|
||||
fmt.Printf("%s\r\n", t.String())
|
||||
prev = t.Second()
|
||||
|
||||
if rtc.AlarmTriggered() {
|
||||
fmt.Printf("alarm triggered\r\n")
|
||||
rtc.ClearAlarm()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/pcf8563"
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = machine.I2C0
|
||||
rtc = pcf8563.New(i2c)
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
|
||||
rtc.Reset()
|
||||
|
||||
for {
|
||||
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_1HZ)
|
||||
time.Sleep(3 * time.Second)
|
||||
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_32HZ)
|
||||
time.Sleep(3 * time.Second)
|
||||
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_1KHZ)
|
||||
time.Sleep(3 * time.Second)
|
||||
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_32KHZ)
|
||||
time.Sleep(3 * time.Second)
|
||||
rtc.SetOscillatorFrequency(pcf8563.RTC_COT_DISABLE)
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/pcf8563"
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = machine.I2C0
|
||||
rtc = pcf8563.New(i2c)
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
|
||||
rtc.Reset()
|
||||
|
||||
rtc.SetTime(time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC))
|
||||
|
||||
prev := -1
|
||||
|
||||
for {
|
||||
for {
|
||||
t, _ := rtc.ReadTime()
|
||||
if prev != t.Second() {
|
||||
fmt.Printf("%s\r\n", t.String())
|
||||
prev = t.Second()
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/pcf8563"
|
||||
)
|
||||
|
||||
var (
|
||||
i2c = machine.I2C0
|
||||
rtc = pcf8563.New(i2c)
|
||||
)
|
||||
|
||||
func main() {
|
||||
i2c.Configure(machine.I2CConfig{Frequency: machine.TWI_FREQ_400KHZ})
|
||||
rtc.Reset()
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
rtc.SetTime(time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC))
|
||||
|
||||
rtc.SetTimer(15 * time.Second)
|
||||
rtc.EnableTimerInterrupt()
|
||||
|
||||
prev := -1
|
||||
|
||||
for {
|
||||
for {
|
||||
t, _ := rtc.ReadTime()
|
||||
if prev != t.Second() {
|
||||
fmt.Printf("%s\r\n", t.String())
|
||||
prev = t.Second()
|
||||
|
||||
if rtc.TimerTriggered() {
|
||||
fmt.Printf("timer triggered\r\n")
|
||||
rtc.ClearTimer()
|
||||
rtc.SetTimer(10 * time.Second)
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"machine"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sdcard"
|
||||
)
|
||||
|
||||
const consoleBufLen = 64
|
||||
const storageBufLen = 1024
|
||||
|
||||
var (
|
||||
debug = false
|
||||
|
||||
input [consoleBufLen]byte
|
||||
store [storageBufLen]byte
|
||||
|
||||
console = machine.Serial
|
||||
|
||||
dev *sdcard.Device
|
||||
|
||||
commands map[string]cmdfunc = map[string]cmdfunc{
|
||||
"": cmdfunc(noop),
|
||||
"help": cmdfunc(help),
|
||||
"dbg": cmdfunc(dbg),
|
||||
"erase": cmdfunc(erase),
|
||||
"lsblk": cmdfunc(lsblk),
|
||||
"write": cmdfunc(write),
|
||||
"xxd": cmdfunc(xxd),
|
||||
}
|
||||
|
||||
his history
|
||||
)
|
||||
|
||||
type history struct {
|
||||
buf [32]string
|
||||
wp int
|
||||
idx int
|
||||
}
|
||||
|
||||
func (h *history) Add(cmd string) {
|
||||
if len(cmd) == 0 {
|
||||
h.idx = h.wp
|
||||
return
|
||||
}
|
||||
|
||||
if h.wp == len(h.buf)-1 {
|
||||
for i := 1; i < len(h.buf); i++ {
|
||||
h.buf[i-1] = h.buf[i]
|
||||
}
|
||||
h.wp--
|
||||
}
|
||||
|
||||
h.buf[h.wp] = cmd
|
||||
h.wp++
|
||||
h.idx = h.wp
|
||||
}
|
||||
|
||||
func (h *history) PeekPrev() string {
|
||||
if h.idx > 0 {
|
||||
h.idx--
|
||||
}
|
||||
return h.buf[h.idx]
|
||||
}
|
||||
|
||||
func (h *history) PeekNext() string {
|
||||
if h.idx < h.wp {
|
||||
h.idx++
|
||||
}
|
||||
return h.buf[h.idx]
|
||||
}
|
||||
|
||||
type cmdfunc func(argv []string)
|
||||
|
||||
const (
|
||||
StateInput = iota
|
||||
StateEscape
|
||||
StateEscBrc
|
||||
StateCSI
|
||||
)
|
||||
|
||||
func RunFor(device *sdcard.Device) {
|
||||
|
||||
dev = device
|
||||
|
||||
prompt()
|
||||
|
||||
var state = StateInput
|
||||
|
||||
for i := 0; ; {
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
if debug {
|
||||
fmt.Printf("\rdata: %x, his.idx: %d\r\n\r", data, his.idx)
|
||||
prompt()
|
||||
console.Write(input[:i])
|
||||
}
|
||||
switch state {
|
||||
case StateInput:
|
||||
switch data {
|
||||
case 0x8:
|
||||
fallthrough
|
||||
case 0x7f: // this is probably wrong... works on my machine tho :)
|
||||
// backspace
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
console.Write([]byte{0x8, 0x20, 0x8})
|
||||
}
|
||||
case 13:
|
||||
// return key
|
||||
console.Write([]byte("\r\n"))
|
||||
runCommand(string(input[:i]))
|
||||
his.Add(string(input[:i]))
|
||||
prompt()
|
||||
|
||||
i = 0
|
||||
continue
|
||||
case 27:
|
||||
// escape
|
||||
state = StateEscape
|
||||
default:
|
||||
// anything else, just echo the character if it is printable
|
||||
if strconv.IsPrint(rune(data)) {
|
||||
if i < (consoleBufLen - 1) {
|
||||
console.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
case StateEscape:
|
||||
switch data {
|
||||
case 0x5b:
|
||||
state = StateEscBrc
|
||||
default:
|
||||
state = StateInput
|
||||
}
|
||||
case StateEscBrc:
|
||||
switch data {
|
||||
case 0x41:
|
||||
// up
|
||||
println()
|
||||
prompt()
|
||||
cmd := his.PeekPrev()
|
||||
i = len(cmd)
|
||||
copy(input[:i], []byte(cmd))
|
||||
console.Write(input[:i])
|
||||
state = StateInput
|
||||
case 0x42:
|
||||
//down
|
||||
println()
|
||||
prompt()
|
||||
cmd := his.PeekNext()
|
||||
i = len(cmd)
|
||||
copy(input[:i], []byte(cmd))
|
||||
console.Write(input[:i])
|
||||
state = StateInput
|
||||
default:
|
||||
// TODO: handle escape sequences
|
||||
state = StateInput
|
||||
}
|
||||
default:
|
||||
// TODO: handle escape sequences
|
||||
state = StateInput
|
||||
}
|
||||
} else {
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(line string) {
|
||||
argv := strings.SplitN(strings.TrimSpace(line), " ", -1)
|
||||
cmd := argv[0]
|
||||
|
||||
cmdfn, ok := commands[cmd]
|
||||
if !ok {
|
||||
println("unknown command: " + line)
|
||||
return
|
||||
}
|
||||
cmdfn(argv)
|
||||
}
|
||||
|
||||
func noop(argv []string) {}
|
||||
|
||||
func help(argv []string) {
|
||||
fmt.Printf("help\r\n")
|
||||
fmt.Printf("dbg\r\n")
|
||||
fmt.Printf("erase\r\n")
|
||||
fmt.Printf("lsblk\r\n")
|
||||
fmt.Printf("write <hex offset> <bytes>\r\n")
|
||||
fmt.Printf("xxd <start address> <length>\r\n")
|
||||
}
|
||||
|
||||
func dbg(argv []string) {
|
||||
if debug {
|
||||
debug = false
|
||||
println("Console debbuging off")
|
||||
} else {
|
||||
debug = true
|
||||
println("Console debbuging on")
|
||||
}
|
||||
}
|
||||
|
||||
func lsblk(argv []string) {
|
||||
csd := dev.CSD
|
||||
sectors, err := csd.Sectors()
|
||||
if err != nil {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
return
|
||||
}
|
||||
cid := dev.CID
|
||||
|
||||
fmt.Printf(
|
||||
"\r\n-------------------------------------\r\n"+
|
||||
" Device Information: \r\n"+
|
||||
"-------------------------------------\r\n"+
|
||||
" JEDEC ID: %v\r\n"+
|
||||
" Serial: %v\r\n"+
|
||||
" Status 1: %02x\r\n"+
|
||||
" Status 2: %02x\r\n"+
|
||||
" \r\n"+
|
||||
" Max clock speed (MHz): %d\r\n"+
|
||||
" Has Sector Protection: %t\r\n"+
|
||||
" Supports Fast Reads: %t\r\n"+
|
||||
" Supports QSPI Reads: %t\r\n"+
|
||||
" Supports QSPI Write: %t\r\n"+
|
||||
" Write Status Split: %t\r\n"+
|
||||
" Single Status Byte: %t\r\n"+
|
||||
"-Sectors: %d\r\n"+
|
||||
"-Bytes (Sectors * 512) %d\r\n"+
|
||||
"-ManufacturerID %02X\r\n"+
|
||||
"-OEMApplicationID %04X\r\n"+
|
||||
"-ProductName %s\r\n"+
|
||||
"-ProductVersion %s\r\n"+
|
||||
"-ProductSerialNumber %08X\r\n"+
|
||||
"-ManufacturingYear %02X\r\n"+
|
||||
"-ManufacturingMonth %02X\r\n"+
|
||||
"-Always1 %d\r\n"+
|
||||
"-CRC %02X\r\n"+
|
||||
"-------------------------------------\r\n\r\n",
|
||||
"attrs.JedecID", // attrs.JedecID,
|
||||
cid.ProductSerialNumber, // serialNumber1,
|
||||
0, // status1,
|
||||
0, // status2,
|
||||
csd.TRAN_SPEED, // attrs.MaxClockSpeedMHz,
|
||||
false, // attrs.HasSectorProtection,
|
||||
false, // attrs.SupportsFastRead,
|
||||
false, // attrs.SupportsQSPI,
|
||||
false, // attrs.SupportsQSPIWrites,
|
||||
false, // attrs.WriteStatusSplit,
|
||||
false, // attrs.SingleStatusByte,
|
||||
sectors,
|
||||
csd.Size(),
|
||||
cid.ManufacturerID,
|
||||
cid.OEMApplicationID,
|
||||
cid.ProductName,
|
||||
cid.ProductVersion,
|
||||
cid.ProductSerialNumber,
|
||||
cid.ManufacturingYear,
|
||||
cid.ManufacturingMonth,
|
||||
cid.Always1,
|
||||
cid.CRC,
|
||||
)
|
||||
}
|
||||
|
||||
func erase(argv []string) {
|
||||
fmt.Printf("erase - not impl\r\n")
|
||||
}
|
||||
|
||||
var writeBuf [256]byte
|
||||
|
||||
func write(argv []string) {
|
||||
if len(argv) < 3 {
|
||||
println("usage: write <hex offset> <bytes>")
|
||||
return
|
||||
}
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
buf := writeBuf[:0]
|
||||
for i := 0; i < len(argv[2]); i += 2 {
|
||||
var b uint64
|
||||
if b, err = strconv.ParseUint(argv[2][i:i+2], 16, 8); err != nil {
|
||||
println("Invalid bytes: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
buf = append(buf, byte(b))
|
||||
}
|
||||
|
||||
if _, err = dev.WriteAt(buf, int64(addr)); err != nil {
|
||||
println("Write error: " + err.Error() + "\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
func xxd(argv []string) {
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
var size int = 64
|
||||
switch len(argv) {
|
||||
case 3:
|
||||
if size, err = strconv.Atoi(argv[2]); err != nil {
|
||||
println("Invalid size argument: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
if size > storageBufLen || size < 1 {
|
||||
fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", storageBufLen)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
// no args supplied, so nothing to do here, just use the defaults
|
||||
default:
|
||||
println("usage: xxd <hex address, ex: 0xA0> <size of hexdump in bytes>\r\n")
|
||||
return
|
||||
}
|
||||
buf := store[0:size]
|
||||
_, err = dev.ReadAt(buf, int64(addr))
|
||||
if err != nil {
|
||||
fmt.Printf("xxd err : %s\r\n", err.Error())
|
||||
}
|
||||
xxdfprint(os.Stdout, uint32(addr), buf)
|
||||
}
|
||||
|
||||
func xxdfprint(w io.Writer, offset uint32, b []byte) {
|
||||
var l int
|
||||
var buf16 = make([]byte, 16)
|
||||
for i, c := 0, len(b); i < c; i += 16 {
|
||||
l = i + 16
|
||||
if l >= c {
|
||||
l = c
|
||||
}
|
||||
fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l])
|
||||
for j, n := 0, l-i; j < 16; j++ {
|
||||
if j >= n || !strconv.IsPrint(rune(b[i+j])) || b[i+j] >= 0x80 {
|
||||
buf16[j] = '.'
|
||||
} else {
|
||||
buf16[j] = b[i+j]
|
||||
}
|
||||
}
|
||||
console.Write(buf16)
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
func prompt() {
|
||||
print("==> ")
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build feather_m4 feather_m4_can feather_nrf52840
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D10
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build grandcentral-m4
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI1
|
||||
sckPin = machine.SDCARD_SCK_PIN
|
||||
sdoPin = machine.SDCARD_SDO_PIN
|
||||
sdiPin = machine.SDCARD_SDI_PIN
|
||||
csPin = machine.SDCARD_CS_PIN
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build atsamd21,!p1am_100
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D2
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/sdcard"
|
||||
)
|
||||
|
||||
var (
|
||||
spi machine.SPI
|
||||
sckPin machine.Pin
|
||||
sdoPin machine.Pin
|
||||
sdiPin machine.Pin
|
||||
csPin machine.Pin
|
||||
ledPin machine.Pin
|
||||
)
|
||||
|
||||
func main() {
|
||||
waitSerial()
|
||||
fmt.Printf("sdcard console\r\n")
|
||||
|
||||
led := ledPin
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
sd := sdcard.New(spi, sckPin, sdoPin, sdiPin, csPin)
|
||||
err := sd.Configure()
|
||||
if err != nil {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
go RunFor(&sd)
|
||||
|
||||
for {
|
||||
led.High()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
led.Low()
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build p1am_100
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SDCARD_SPI
|
||||
sckPin = machine.SDCARD_SCK_PIN
|
||||
sdoPin = machine.SDCARD_SDO_PIN
|
||||
sdiPin = machine.SDCARD_SDI_PIN
|
||||
csPin = machine.SDCARD_SS_PIN
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build pygamer
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D4
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D32 // SD_CS
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI2
|
||||
sckPin = machine.SCK2
|
||||
sdoPin = machine.SDO2
|
||||
sdiPin = machine.SDI2
|
||||
csPin = machine.SS2
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
// +build tinygo
|
||||
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"machine"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinyfs"
|
||||
)
|
||||
|
||||
const consoleBufLen = 64
|
||||
|
||||
const startBlock = 0
|
||||
const blockCount = 0
|
||||
|
||||
var (
|
||||
debug = false
|
||||
|
||||
input [consoleBufLen]byte
|
||||
|
||||
console = machine.Serial
|
||||
readyLED = machine.LED
|
||||
|
||||
flashdev tinyfs.BlockDevice
|
||||
fs tinyfs.Filesystem
|
||||
|
||||
currdir = "/"
|
||||
|
||||
commands = map[string]cmdfunc{
|
||||
"": noop,
|
||||
"dbg": dbg,
|
||||
"lsblk": lsblk,
|
||||
"mount": mount,
|
||||
"umount": umount,
|
||||
"format": format,
|
||||
"xxd": xxd,
|
||||
"ls": ls,
|
||||
"samples": samples,
|
||||
"mkdir": mkdir,
|
||||
"cat": cat,
|
||||
"create": create,
|
||||
"write": write,
|
||||
"rm": rm,
|
||||
}
|
||||
)
|
||||
|
||||
type cmdfunc func(argv []string)
|
||||
|
||||
const (
|
||||
StateInput = iota
|
||||
StateEscape
|
||||
StateEscBrc
|
||||
StateCSI
|
||||
)
|
||||
|
||||
func RunFor(dev tinyfs.BlockDevice, filesys tinyfs.Filesystem) {
|
||||
flashdev = dev
|
||||
fs = filesys
|
||||
|
||||
readyLED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
readyLED.High()
|
||||
|
||||
readyLED.Low()
|
||||
println("SPI Configured. Reading flash info")
|
||||
|
||||
/*
|
||||
lfsConfig := flashlfs.NewConfig()
|
||||
if blockCount == 0 {
|
||||
lfsConfig.BlockCount = (flashdev.Attrs().TotalSize / lfsConfig.BlockSize) - startBlock
|
||||
} else {
|
||||
lfsConfig.BlockCount = blockCount
|
||||
}
|
||||
println("Start block:", startBlock)
|
||||
println("Block count:", lfsConfig.BlockCount)
|
||||
|
||||
blockdev = flashlfs.NewBlockDevice(flashdev, startBlock, lfsConfig.BlockSize)
|
||||
*/
|
||||
|
||||
prompt()
|
||||
|
||||
var state = StateInput
|
||||
|
||||
for i := 0; ; {
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
if debug {
|
||||
fmt.Printf("\rdata: %x\r\n\r", data)
|
||||
prompt()
|
||||
console.Write(input[:i])
|
||||
}
|
||||
switch state {
|
||||
case StateInput:
|
||||
switch data {
|
||||
case 0x8:
|
||||
fallthrough
|
||||
case 0x7f: // this is probably wrong... works on my machine tho :)
|
||||
// backspace
|
||||
if i > 0 {
|
||||
i -= 1
|
||||
console.Write([]byte{0x8, 0x20, 0x8})
|
||||
}
|
||||
case 13:
|
||||
// return key
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
if data != 10 {
|
||||
println("\r\nunexpected: \r", int(data))
|
||||
}
|
||||
}
|
||||
console.Write([]byte("\r\n"))
|
||||
runCommand(string(input[:i]))
|
||||
prompt()
|
||||
|
||||
i = 0
|
||||
continue
|
||||
case 27:
|
||||
// escape
|
||||
state = StateEscape
|
||||
default:
|
||||
// anything else, just echo the character if it is printable
|
||||
if strconv.IsPrint(rune(data)) {
|
||||
if i < (consoleBufLen - 1) {
|
||||
console.WriteByte(data)
|
||||
input[i] = data
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
case StateEscape:
|
||||
switch data {
|
||||
case 0x5b:
|
||||
state = StateEscBrc
|
||||
default:
|
||||
state = StateInput
|
||||
}
|
||||
default:
|
||||
// TODO: handle escape sequences
|
||||
state = StateInput
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(line string) {
|
||||
argv := strings.SplitN(strings.TrimSpace(line), " ", -1)
|
||||
cmd := argv[0]
|
||||
cmdfn, ok := commands[cmd]
|
||||
if !ok {
|
||||
println("unknown command: " + line)
|
||||
return
|
||||
}
|
||||
cmdfn(argv)
|
||||
}
|
||||
|
||||
func noop(argv []string) {}
|
||||
|
||||
func dbg(argv []string) {
|
||||
if debug {
|
||||
debug = false
|
||||
println("Console debugging off")
|
||||
} else {
|
||||
debug = true
|
||||
println("Console debugging on")
|
||||
}
|
||||
}
|
||||
|
||||
func lsblk(argv []string) {
|
||||
fmt.Printf("lsblk : not implement\r\n")
|
||||
}
|
||||
|
||||
func mount(argv []string) {
|
||||
if err := fs.Mount(); err != nil {
|
||||
println("Could not mount LittleFS filesystem: " + err.Error() + "\r\n")
|
||||
} else {
|
||||
println("Successfully mounted LittleFS filesystem.\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
func format(argv []string) {
|
||||
if err := fs.Format(); err != nil {
|
||||
println("Could not format LittleFS filesystem: " + err.Error() + "\r\n")
|
||||
} else {
|
||||
println("Successfully formatted LittleFS filesystem.\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
func umount(argv []string) {
|
||||
if err := fs.Unmount(); err != nil {
|
||||
println("Could not unmount LittleFS filesystem: " + err.Error() + "\r\n")
|
||||
} else {
|
||||
println("Successfully unmounted LittleFS filesystem.\r\n")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
var err error
|
||||
if fatfs == nil {
|
||||
fatfs, err = fat.New(fatdisk)
|
||||
if err != nil {
|
||||
fatfs = nil
|
||||
println("could not load FAT filesystem: " + err.Error() + "\r\n")
|
||||
}
|
||||
fmt.Printf("loaded fs\r\n")
|
||||
}
|
||||
if rootdir == nil {
|
||||
rootdir, err = fatfs.RootDir()
|
||||
if err != nil {
|
||||
rootdir = nil
|
||||
println("could not load rootdir: " + err.Error() + "\r\n")
|
||||
}
|
||||
fmt.Printf("loaded rootdir\r\n")
|
||||
}
|
||||
if currdir == nil {
|
||||
currdir = rootdir
|
||||
}
|
||||
*/
|
||||
|
||||
func ls(argv []string) {
|
||||
path := "/"
|
||||
if len(argv) > 1 {
|
||||
path = strings.TrimSpace(argv[1])
|
||||
}
|
||||
dir, err := fs.Open(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Could not open directory %s: %v\r\n", path, err)
|
||||
return
|
||||
}
|
||||
defer dir.Close()
|
||||
infos, err := dir.Readdir(0)
|
||||
_ = infos
|
||||
if err != nil {
|
||||
fmt.Printf("Could not read directory %s: %v\r\n", path, err)
|
||||
return
|
||||
}
|
||||
for _, info := range infos {
|
||||
s := "-rwxrwxrwx"
|
||||
if info.IsDir() {
|
||||
s = "drwxrwxrwx"
|
||||
}
|
||||
fmt.Printf("%s %5d %s\r\n", s, info.Size(), info.Name())
|
||||
}
|
||||
}
|
||||
|
||||
func mkdir(argv []string) {
|
||||
tgt := ""
|
||||
if len(argv) == 2 {
|
||||
tgt = strings.TrimSpace(argv[1])
|
||||
}
|
||||
if debug {
|
||||
println("Trying mkdir to " + tgt)
|
||||
}
|
||||
if tgt == "" {
|
||||
println("Usage: mkdir <target dir>")
|
||||
return
|
||||
}
|
||||
err := fs.Mkdir(tgt, 0777)
|
||||
if err != nil {
|
||||
println("Could not mkdir " + tgt + ": " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func rm(argv []string) {
|
||||
tgt := ""
|
||||
if len(argv) == 2 {
|
||||
tgt = strings.TrimSpace(argv[1])
|
||||
}
|
||||
if debug {
|
||||
println("Trying rm to " + tgt)
|
||||
}
|
||||
if tgt == "" {
|
||||
println("Usage: rm <target dir>")
|
||||
return
|
||||
}
|
||||
err := fs.Remove(tgt)
|
||||
if err != nil {
|
||||
println("Could not rm " + tgt + ": " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func samples(argv []string) {
|
||||
buf := make([]byte, 90)
|
||||
for i := 0; i < 5; i++ {
|
||||
name := fmt.Sprintf("file%d.txt", i)
|
||||
if bytes, err := createSampleFile(name, buf); err != nil {
|
||||
fmt.Printf("%s\r\n", err)
|
||||
return
|
||||
} else {
|
||||
fmt.Printf("wrote %d bytes to %s\r\n", bytes, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func create(argv []string) {
|
||||
tgt := ""
|
||||
if len(argv) == 2 {
|
||||
tgt = strings.TrimSpace(argv[1])
|
||||
}
|
||||
if debug {
|
||||
println("Trying create to " + tgt)
|
||||
}
|
||||
buf := make([]byte, 90)
|
||||
if bytes, err := createSampleFile(tgt, buf); err != nil {
|
||||
fmt.Printf("%s\r\n", err)
|
||||
return
|
||||
} else {
|
||||
fmt.Printf("wrote %d bytes to %s\r\n", bytes, tgt)
|
||||
}
|
||||
}
|
||||
|
||||
func write(argv []string) {
|
||||
tgt := ""
|
||||
if len(argv) == 2 {
|
||||
tgt = strings.TrimSpace(argv[1])
|
||||
}
|
||||
if debug {
|
||||
println("Trying receive to " + tgt)
|
||||
}
|
||||
buf := make([]byte, 1)
|
||||
f, err := fs.OpenFile(tgt, os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
if err != nil {
|
||||
fmt.Printf("error opening %s: %s\r\n", tgt, err.Error())
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
var n int
|
||||
for {
|
||||
if console.Buffered() > 0 {
|
||||
data, _ := console.ReadByte()
|
||||
switch data {
|
||||
case 0x04:
|
||||
fmt.Printf("wrote %d bytes to %s\r\n", n, tgt)
|
||||
return
|
||||
default:
|
||||
// anything else, just echo the character if it is printable
|
||||
if strconv.IsPrint(rune(data)) {
|
||||
console.WriteByte(data)
|
||||
}
|
||||
buf[0] = data
|
||||
if _, err := f.Write(buf); err != nil {
|
||||
fmt.Printf("\nerror writing: %s\r\n", err)
|
||||
return
|
||||
}
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createSampleFile(name string, buf []byte) (int, error) {
|
||||
for j := uint8(0); j < uint8(len(buf)); j++ {
|
||||
buf[j] = 0x20 + j
|
||||
}
|
||||
f, err := fs.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error opening %s: %s", name, err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
bytes, err := f.Write(buf)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("error writing %s: %s", name, err.Error())
|
||||
}
|
||||
return bytes, nil
|
||||
}
|
||||
|
||||
/*
|
||||
func cd(argv []string) {
|
||||
|
||||
if fatfs == nil || rootdir == nil {
|
||||
mnt(nil)
|
||||
}
|
||||
if len(argv) == 1 {
|
||||
currdir = rootdir
|
||||
return
|
||||
}
|
||||
tgt := ""
|
||||
if len(argv) == 2 {
|
||||
tgt = strings.TrimSpace(argv[1])
|
||||
}
|
||||
if debug {
|
||||
println("Trying to cd to " + tgt)
|
||||
}
|
||||
if tgt == "" {
|
||||
println("Usage: cd <target dir>")
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
println("Getting entry")
|
||||
}
|
||||
entry := currdir.Entry(tgt)
|
||||
if entry == nil {
|
||||
println("File not found: " + tgt)
|
||||
return
|
||||
}
|
||||
if !entry.IsDir() {
|
||||
println("Not a directory: " + tgt)
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
println("Getting dir")
|
||||
}
|
||||
cd, err := entry.Dir()
|
||||
if err != nil {
|
||||
println("Could not cd to " + tgt + ": " + err.Error())
|
||||
}
|
||||
currdir = cd
|
||||
}
|
||||
*/
|
||||
|
||||
func cat(argv []string) {
|
||||
tgt := ""
|
||||
if len(argv) == 2 {
|
||||
tgt = strings.TrimSpace(argv[1])
|
||||
}
|
||||
if debug {
|
||||
println("Trying to cat to " + tgt)
|
||||
}
|
||||
if tgt == "" {
|
||||
println("Usage: cat <target dir>")
|
||||
return
|
||||
}
|
||||
if debug {
|
||||
println("Getting entry")
|
||||
}
|
||||
f, err := fs.Open(tgt)
|
||||
if err != nil {
|
||||
println("Could not open: " + err.Error())
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
if f.IsDir() {
|
||||
println("Not a file: " + tgt)
|
||||
return
|
||||
}
|
||||
off := 0x0
|
||||
buf := make([]byte, 64)
|
||||
for {
|
||||
n, err := f.Read(buf)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
println("Error reading " + tgt + ": " + err.Error())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
xxdfprint(os.Stdout, uint32(off), buf[:n])
|
||||
off += n
|
||||
}
|
||||
}
|
||||
|
||||
func xxd(argv []string) {
|
||||
var err error
|
||||
var addr uint64 = 0x0
|
||||
var size int = 64
|
||||
switch len(argv) {
|
||||
case 3:
|
||||
if size, err = strconv.Atoi(argv[2]); err != nil {
|
||||
println("Invalid size argument: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
if size > 512 || size < 1 {
|
||||
fmt.Printf("Size of hexdump must be greater than 0 and less than %d\r\n", 512)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 2:
|
||||
/*
|
||||
if argv[1][:2] != "0x" {
|
||||
println("Invalid hex address (should start with 0x)")
|
||||
return
|
||||
}
|
||||
*/
|
||||
if addr, err = strconv.ParseUint(argv[1], 16, 32); err != nil {
|
||||
println("Invalid address: " + err.Error() + "\r\n")
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 1:
|
||||
// no args supplied, so nothing to do here, just use the defaults
|
||||
default:
|
||||
println("usage: xxd <hex address, ex: 0xA0> <size of hexdump in bytes>\r\n")
|
||||
return
|
||||
}
|
||||
buf := make([]byte, size)
|
||||
//bsz := uint64(flash.SectorSize)
|
||||
//blockdev.ReadBlock(uint32(addr/bsz), uint32(addr%bsz), buf)
|
||||
flashdev.ReadAt(buf, int64(addr))
|
||||
xxdfprint(os.Stdout, uint32(addr), buf)
|
||||
}
|
||||
|
||||
func xxdfprint(w io.Writer, offset uint32, b []byte) {
|
||||
var l int
|
||||
var buf16 = make([]byte, 16)
|
||||
for i, c := 0, len(b); i < c; i += 16 {
|
||||
l = i + 16
|
||||
if l >= c {
|
||||
l = c
|
||||
}
|
||||
fmt.Fprintf(w, "%08x: % x ", offset+uint32(i), b[i:l])
|
||||
for j, n := 0, l-i; j < 16; j++ {
|
||||
if j >= n || !strconv.IsPrint(rune(b[i+j])) || b[i+j] >= 0x80 {
|
||||
buf16[j] = '.'
|
||||
} else {
|
||||
buf16[j] = b[i+j]
|
||||
}
|
||||
}
|
||||
console.Write(buf16)
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
func prompt() {
|
||||
print("==> ")
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build feather_m4 feather_m4_can feather_nrf52840
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D10
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build grandcentral-m4
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI1
|
||||
sckPin = machine.SDCARD_SCK_PIN
|
||||
sdoPin = machine.SDCARD_SDO_PIN
|
||||
sdiPin = machine.SDCARD_SDI_PIN
|
||||
csPin = machine.SDCARD_CS_PIN
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build atsamd21,!p1am_100
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D2
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"github.com/tinygo-org/tinyfs/fatfs"
|
||||
"tinygo.org/x/drivers/examples/sdcard/tinyfs/console"
|
||||
"tinygo.org/x/drivers/sdcard"
|
||||
)
|
||||
|
||||
var (
|
||||
spi machine.SPI
|
||||
sckPin machine.Pin
|
||||
sdoPin machine.Pin
|
||||
sdiPin machine.Pin
|
||||
csPin machine.Pin
|
||||
ledPin machine.Pin
|
||||
)
|
||||
|
||||
func main() {
|
||||
waitSerial()
|
||||
|
||||
sd := sdcard.New(spi, sckPin, sdoPin, sdiPin, csPin)
|
||||
err := sd.Configure()
|
||||
if err != nil {
|
||||
fmt.Printf("%s\r\n", err.Error())
|
||||
for {
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
filesystem := fatfs.New(&sd)
|
||||
|
||||
// Configure FATFS with sector size (must match value in ff.h - use 512)
|
||||
filesystem.Configure(&fatfs.Config{
|
||||
SectorSize: 512,
|
||||
})
|
||||
|
||||
console.RunFor(&sd, filesystem)
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build p1am_100
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SDCARD_SPI
|
||||
sckPin = machine.SDCARD_SCK_PIN
|
||||
sdoPin = machine.SDCARD_SDO_PIN
|
||||
sdiPin = machine.SDCARD_SDI_PIN
|
||||
csPin = machine.SDCARD_SS_PIN
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build pygamer
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D4
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build pyportal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI0
|
||||
sckPin = machine.SPI0_SCK_PIN
|
||||
sdoPin = machine.SPI0_SDO_PIN
|
||||
sdiPin = machine.SPI0_SDI_PIN
|
||||
csPin = machine.D32 // SD_CS
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// +build wioterminal
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
func init() {
|
||||
spi = machine.SPI2
|
||||
sckPin = machine.SCK2
|
||||
sdoPin = machine.SDO2
|
||||
sdiPin = machine.SDI2
|
||||
csPin = machine.SS2
|
||||
|
||||
ledPin = machine.LED
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/servo"
|
||||
)
|
||||
|
||||
// Configuration for the Arduino Uno.
|
||||
// Please change the PWM and pin if you want to try this example on a different
|
||||
// board.
|
||||
var (
|
||||
pwm = machine.Timer1
|
||||
pin = machine.D9
|
||||
)
|
||||
|
||||
func main() {
|
||||
s, err := servo.New(pwm, pin)
|
||||
if err != nil {
|
||||
for {
|
||||
println("could not configure servo")
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
println("setting to 0°")
|
||||
s.SetMicroseconds(1000)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
println("setting to 45°")
|
||||
s.SetMicroseconds(1500)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
println("setting to 90°")
|
||||
s.SetMicroseconds(2000)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// This example shows how to use 128x64 display over I2C
|
||||
// Tested on Seeeduino XIAO Expansion Board https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
|
||||
//
|
||||
// According to manual, I2C address of the display is 0x78, but that's 8-bit address.
|
||||
// TinyGo operates on 7-bit addresses and respective 7-bit address would be 0x3C, which we use below.
|
||||
//
|
||||
// To learn more about different types of I2C addresses, please see following page
|
||||
// https://www.totalphase.com/support/articles/200349176-7-bit-8-bit-and-10-bit-I2C-Slave-Addressing
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
|
||||
"image/color"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/ssd1306"
|
||||
)
|
||||
|
||||
func main() {
|
||||
machine.I2C0.Configure(machine.I2CConfig{
|
||||
Frequency: machine.TWI_FREQ_400KHZ,
|
||||
})
|
||||
|
||||
display := ssd1306.NewI2C(machine.I2C0)
|
||||
display.Configure(ssd1306.Config{
|
||||
Address: 0x3C,
|
||||
Width: 128,
|
||||
Height: 64,
|
||||
})
|
||||
|
||||
display.ClearDisplay()
|
||||
|
||||
x := int16(0)
|
||||
y := int16(0)
|
||||
deltaX := int16(1)
|
||||
deltaY := int16(1)
|
||||
for {
|
||||
pixel := display.GetPixel(x, y)
|
||||
c := color.RGBA{255, 255, 255, 255}
|
||||
if pixel {
|
||||
c = color.RGBA{0, 0, 0, 255}
|
||||
}
|
||||
display.SetPixel(x, y, c)
|
||||
display.Display()
|
||||
|
||||
x += deltaX
|
||||
y += deltaY
|
||||
|
||||
if x == 0 || x == 127 {
|
||||
deltaX = -deltaX
|
||||
}
|
||||
|
||||
if y == 0 || y == 63 {
|
||||
deltaY = -deltaY
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/tm1637"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
tm := tm1637.New(machine.D2, machine.D3, 7) // clk, dio, brightness
|
||||
tm.Configure()
|
||||
|
||||
tm.ClearDisplay()
|
||||
|
||||
tm.DisplayText([]byte("Tiny"))
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
tm.ClearDisplay()
|
||||
|
||||
tm.DisplayChr(byte('G'), 1)
|
||||
tm.DisplayDigit(0, 2) // looks like O
|
||||
time.Sleep(time.Millisecond * 1000)
|
||||
|
||||
tm.DisplayClock(12, 59, true)
|
||||
|
||||
for i := uint8(0); i < 8; i++ {
|
||||
tm.Brightness(i)
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
|
||||
i := int16(0)
|
||||
for {
|
||||
tm.DisplayNumber(i)
|
||||
i++
|
||||
time.Sleep(time.Millisecond * 50)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/tone"
|
||||
)
|
||||
|
||||
var (
|
||||
// Configuration for the Adafruit Circuit Playground Bluefruit.
|
||||
pwm = machine.PWM0
|
||||
pin = machine.D12
|
||||
)
|
||||
|
||||
func main() {
|
||||
speaker, err := tone.New(pwm, pin)
|
||||
if err != nil {
|
||||
println("failed to configure PWM")
|
||||
return
|
||||
}
|
||||
|
||||
// Two tone siren.
|
||||
for {
|
||||
println("nee")
|
||||
speaker.SetNote(tone.B5)
|
||||
time.Sleep(time.Second / 2)
|
||||
|
||||
println("naw")
|
||||
speaker.SetNote(tone.A5)
|
||||
time.Sleep(time.Second / 2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// This example connects to Access Point and prints some info
|
||||
package main
|
||||
|
||||
import (
|
||||
"machine"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/wifinina"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = ""
|
||||
const pass = ""
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
)
|
||||
|
||||
func setup() {
|
||||
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: machine.NINA_SDO,
|
||||
SDI: machine.NINA_SDI,
|
||||
SCK: machine.NINA_SCK,
|
||||
})
|
||||
|
||||
adaptor = wifinina.New(spi,
|
||||
machine.NINA_CS,
|
||||
machine.NINA_ACK,
|
||||
machine.NINA_GPIO0,
|
||||
machine.NINA_RESETN)
|
||||
adaptor.Configure()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
setup()
|
||||
|
||||
waitSerial()
|
||||
|
||||
connectToAP()
|
||||
|
||||
for {
|
||||
println("----------------------------------------")
|
||||
printSSID()
|
||||
printRSSI()
|
||||
printMac()
|
||||
printIPs()
|
||||
printTime()
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func printSSID() {
|
||||
print("SSID: ")
|
||||
ssid, err := adaptor.GetCurrentSSID()
|
||||
if err != nil {
|
||||
println("Unknown (error: ", err.Error(), ")")
|
||||
return
|
||||
}
|
||||
println(ssid)
|
||||
}
|
||||
|
||||
func printRSSI() {
|
||||
print("RSSI: ")
|
||||
rssi, err := adaptor.GetCurrentRSSI()
|
||||
if err != nil {
|
||||
println("Unknown (error: ", err.Error(), ")")
|
||||
return
|
||||
}
|
||||
println(strconv.Itoa(int(rssi)))
|
||||
}
|
||||
|
||||
func printIPs() {
|
||||
ip, subnet, gateway, err := adaptor.GetIP()
|
||||
if err != nil {
|
||||
println("IP: Unknown (error: ", err.Error(), ")")
|
||||
return
|
||||
}
|
||||
println("IP: ", ip.String())
|
||||
println("Subnet: ", subnet.String())
|
||||
println("Gateway: ", gateway.String())
|
||||
}
|
||||
|
||||
func printTime() {
|
||||
print("Time: ")
|
||||
t, err := adaptor.GetTime()
|
||||
for {
|
||||
if err != nil {
|
||||
println("Unknown (error: ", err.Error(), ")")
|
||||
return
|
||||
}
|
||||
if t != 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
t, err = adaptor.GetTime()
|
||||
}
|
||||
println(time.Unix(int64(t), 0).String())
|
||||
}
|
||||
|
||||
func printMac() {
|
||||
print("MAC: ")
|
||||
mac, err := adaptor.GetMACAddress()
|
||||
if err != nil {
|
||||
println("Unknown (", err.Error(), ")")
|
||||
}
|
||||
println(mac.String())
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
if len(ssid) == 0 || len(pass) == 0 {
|
||||
for {
|
||||
println("Connection failed: Either ssid or password not set")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
println("Connecting to " + ssid)
|
||||
adaptor.SetPassphrase(ssid, pass)
|
||||
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
|
||||
println("Connection status: " + st.String())
|
||||
time.Sleep(1 * time.Second)
|
||||
st, _ = adaptor.GetConnectionStatus()
|
||||
}
|
||||
println("Connected.")
|
||||
}
|
||||
@@ -35,10 +35,7 @@ const server = "tcp://test.mosquitto.org:1883"
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
uart = machine.UART2
|
||||
tx = machine.NINA_TX
|
||||
rx = machine.NINA_RX
|
||||
spi = machine.NINA_SPI
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
@@ -48,7 +45,6 @@ var (
|
||||
func main() {
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
@@ -94,7 +90,7 @@ func main() {
|
||||
println(err.Error())
|
||||
}
|
||||
}
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
|
||||
@@ -32,12 +32,8 @@ const server = "tcp://test.mosquitto.org:1883"
|
||||
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
uart = machine.UART2
|
||||
tx = machine.NINA_TX
|
||||
rx = machine.NINA_RX
|
||||
spi = machine.NINA_SPI
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
@@ -55,7 +51,6 @@ func subHandler(client mqtt.Client, msg mqtt.Message) {
|
||||
func main() {
|
||||
time.Sleep(3000 * time.Millisecond)
|
||||
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
|
||||
@@ -23,33 +23,41 @@ const ntpHost = "129.6.15.29"
|
||||
|
||||
const NTP_PACKET_SIZE = 48
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
b = make([]byte, NTP_PACKET_SIZE)
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Init esp32
|
||||
func setup() {
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
machine.NINA_SPI.Configure(machine.SPIConfig{
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: machine.NINA_SDO,
|
||||
SDI: machine.NINA_SDI,
|
||||
SCK: machine.NINA_SCK,
|
||||
})
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
adaptor = wifinina.New(machine.NINA_SPI,
|
||||
adaptor = wifinina.New(spi,
|
||||
machine.NINA_CS,
|
||||
machine.NINA_ACK,
|
||||
machine.NINA_GPIO0,
|
||||
machine.NINA_RESETN)
|
||||
|
||||
adaptor.Configure()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
setup()
|
||||
|
||||
waitSerial()
|
||||
|
||||
// connect to access point
|
||||
connectToAP()
|
||||
|
||||
// now make UDP connection
|
||||
@@ -80,10 +88,13 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// Right now this code is never reached. Need a way to trigger it...
|
||||
println("Disconnecting UDP...")
|
||||
conn.Close()
|
||||
println("Done.")
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func getCurrentTime(conn *net.UDPSerialConn) (time.Time, error) {
|
||||
@@ -140,6 +151,12 @@ func clearBuffer() {
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
if len(ssid) == 0 || len(pass) == 0 {
|
||||
for {
|
||||
println("Connection failed: Either ssid or password not set")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
message("Connecting to " + ssid)
|
||||
adaptor.SetPassphrase(ssid, pass)
|
||||
|
||||
@@ -29,10 +29,7 @@ const serverIP = ""
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
uart = machine.UART2
|
||||
tx = machine.NINA_TX
|
||||
rx = machine.NINA_RX
|
||||
spi = machine.NINA_SPI
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
@@ -42,8 +39,6 @@ var buf = &bytes.Buffer{}
|
||||
|
||||
func main() {
|
||||
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// This example opens a TCP connection using a device with WiFiNINA firmware
|
||||
// and sends a HTTPS request to retrieve a webpage
|
||||
//
|
||||
// You shall see "strict-transport-security" header in the response,
|
||||
// this confirms communication is indeed over HTTPS
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
"tinygo.org/x/drivers/net/tls"
|
||||
"tinygo.org/x/drivers/wifinina"
|
||||
)
|
||||
|
||||
// access point info
|
||||
const ssid = ""
|
||||
const pass = ""
|
||||
|
||||
// IP address of the server aka "hub". Replace with your own info.
|
||||
const server = "tinygo.org"
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
// change these to connect to a different UART or pins for the ESP8266/ESP32
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
)
|
||||
|
||||
var buf [256]byte
|
||||
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
|
||||
func setup() {
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
SDO: machine.NINA_SDO,
|
||||
SDI: machine.NINA_SDI,
|
||||
SCK: machine.NINA_SCK,
|
||||
})
|
||||
|
||||
adaptor = wifinina.New(spi,
|
||||
machine.NINA_CS,
|
||||
machine.NINA_ACK,
|
||||
machine.NINA_GPIO0,
|
||||
machine.NINA_RESETN)
|
||||
adaptor.Configure()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
setup()
|
||||
|
||||
waitSerial()
|
||||
|
||||
connectToAP()
|
||||
|
||||
for {
|
||||
readConnection()
|
||||
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
|
||||
makeHTTPSRequest()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func readConnection() {
|
||||
if conn != nil {
|
||||
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
|
||||
if err != nil {
|
||||
println("Read error: " + err.Error())
|
||||
} else {
|
||||
print(string(buf[0:n]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeHTTPSRequest() {
|
||||
|
||||
var err error
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
message("\r\n---------------\r\nDialing TCP connection")
|
||||
conn, err = tls.Dial("tcp", server, nil)
|
||||
for ; err != nil; conn, err = tls.Dial("tcp", server, nil) {
|
||||
message("Connection failed: " + err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
println("Connected!\r")
|
||||
|
||||
print("Sending HTTPS request...")
|
||||
fmt.Fprintln(conn, "GET / HTTP/1.1")
|
||||
fmt.Fprintln(conn, "Host:", strings.Split(server, ":")[0])
|
||||
fmt.Fprintln(conn, "User-Agent: TinyGo")
|
||||
fmt.Fprintln(conn, "Connection: close")
|
||||
fmt.Fprintln(conn)
|
||||
println("Sent!\r\n\r")
|
||||
|
||||
lastRequestTime = time.Now()
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
if len(ssid) == 0 || len(pass) == 0 {
|
||||
for {
|
||||
println("Connection failed: Either ssid or password not set")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
message("Connecting to " + ssid)
|
||||
adaptor.SetPassphrase(ssid, pass)
|
||||
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
|
||||
message("Connection status: " + st.String())
|
||||
time.Sleep(1 * time.Second)
|
||||
st, _ = adaptor.GetConnectionStatus()
|
||||
}
|
||||
message("Connected.")
|
||||
time.Sleep(2 * time.Second)
|
||||
ip, _, _, err := adaptor.GetIP()
|
||||
for ; err != nil; ip, _, _, err = adaptor.GetIP() {
|
||||
message(err.Error())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
message(ip.String())
|
||||
}
|
||||
|
||||
func message(msg string) {
|
||||
println(msg, "\r")
|
||||
}
|
||||
@@ -27,10 +27,7 @@ const server = "tinygo.org"
|
||||
var (
|
||||
|
||||
// these are the default pins for the Arduino Nano33 IoT.
|
||||
uart = machine.UART2
|
||||
tx = machine.NINA_TX
|
||||
rx = machine.NINA_RX
|
||||
spi = machine.NINA_SPI
|
||||
spi = machine.NINA_SPI
|
||||
|
||||
// this is the ESP chip that has the WIFININA firmware flashed on it
|
||||
adaptor *wifinina.Device
|
||||
@@ -41,10 +38,7 @@ var buf [256]byte
|
||||
var lastRequestTime time.Time
|
||||
var conn net.Conn
|
||||
|
||||
func main() {
|
||||
|
||||
uart.Configure(machine.UARTConfig{TX: tx, RX: rx})
|
||||
|
||||
func setup() {
|
||||
// Configure SPI for 8Mhz, Mode 0, MSB First
|
||||
spi.Configure(machine.SPIConfig{
|
||||
Frequency: 8 * 1e6,
|
||||
@@ -59,16 +53,33 @@ func main() {
|
||||
machine.NINA_GPIO0,
|
||||
machine.NINA_RESETN)
|
||||
adaptor.Configure()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
setup()
|
||||
|
||||
waitSerial()
|
||||
|
||||
connectToAP()
|
||||
|
||||
for {
|
||||
loop()
|
||||
readConnection()
|
||||
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
|
||||
makeHTTPRequest()
|
||||
}
|
||||
}
|
||||
println("Done.")
|
||||
|
||||
}
|
||||
|
||||
func loop() {
|
||||
// Wait for user to open serial console
|
||||
func waitSerial() {
|
||||
for !machine.Serial.DTR() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func readConnection() {
|
||||
if conn != nil {
|
||||
for n, err := conn.Read(buf[:]); n > 0; n, err = conn.Read(buf[:]) {
|
||||
if err != nil {
|
||||
@@ -78,9 +89,6 @@ func loop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if time.Now().Sub(lastRequestTime).Milliseconds() >= 10000 {
|
||||
makeHTTPRequest()
|
||||
}
|
||||
}
|
||||
|
||||
func makeHTTPRequest() {
|
||||
@@ -98,7 +106,7 @@ func makeHTTPRequest() {
|
||||
message("\r\n---------------\r\nDialing TCP connection")
|
||||
conn, err = net.DialTCP("tcp", laddr, raddr)
|
||||
for ; err != nil; conn, err = net.DialTCP("tcp", laddr, raddr) {
|
||||
message("connection failed: " + err.Error())
|
||||
message("Connection failed: " + err.Error())
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
println("Connected!\r")
|
||||
@@ -106,7 +114,7 @@ func makeHTTPRequest() {
|
||||
print("Sending HTTP request...")
|
||||
fmt.Fprintln(conn, "GET / HTTP/1.1")
|
||||
fmt.Fprintln(conn, "Host:", server)
|
||||
fmt.Fprintln(conn, "User-Agent: TinyGo/0.10.0")
|
||||
fmt.Fprintln(conn, "User-Agent: TinyGo")
|
||||
fmt.Fprintln(conn, "Connection: close")
|
||||
fmt.Fprintln(conn)
|
||||
println("Sent!\r\n\r")
|
||||
@@ -114,19 +122,14 @@ func makeHTTPRequest() {
|
||||
lastRequestTime = time.Now()
|
||||
}
|
||||
|
||||
func readLine(conn *net.TCPSerialConn) string {
|
||||
println("Attempting to read...\r")
|
||||
b := buf[:]
|
||||
for expiry := time.Now().Unix() + 10; time.Now().Unix() > expiry; {
|
||||
if n, err := conn.Read(b); n > 0 && err == nil {
|
||||
return string(b[0:n])
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// connect to access point
|
||||
func connectToAP() {
|
||||
if len(ssid) == 0 || len(pass) == 0 {
|
||||
for {
|
||||
println("Connection failed: Either ssid or password not set")
|
||||
time.Sleep(10 * time.Second)
|
||||
}
|
||||
}
|
||||
time.Sleep(2 * time.Second)
|
||||
message("Connecting to " + ssid)
|
||||
adaptor.SetPassphrase(ssid, pass)
|
||||
|
||||
@@ -4,6 +4,7 @@ package main
|
||||
|
||||
import "machine"
|
||||
|
||||
// Replace neo in the code below to match the pin
|
||||
// Replace neo and led in the code below to match the pin
|
||||
// that you are using if different.
|
||||
var neo = machine.D2
|
||||
var led = machine.LED
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import "machine"
|
||||
|
||||
// This is the pin assignment for the Digispark only.
|
||||
// Replace neo in the code below to match the pin
|
||||
// Replace neo and led in the code below to match the pin
|
||||
// that you are using if different.
|
||||
var neo machine.Pin = 0
|
||||
var led = machine.LED
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
var leds [10]color.RGBA
|
||||
|
||||
func main() {
|
||||
led := machine.LED
|
||||
led.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
neo.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// +build !digispark,!arduino
|
||||
// +build !digispark,!arduino,!qtpy
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
// Replace neo in the code below to match the pin
|
||||
// Replace neo and led in the code below to match the pin
|
||||
// that you are using if different.
|
||||
var neo machine.Pin = machine.NEOPIXELS
|
||||
var led = machine.LED
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// +build qtpy
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
// Replace neo and led in the code below to match the pin
|
||||
// that you are using if different.
|
||||
var neo machine.Pin = machine.NEOPIXELS
|
||||
var led = machine.NoPin
|
||||
|
||||
func init() {
|
||||
pwr := machine.NEOPIXELS_POWER
|
||||
pwr.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
pwr.High()
|
||||
}
|
||||
+1
-2
@@ -290,9 +290,8 @@ func (dev *Device) EraseBlockSize() int64 {
|
||||
// supports this. The start and len parameters are in block numbers, use
|
||||
// EraseBlockSize to map addresses to blocks.
|
||||
func (dev *Device) EraseBlocks(start, len int64) error {
|
||||
// TODO: maybe combine sector erase operations into block erase operations
|
||||
for i := start; i < start+len; i++ {
|
||||
if err := dev.EraseSector(uint32(i)); err != nil {
|
||||
if err := dev.EraseBlock(uint32(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,10 @@ go 1.15
|
||||
require (
|
||||
github.com/eclipse/paho.mqtt.golang v1.2.0
|
||||
github.com/frankban/quicktest v1.10.2
|
||||
github.com/sago35/tinygo-dma v0.0.0-20210610020721-297675ab9b23 // indirect
|
||||
github.com/tinygo-org/tinyfs v0.0.0-20210514090915-924e60a7bcf8
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/sago35/tinygo-dma => ../../sago35/tinygo-dma
|
||||
)
|
||||
|
||||
@@ -9,5 +9,12 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/sago35/tinygo-dma v0.0.0-20210610020721-297675ab9b23 h1:ESMYhe6RQJ2RbBCH2OK1SNyYq3gKoBpI7g07aLjQgJI=
|
||||
github.com/sago35/tinygo-dma v0.0.0-20210610020721-297675ab9b23/go.mod h1:gXCCsg4cKOJ6NJl7teGN597XgN0hzQJz0Wekayj4k+M=
|
||||
github.com/tinygo-org/tinyfs v0.0.0-20210514004344-b02c062d12c9 h1:66or7MohOph3xr3gMGCXceLkd+MBoUbV/rPPjl8iQOU=
|
||||
github.com/tinygo-org/tinyfs v0.0.0-20210514004344-b02c062d12c9/go.mod h1:HGuyo42bGd1zWSuS1gwgyyjN36ZZMlEpwM0vSrglmXc=
|
||||
github.com/tinygo-org/tinyfs v0.0.0-20210514090915-924e60a7bcf8 h1:pYwuKe1XuNeJnGV1UjeZ0FhuZ5+lapIq1NUmGacbBwo=
|
||||
github.com/tinygo-org/tinyfs v0.0.0-20210514090915-924e60a7bcf8/go.mod h1:HGuyo42bGd1zWSuS1gwgyyjN36ZZMlEpwM0vSrglmXc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
|
||||
|
||||
+2
-3
@@ -4,7 +4,6 @@ package gps // import "tinygo.org/x/drivers/gps"
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"machine"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,13 +20,13 @@ type Device struct {
|
||||
buffer []byte
|
||||
bufIdx int
|
||||
sentence strings.Builder
|
||||
uart *machine.UART
|
||||
uart drivers.UART
|
||||
bus drivers.I2C
|
||||
address uint16
|
||||
}
|
||||
|
||||
// NewUART creates a new UART GPS connection. The UART must already be configured.
|
||||
func NewUART(uart *machine.UART) Device {
|
||||
func NewUART(uart drivers.UART) Device {
|
||||
return Device{
|
||||
uart: uart,
|
||||
buffer: make([]byte, bufferSize),
|
||||
|
||||
+55
-36
@@ -28,6 +28,35 @@ type Device struct {
|
||||
rd machine.Pin
|
||||
}
|
||||
|
||||
var cmdBuf [4]byte
|
||||
var 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
|
||||
}
|
||||
|
||||
// Configure prepares display for use
|
||||
func (d *Device) Configure(config Config) {
|
||||
|
||||
@@ -80,33 +109,6 @@ func (d *Device) Configure(config Config) {
|
||||
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 {
|
||||
@@ -146,8 +148,24 @@ func (d *Device) Display() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err1 = errors.New("rectangle coordinates outside display area")
|
||||
|
||||
// DrawRGBBitmap copies an RGB bitmap to the internal buffer at given coordinates
|
||||
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 err1
|
||||
}
|
||||
d.setWindow(x, y, w, h)
|
||||
d.startWrite()
|
||||
d.driver.write16sl(data)
|
||||
d.endWrite()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DrawRGBBitmap8 copies an RGB bitmap to the internal buffer at given coordinates
|
||||
func (d *Device) DrawRGBBitmap8(x, y int16, data []uint8, 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 {
|
||||
@@ -155,7 +173,7 @@ func (d *Device) DrawRGBBitmap(x, y int16, data []uint16, w, h int16) error {
|
||||
}
|
||||
d.setWindow(x, y, w, h)
|
||||
d.startWrite()
|
||||
d.driver.write16sl(data)
|
||||
d.driver.write8sl(data)
|
||||
d.endWrite()
|
||||
return nil
|
||||
}
|
||||
@@ -235,7 +253,8 @@ func (d *Device) SetRotation(rotation Rotation) {
|
||||
case 3:
|
||||
madctl = MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR
|
||||
}
|
||||
d.sendCommand(MADCTL, []uint8{madctl})
|
||||
cmdBuf[0] = madctl
|
||||
d.sendCommand(MADCTL, cmdBuf[:1])
|
||||
d.rotation = rotation
|
||||
}
|
||||
|
||||
@@ -266,16 +285,14 @@ func (d *Device) setWindow(x, y, w, h int16) {
|
||||
//y += d.rowOffset
|
||||
x1 := x + w - 1
|
||||
if x != d.x0 || x1 != d.x1 {
|
||||
d.sendCommand(CASET, []uint8{
|
||||
uint8(x >> 8), uint8(x), uint8(x1 >> 8), uint8(x1),
|
||||
})
|
||||
cmdBuf[0], cmdBuf[1], cmdBuf[2], cmdBuf[3] = uint8(x>>8), uint8(x), uint8(x1>>8), uint8(x1)
|
||||
d.sendCommand(CASET, cmdBuf[:4])
|
||||
d.x0, d.x1 = x, x1
|
||||
}
|
||||
y1 := y + h - 1
|
||||
if y != d.y0 || y1 != d.y1 {
|
||||
d.sendCommand(PASET, []uint8{
|
||||
uint8(y >> 8), uint8(y), uint8(y1 >> 8), uint8(y1),
|
||||
})
|
||||
cmdBuf[0], cmdBuf[1], cmdBuf[2], cmdBuf[3] = uint8(y>>8), uint8(y), uint8(y1>>8), uint8(y1)
|
||||
d.sendCommand(PASET, cmdBuf[:4])
|
||||
d.y0, d.y1 = y, y1
|
||||
}
|
||||
d.sendCommand(RAMWR, nil)
|
||||
@@ -300,7 +317,9 @@ func (d *Device) sendCommand(cmd byte, data []byte) {
|
||||
d.dc.Low()
|
||||
d.driver.write8(cmd)
|
||||
d.dc.High()
|
||||
d.driver.write8sl(data)
|
||||
if data != nil {
|
||||
d.driver.write8sl(data)
|
||||
}
|
||||
d.endWrite()
|
||||
}
|
||||
|
||||
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
// +build !atsamd51,!atsamd21
|
||||
|
||||
package ili9341
|
||||
|
||||
import (
|
||||
"device/sam"
|
||||
"machine"
|
||||
"unsafe"
|
||||
|
||||
dma "github.com/sago35/tinygo-dma"
|
||||
)
|
||||
|
||||
var (
|
||||
dbg5 = machine.D5
|
||||
dbg6 = machine.D6
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbg5.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
dbg6.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
var buf [64]byte
|
||||
|
||||
type spiDriver struct {
|
||||
bus machine.SPI
|
||||
}
|
||||
|
||||
var (
|
||||
dmatx *dma.DMA
|
||||
desc *dma.DMADescriptor
|
||||
)
|
||||
|
||||
func NewSPI(bus machine.SPI, dc, cs, rst machine.Pin) *Device {
|
||||
from := make([]byte, 256)
|
||||
for i := range from {
|
||||
from[i] = byte(i)
|
||||
}
|
||||
|
||||
dmatx = dma.NewDMA(func(d *dma.DMA) {
|
||||
d.Wait()
|
||||
return
|
||||
})
|
||||
dmatx.SetTrigger(dma.DMAC_CHANNEL_CHCTRLA_TRIGSRC_SERCOM1_TX)
|
||||
dmatx.SetTriggerAction(sam.DMAC_CHANNEL_CHCTRLA_TRIGACT_BURST)
|
||||
desc = dmatx.GetDescriptor()
|
||||
|
||||
return &Device{
|
||||
dc: dc,
|
||||
cs: cs,
|
||||
rst: rst,
|
||||
rd: machine.NoPin,
|
||||
driver: &spiDriver{
|
||||
bus: bus,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (pd *spiDriver) configure(config *Config) {
|
||||
}
|
||||
|
||||
func (pd *spiDriver) write8(b byte) {
|
||||
buf[0] = b
|
||||
pd.bus.Tx(buf[:1], nil)
|
||||
}
|
||||
|
||||
func (pd *spiDriver) write8n(b byte, n int) {
|
||||
buf[0] = b
|
||||
for i := 0; i < n; i++ {
|
||||
pd.bus.Tx(buf[:1], nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (pd *spiDriver) write8sl(b []byte) {
|
||||
if len(b) > 64 {
|
||||
desc.UpdateDescriptor(dma.DescriptorConfig{
|
||||
SRC: unsafe.Pointer(&b[0]),
|
||||
DST: unsafe.Pointer(&pd.bus.Bus.DATA.Reg),
|
||||
SRCINC: dma.DMAC_SRAM_BTCTRL_SRCINC_ENABLE,
|
||||
DSTINC: dma.DMAC_SRAM_BTCTRL_DSTINC_DISABLE,
|
||||
SIZE: uint32(len(b)), // Total size of DMA transfer
|
||||
BLOCKACT: 1,
|
||||
})
|
||||
dmatx.Start()
|
||||
return
|
||||
}
|
||||
|
||||
pd.bus.Tx(b, nil)
|
||||
}
|
||||
|
||||
func (pd *spiDriver) write16(data uint16) {
|
||||
buf[0] = uint8(data >> 8)
|
||||
buf[1] = uint8(data)
|
||||
pd.bus.Tx(buf[:2], nil)
|
||||
}
|
||||
|
||||
func (pd *spiDriver) write16n(data uint16, n int) {
|
||||
for i := 0; i < len(buf); i += 2 {
|
||||
buf[i] = uint8(data >> 8)
|
||||
buf[i+1] = uint8(data)
|
||||
}
|
||||
|
||||
for i := 0; i < (n >> 5); i++ {
|
||||
pd.bus.Tx(buf[:], nil)
|
||||
}
|
||||
|
||||
pd.bus.Tx(buf[:n%64], nil)
|
||||
}
|
||||
|
||||
func (pd *spiDriver) write16sl(data []uint16) {
|
||||
if len(data) > 64 {
|
||||
desc.UpdateDescriptor(dma.DescriptorConfig{
|
||||
SRC: unsafe.Pointer(&data[0]),
|
||||
DST: unsafe.Pointer(&pd.bus.Bus.DATA.Reg),
|
||||
SRCINC: dma.DMAC_SRAM_BTCTRL_SRCINC_ENABLE,
|
||||
DSTINC: dma.DMAC_SRAM_BTCTRL_DSTINC_DISABLE,
|
||||
SIZE: uint32(len(data) * 2), // Total size of DMA transfer
|
||||
BLOCKACT: 1,
|
||||
})
|
||||
dmatx.Start()
|
||||
return
|
||||
}
|
||||
for i, c := 0, len(data); i < c; i++ {
|
||||
buf[0] = uint8(data[i] >> 8)
|
||||
buf[1] = uint8(data[i])
|
||||
pd.bus.Tx(buf[:2], nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package ina260
|
||||
|
||||
import "tinygo.org/x/drivers"
|
||||
|
||||
// Device wraps an I2C connection to an INA260 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
}
|
||||
|
||||
// Config holds the configuration of the INA260 device.
|
||||
type Config struct {
|
||||
// One of AVGMODE_XXX
|
||||
AverageMode byte
|
||||
|
||||
// One of CONVTIME_XXXXUSEC
|
||||
VoltConvTime byte
|
||||
|
||||
// One of CONVTIME_XXXXUSEC
|
||||
CurrentConvTime byte
|
||||
|
||||
// Multiple of MODE_XXXX
|
||||
Mode byte
|
||||
}
|
||||
|
||||
// New creates a new INA260 connection. The I2C bus must already be
|
||||
// configured.
|
||||
//
|
||||
// This function only creates the Device object, it does not touch the device.
|
||||
func New(bus drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: bus,
|
||||
Address: Address,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure sets up the device.
|
||||
//
|
||||
// This only needs to be called to override built-in defaults. By default,
|
||||
// the device starts with:
|
||||
//
|
||||
// * AverageMode = AVGMODE_1
|
||||
// * VoltConvTime = CONVTIME_1100USEC
|
||||
// * CurrentConvTime = CONVTIME_1100USEC
|
||||
// * Mode = MODE_CONTINUOUS | MODE_VOLTAGE | MODE_CURRENT
|
||||
//
|
||||
func (d *Device) Configure(cfg Config) {
|
||||
var val uint16
|
||||
|
||||
val = uint16(cfg.AverageMode&0x7) << 9
|
||||
val |= uint16(cfg.VoltConvTime&0x7) << 6
|
||||
val |= uint16(cfg.CurrentConvTime&0x7) << 3
|
||||
val |= uint16(cfg.Mode & 0x7)
|
||||
|
||||
d.WriteRegister(REG_CONFIG, val)
|
||||
}
|
||||
|
||||
// Resets the device, setting all registers to default values
|
||||
func (d *Device) Reset() {
|
||||
d.WriteRegister(REG_CONFIG, 0x8000)
|
||||
}
|
||||
|
||||
// Connected returns whether an INA260 has been found.
|
||||
func (d *Device) Connected() bool {
|
||||
return d.ReadRegister(REG_MANF_ID) == MANF_ID &&
|
||||
(d.ReadRegister(REG_DIE_ID)&DEVICE_ID_MASK) == DEVICE_ID
|
||||
}
|
||||
|
||||
// Gets the measured current in µA (max resolution 1.25mA)
|
||||
func (d *Device) Current() int32 {
|
||||
val := d.ReadRegister(REG_CURRENT)
|
||||
|
||||
if val&0x8000 == 0 {
|
||||
return int32(val) * 1250
|
||||
}
|
||||
|
||||
// Two's complement, convert to signed int
|
||||
return -(int32(^val) + 1) * 1250
|
||||
}
|
||||
|
||||
// Gets the measured voltage in µV (max resolution 1.25mV)
|
||||
func (d *Device) Voltage() int32 {
|
||||
val := d.ReadRegister(REG_BUSVOLTAGE)
|
||||
|
||||
if val&0x8000 == 0 {
|
||||
return int32(val) * 1250
|
||||
}
|
||||
|
||||
// Two's complement, convert to signed int
|
||||
return -(int32(^val) + 1) * 1250
|
||||
}
|
||||
|
||||
// Gets the measured power in µW (max resolution 10mW)
|
||||
func (d *Device) Power() int32 {
|
||||
return int32(d.ReadRegister(REG_POWER)) * 10000
|
||||
}
|
||||
|
||||
// Read a register
|
||||
func (d *Device) ReadRegister(reg uint8) uint16 {
|
||||
data := []byte{0, 0}
|
||||
d.bus.ReadRegister(uint8(d.Address), reg, data)
|
||||
return (uint16(data[0]) << 8) | uint16(data[1])
|
||||
}
|
||||
|
||||
// Write to a register
|
||||
func (d *Device) WriteRegister(reg uint8, v uint16) {
|
||||
data := []byte{0, 0}
|
||||
data[0] = byte(v >> 8)
|
||||
data[1] = byte(v & 0xff)
|
||||
|
||||
d.bus.WriteRegister(uint8(d.Address), reg, data)
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package ina260
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"tinygo.org/x/drivers/tester"
|
||||
)
|
||||
|
||||
func TestDefaultI2CAddress(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
dev := New(bus)
|
||||
c.Assert(dev.Address, qt.Equals, uint16(Address))
|
||||
}
|
||||
|
||||
func TestConnected(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = defaultRegisters()
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
c.Assert(dev.Connected(), qt.Equals, true)
|
||||
}
|
||||
|
||||
func TestVoltage(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = defaultRegisters()
|
||||
fake.Registers[REG_BUSVOLTAGE] = 0x2570
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
// Datasheet: 2570h = 11.98V = 11980mV = 11980000uV
|
||||
c.Assert(dev.Voltage(), qt.Equals, int32(11980000))
|
||||
}
|
||||
|
||||
func TestCurrent(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = defaultRegisters()
|
||||
fake.Registers[REG_CURRENT] = 0x2710
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
// Datasheet: 2710h = 12.5A = 12500mA = 12500000uA
|
||||
c.Assert(dev.Current(), qt.Equals, int32(12500000))
|
||||
}
|
||||
|
||||
func TestPower(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := tester.NewI2CBus(c)
|
||||
fake := tester.NewI2CDevice16(c, Address)
|
||||
fake.Registers = defaultRegisters()
|
||||
fake.Registers[REG_POWER] = 0x3A7F
|
||||
bus.AddDevice(fake)
|
||||
|
||||
dev := New(bus)
|
||||
// 3A7Fh = 149.75W = 149750mW = 149750000uW
|
||||
c.Assert(dev.Power(), qt.Equals, int32(149750000))
|
||||
}
|
||||
|
||||
// defaultRegisters returns the default values for all of the device's registers.
|
||||
// set TI INA260 datasheet for power-on defaults
|
||||
func defaultRegisters() map[uint8]uint16 {
|
||||
return map[uint8]uint16{
|
||||
REG_CONFIG: 0x6127,
|
||||
REG_CURRENT: 0x0000,
|
||||
REG_BUSVOLTAGE: 0x0000,
|
||||
REG_POWER: 0x0000,
|
||||
REG_MASKENABLE: 0x0000,
|
||||
REG_ALERTLIMIT: 0x0000,
|
||||
REG_MANF_ID: 0x5449,
|
||||
REG_DIE_ID: 0x2270,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ina260
|
||||
|
||||
// The default I2C address for this device.
|
||||
//
|
||||
// The actual address is configurable by connecting address pins.
|
||||
const Address = 0x40
|
||||
|
||||
// Registers
|
||||
const (
|
||||
REG_CONFIG = 0x00
|
||||
REG_CURRENT = 0x01
|
||||
REG_BUSVOLTAGE = 0x02
|
||||
REG_POWER = 0x03
|
||||
REG_MASKENABLE = 0x06
|
||||
REG_ALERTLIMIT = 0x07
|
||||
REG_MANF_ID = 0xFE
|
||||
REG_DIE_ID = 0xFF
|
||||
)
|
||||
|
||||
// Well-Known Values
|
||||
const (
|
||||
MANF_ID = 0x5449 // TI
|
||||
DEVICE_ID = 0x2270 // 227h
|
||||
DEVICE_ID_MASK = 0xFFF0
|
||||
|
||||
AVGMODE_1 = 0
|
||||
AVGMODE_4 = 1
|
||||
AVGMODE_16 = 2
|
||||
AVGMODE_64 = 3
|
||||
AVGMODE_128 = 4
|
||||
AVGMODE_256 = 5
|
||||
AVGMODE_512 = 6
|
||||
AVGMODE_1024 = 7
|
||||
|
||||
CONVTIME_140USEC = 0
|
||||
CONVTIME_204USEC = 1
|
||||
CONVTIME_332USEC = 2
|
||||
CONVTIME_588USEC = 3
|
||||
CONVTIME_1100USEC = 4 // 1.1 ms
|
||||
CONVTIME_2116USEC = 5 // 2.1 ms
|
||||
CONVTIME_4156USEC = 6 // 4.2 ms
|
||||
CONVTIME_8244USEC = 7 // 8.2 ms
|
||||
|
||||
MODE_CONTINUOUS = 0x4
|
||||
MODE_TRIGGERED = 0x0
|
||||
MODE_VOLTAGE = 0x2
|
||||
MODE_NO_VOLTAGE = 0x0
|
||||
MODE_CURRENT = 0x1
|
||||
MODE_NO_CURRENT = 0x0
|
||||
)
|
||||
@@ -0,0 +1,107 @@
|
||||
package keypad4x4
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
// NoKeyPressed is used, when no key was pressed
|
||||
const NoKeyPressed = 255
|
||||
|
||||
// Device is used as 4x4 keypad driver
|
||||
type Device interface {
|
||||
Configure()
|
||||
GetKey() uint8
|
||||
GetIndices() (int, int)
|
||||
}
|
||||
|
||||
// device is a driver for 4x4 keypads
|
||||
type device struct {
|
||||
inputEnabled bool
|
||||
lastColumn int
|
||||
lastRow int
|
||||
columns [4]machine.Pin
|
||||
rows [4]machine.Pin
|
||||
mapping [4][4]uint8
|
||||
}
|
||||
|
||||
// takes r4 -r1 pins and c4 - c1 pins
|
||||
func NewDevice(r4, r3, r2, r1, c4, c3, c2, c1 machine.Pin) Device {
|
||||
result := &device{}
|
||||
result.columns = [4]machine.Pin{c4, c3, c2, c1}
|
||||
result.rows = [4]machine.Pin{r4, r3, r2, r1}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Configure sets the column pins as input and the row pins as output
|
||||
func (keypad *device) Configure() {
|
||||
inputConfig := machine.PinConfig{Mode: machine.PinInputPullup}
|
||||
for i := range keypad.columns {
|
||||
keypad.columns[i].Configure(inputConfig)
|
||||
}
|
||||
|
||||
outputConfig := machine.PinConfig{Mode: machine.PinOutput}
|
||||
for i := range keypad.rows {
|
||||
keypad.rows[i].Configure(outputConfig)
|
||||
keypad.rows[i].High()
|
||||
}
|
||||
|
||||
keypad.mapping = [4][4]uint8{
|
||||
{0, 1, 2, 3},
|
||||
{4, 5, 6, 7},
|
||||
{8, 9, 10, 11},
|
||||
{12, 13, 14, 15},
|
||||
}
|
||||
|
||||
keypad.inputEnabled = true
|
||||
keypad.lastColumn = -1
|
||||
keypad.lastRow = -1
|
||||
}
|
||||
|
||||
// GetKey returns the code for the given key.
|
||||
// The codes start with 0 at the upper left end of the keypad and end with 15 at the lower right end of the keypad
|
||||
// Example:
|
||||
// 0 1 2 3
|
||||
// 4 5 6 7
|
||||
// 8 9 10 11
|
||||
// 12 13 14 15
|
||||
// returns 255 for no keyPressed
|
||||
func (keypad *device) GetKey() uint8 {
|
||||
row, column := keypad.GetIndices()
|
||||
if row == -1 && column == -1 {
|
||||
return NoKeyPressed
|
||||
}
|
||||
|
||||
return keypad.mapping[row][column]
|
||||
}
|
||||
|
||||
// GetIndices returns the position of the pressed key
|
||||
func (keypad *device) GetIndices() (int, int) {
|
||||
for rowIndex, rowPin := range keypad.rows {
|
||||
rowPin.Low()
|
||||
|
||||
for columnIndex := range keypad.columns {
|
||||
columnPin := keypad.columns[columnIndex]
|
||||
|
||||
if !columnPin.Get() && keypad.inputEnabled {
|
||||
keypad.inputEnabled = false
|
||||
|
||||
keypad.lastColumn = columnIndex
|
||||
keypad.lastRow = rowIndex
|
||||
|
||||
return keypad.lastRow, keypad.lastColumn
|
||||
}
|
||||
|
||||
if columnPin.Get() &&
|
||||
columnIndex == keypad.lastColumn &&
|
||||
rowIndex == keypad.lastRow &&
|
||||
!keypad.inputEnabled {
|
||||
keypad.inputEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
rowPin.High()
|
||||
}
|
||||
|
||||
return -1, -1
|
||||
}
|
||||
+38
-16
@@ -56,49 +56,71 @@ func (d *Device) Stop() {
|
||||
d.en.Low()
|
||||
}
|
||||
|
||||
// PWM is the interface necessary for controlling the motor driver.
|
||||
type PWM interface {
|
||||
Configure(config machine.PWMConfig) error
|
||||
Channel(pin machine.Pin) (channel uint8, err error)
|
||||
Top() uint32
|
||||
Set(channel uint8, value uint32)
|
||||
SetPeriod(period uint64) error
|
||||
}
|
||||
|
||||
// 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
|
||||
spc uint8
|
||||
pwm 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 {
|
||||
// NewWithSpeed returns a new PWMMotor driver that uses an already configured PWM channel
|
||||
// to control speed.
|
||||
func NewWithSpeed(direction1, direction2 machine.Pin, spc uint8, pwm PWM) PWMDevice {
|
||||
return PWMDevice{
|
||||
a1: direction1,
|
||||
a2: direction2,
|
||||
en: speedPin,
|
||||
a1: direction1,
|
||||
a2: direction2,
|
||||
spc: spc,
|
||||
pwm: pwm,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the PWMDevice.
|
||||
func (d *PWMDevice) Configure() {
|
||||
// Configure configures the PWMDevice. Note that the PWM interface and
|
||||
// channel must already be configured, this function will not do it for you.
|
||||
func (d *PWMDevice) Configure() error {
|
||||
d.a1.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.a2.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.en.Configure()
|
||||
|
||||
d.Stop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Forward turns motor on in forward direction at specific speed.
|
||||
func (d *PWMDevice) Forward(speed uint16) {
|
||||
// Forward turns motor on in forward direction at specific speed as a percentage.
|
||||
func (d *PWMDevice) Forward(speed uint32) {
|
||||
if speed > 100 {
|
||||
speed = 100
|
||||
}
|
||||
|
||||
d.a1.High()
|
||||
d.a2.Low()
|
||||
d.en.Set(speed)
|
||||
d.pwm.Set(d.spc, d.pwm.Top()*speed/100)
|
||||
}
|
||||
|
||||
// Backward turns motor on in backward direction at specific speed.
|
||||
func (d *PWMDevice) Backward(speed uint16) {
|
||||
// Backward turns motor on in backward direction at specific speed as a percentage.
|
||||
func (d *PWMDevice) Backward(speed uint32) {
|
||||
if speed > 100 {
|
||||
speed = 100
|
||||
}
|
||||
|
||||
d.a1.Low()
|
||||
d.a2.High()
|
||||
d.en.Set(speed)
|
||||
d.pwm.Set(d.spc, d.pwm.Top()*speed/100)
|
||||
}
|
||||
|
||||
// Stop turns motor off.
|
||||
func (d *PWMDevice) Stop() {
|
||||
d.a1.Low()
|
||||
d.a2.Low()
|
||||
d.en.Set(0)
|
||||
d.pwm.Set(d.spc, 0)
|
||||
}
|
||||
|
||||
+29
-19
@@ -49,42 +49,52 @@ func (d *Device) Stop() {
|
||||
d.ib.Low()
|
||||
}
|
||||
|
||||
// PWM is the interface necessary for controlling the motor driver.
|
||||
type PWM interface {
|
||||
Configure(config machine.PWMConfig) error
|
||||
Channel(pin machine.Pin) (channel uint8, err error)
|
||||
Top() uint32
|
||||
Set(channel uint8, value uint32)
|
||||
SetPeriod(period uint64) error
|
||||
}
|
||||
|
||||
// PWMDevice is a motor with speed control.
|
||||
// ia and ib are the directional/speed PWM pins.
|
||||
type PWMDevice struct {
|
||||
ia, ib machine.PWM
|
||||
pwm PWM
|
||||
ca, cb uint8
|
||||
}
|
||||
|
||||
// NewWithSpeed returns a new PWMMotor driver that uses 2 PWM pins to control both direction and speed.
|
||||
func NewWithSpeed(direction1, direction2 machine.PWM) PWMDevice {
|
||||
func NewWithSpeed(ca, cb uint8, pwm PWM) PWMDevice {
|
||||
return PWMDevice{
|
||||
ia: direction1,
|
||||
ib: direction2,
|
||||
pwm: pwm,
|
||||
ca: ca,
|
||||
cb: cb,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure configures the PWMDevice.
|
||||
func (d *PWMDevice) Configure() {
|
||||
d.ia.Configure()
|
||||
d.ib.Configure()
|
||||
|
||||
// Configure configures the PWMDevice. Note that the pins, PWM interface,
|
||||
// and channels must all already be configured.
|
||||
func (d *PWMDevice) Configure() (err error) {
|
||||
d.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
// Forward turns motor on in forward direction at specific speed.
|
||||
func (d *PWMDevice) Forward(speed uint16) {
|
||||
d.ia.Set(speed)
|
||||
d.ib.Set(0)
|
||||
// Forward turns motor on in forward direction at specific speed as a percentage.
|
||||
func (d *PWMDevice) Forward(speed uint32) {
|
||||
d.pwm.Set(d.ca, d.pwm.Top()*speed/100)
|
||||
d.pwm.Set(d.cb, 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)
|
||||
// Backward turns motor on in backward direction at specific speed as a percentage.
|
||||
func (d *PWMDevice) Backward(speed uint32) {
|
||||
d.pwm.Set(d.ca, 0)
|
||||
d.pwm.Set(d.cb, d.pwm.Top()*speed/100)
|
||||
}
|
||||
|
||||
// Stop turns motor off.
|
||||
func (d *PWMDevice) Stop() {
|
||||
d.ia.Set(0)
|
||||
d.ib.Set(0)
|
||||
d.pwm.Set(d.ca, 0)
|
||||
d.pwm.Set(d.cb, 0)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// Driver works for max7219 and 7221
|
||||
// Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf
|
||||
package max72xx
|
||||
|
||||
import (
|
||||
"machine"
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
cs machine.Pin
|
||||
}
|
||||
|
||||
// NewDriver creates a new max7219 connection. The SPI wire must already be configured
|
||||
// The SPI frequency must not be higher than 10MHz.
|
||||
// parameter cs: the datasheet also refers to this pin as "load" pin.
|
||||
func NewDevice(bus machine.SPI, cs machine.Pin) *Device {
|
||||
return &Device{
|
||||
bus: bus,
|
||||
cs: cs,
|
||||
}
|
||||
}
|
||||
|
||||
// Configure setups the pins.
|
||||
func (driver *Device) Configure() {
|
||||
outPutConfig := machine.PinConfig{Mode: machine.PinOutput}
|
||||
|
||||
driver.cs.Configure(outPutConfig)
|
||||
}
|
||||
|
||||
// SetScanLimit sets the scan limit. Maximum is 8.
|
||||
// Example: a 4 digit 7SegmentDisplay has a scan limit of 4
|
||||
func (driver *Device) SetScanLimit(digitNumber uint8) {
|
||||
driver.WriteCommand(byte(REG_SCANLIMIT), byte(digitNumber-1))
|
||||
}
|
||||
|
||||
// SetDecodeMode sets the decode mode for 7 segment displays.
|
||||
// digitNumber = 1 -> 1 digit gets decoded
|
||||
// digitNumber = 2 or 3, or 4 -> 4 digit are being decoded
|
||||
// digitNumber = 8 -> 8 digits are being decoded
|
||||
// digitNumber 0 || digitNumber > 8 -> no decoding is being used
|
||||
func (driver *Device) SetDecodeMode(digitNumber uint8) {
|
||||
switch digitNumber {
|
||||
case 1: // only decode first digit
|
||||
driver.WriteCommand(REG_DECODE_MODE, 0x01)
|
||||
case 2, 3, 4: // decode digits 3-0
|
||||
driver.WriteCommand(REG_DECODE_MODE, 0x0F)
|
||||
case 8: // decode 8 digits
|
||||
driver.WriteCommand(REG_DECODE_MODE, 0xFF)
|
||||
default:
|
||||
driver.WriteCommand(REG_DECODE_MODE, 0x00)
|
||||
}
|
||||
}
|
||||
|
||||
// StartShutdownMode sets the IC into a low power shutdown mode.
|
||||
func (driver *Device) StartShutdownMode() {
|
||||
driver.WriteCommand(REG_SHUTDOWN, 0x00)
|
||||
}
|
||||
|
||||
// StartShutdownMode sets the IC into normal operation mode.
|
||||
func (driver *Device) StopShutdownMode() {
|
||||
driver.WriteCommand(REG_SHUTDOWN, 0x01)
|
||||
}
|
||||
|
||||
// StartDisplayTest starts a display test.
|
||||
func (driver *Device) StartDisplayTest() {
|
||||
driver.WriteCommand(REG_DISPLAY_TEST, 0x01)
|
||||
}
|
||||
|
||||
// StopDisplayTest stops the display test and gets into normal operation mode.
|
||||
func (driver *Device) StopDisplayTest() {
|
||||
driver.WriteCommand(REG_DISPLAY_TEST, 0x00)
|
||||
}
|
||||
|
||||
func (driver *Device) writeByte(data byte) {
|
||||
driver.bus.Transfer(data)
|
||||
}
|
||||
|
||||
// WriteCommand write data to a given register.
|
||||
func (driver *Device) WriteCommand(register, data byte) {
|
||||
driver.cs.Low()
|
||||
driver.writeByte(register)
|
||||
driver.writeByte(data)
|
||||
driver.cs.High()
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package max72xx
|
||||
|
||||
const (
|
||||
REG_NOOP byte = 0x00
|
||||
REG_DIGIT0 byte = 0x01
|
||||
REG_DIGIT1 byte = 0x02
|
||||
REG_DIGIT2 byte = 0x03
|
||||
REG_DIGIT3 byte = 0x04
|
||||
REG_DIGIT4 byte = 0x05
|
||||
REG_DIGIT5 byte = 0x06
|
||||
REG_DIGIT6 byte = 0x07
|
||||
REG_DIGIT7 byte = 0x08
|
||||
REG_DECODE_MODE byte = 0x09 // turn of for led matrix, turn on for digits
|
||||
REG_INTENSITY byte = 0x0A
|
||||
REG_SCANLIMIT byte = 0x0B
|
||||
REG_SHUTDOWN byte = 0x0C // turn on for no shutdown mode
|
||||
REG_DISPLAY_TEST byte = 0x0F // turn off for no display test
|
||||
)
|
||||
@@ -211,7 +211,7 @@ func TestInitWithError(t *testing.T) {
|
||||
c.Assert(dev, qt.IsNil)
|
||||
}
|
||||
|
||||
func newDevice(bus *tester.I2CBus, addr uint8) *tester.I2CDevice {
|
||||
func newDevice(bus *tester.I2CBus, addr uint8) *tester.I2CDevice8 {
|
||||
fdev := bus.NewDevice(addr)
|
||||
// IODIRA and IODIRB are all ones by default.
|
||||
fdev.Registers[rIODIR] = 0xff
|
||||
|
||||
@@ -0,0 +1,786 @@
|
||||
// Package mcp2515 implements a driver for the MCP2515 CAN Controller.
|
||||
//
|
||||
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf
|
||||
//
|
||||
// Reference: https://github.com/coryjfowler/MCP_CAN_lib
|
||||
package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps MCP2515 SPI CAN Module.
|
||||
type Device struct {
|
||||
spi SPI
|
||||
cs machine.Pin
|
||||
msg *CANMsg
|
||||
mcpMode byte
|
||||
}
|
||||
|
||||
// CANMsg stores CAN message fields.
|
||||
type CANMsg struct {
|
||||
ID uint32
|
||||
Dlc uint8
|
||||
Data []byte
|
||||
Ext bool
|
||||
Rtr bool
|
||||
}
|
||||
|
||||
const (
|
||||
bufferSize int = 64
|
||||
)
|
||||
|
||||
// New returns a new MCP2515 driver. Pass in a fully configured SPI bus.
|
||||
func New(b drivers.SPI, csPin machine.Pin) *Device {
|
||||
d := &Device{
|
||||
spi: SPI{
|
||||
bus: b,
|
||||
tx: make([]byte, 0, bufferSize),
|
||||
rx: make([]byte, 0, bufferSize),
|
||||
},
|
||||
cs: csPin,
|
||||
msg: &CANMsg{},
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Configure sets up the device for communication.
|
||||
func (d *Device) Configure() {
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
}
|
||||
|
||||
const beginTimeoutValue int = 10
|
||||
|
||||
// Begin starts the CAN controller.
|
||||
func (d *Device) Begin(speed byte, clock byte) error {
|
||||
timeOutCount := 0
|
||||
for {
|
||||
err := d.init(speed, clock)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
timeOutCount++
|
||||
if timeOutCount >= beginTimeoutValue {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Received returns true if CAN message is received.
|
||||
func (d *Device) Received() bool {
|
||||
res, err := d.readStatus()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// if RX STATUS INSTRUCTION result is not 0x00 (= No RX message)
|
||||
// TODO: reconsider this logic
|
||||
return (res & mcpStatRxifMask) != 0x00
|
||||
}
|
||||
|
||||
// Rx returns received CAN message.
|
||||
func (d *Device) Rx() (*CANMsg, error) {
|
||||
err := d.readMsg()
|
||||
return d.msg, err
|
||||
}
|
||||
|
||||
// Tx transmits CAN Message.
|
||||
func (d *Device) Tx(canid uint32, dlc uint8, data []byte) error {
|
||||
// TODO: add ext, rtrBit, waitSent
|
||||
timeoutCount := 0
|
||||
|
||||
var bufNum, res uint8
|
||||
var err error
|
||||
res = mcpAlltxbusy
|
||||
for res == mcpAlltxbusy && (timeoutCount < timeoutvalue) {
|
||||
if timeoutCount > 0 {
|
||||
time.Sleep(time.Microsecond * 10)
|
||||
}
|
||||
bufNum, res, err = d.getNextFreeTxBuf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
timeoutCount++
|
||||
}
|
||||
if timeoutCount == timeoutvalue {
|
||||
return fmt.Errorf("Tx: Tx timeout")
|
||||
}
|
||||
err = d.writeCANMsg(bufNum, canid, 0, 0, dlc, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) init(speed, clock byte) error {
|
||||
err := d.Reset()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := d.setCANCTRLMode(modeConfig); err != nil {
|
||||
return fmt.Errorf("setCANCTRLMode %s: ", err)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
// set baudrate
|
||||
if err := d.configRate(speed, clock); err != nil {
|
||||
return fmt.Errorf("configRate %s: ", err)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
if err := d.initCANBuffers(); err != nil {
|
||||
return fmt.Errorf("initCANBuffers: %s ", err)
|
||||
}
|
||||
if err := d.setRegister(mcpCANINTE, mcpRX0IF|mcpRX1IF); err != nil {
|
||||
return fmt.Errorf("setRegister: %s ", err)
|
||||
}
|
||||
if err := d.modifyRegister(mcpRXB0CTRL, mcpRxbRxMask|mcpRxbBuktMask, mcpRxbRxStdExt|mcpRxbBuktMask); err != nil {
|
||||
return fmt.Errorf("modifyRegister: %s ", err)
|
||||
}
|
||||
if err := d.modifyRegister(mcpRXB1CTRL, mcpRxbRxMask, mcpRxbRxStdExt); err != nil {
|
||||
return fmt.Errorf("modifyRegister: %s ", err)
|
||||
}
|
||||
|
||||
if err := d.setMode(modeNormal); err != nil {
|
||||
return fmt.Errorf("setMode %s: ", err)
|
||||
}
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset resets mcp2515.
|
||||
func (d *Device) Reset() error {
|
||||
d.cs.Low()
|
||||
_, err := d.spi.readWrite(mcpReset)
|
||||
d.cs.High()
|
||||
// time.Sleep(time.Microsecond * 4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
time.Sleep(time.Millisecond * 10)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) setCANCTRLMode(newMode byte) error {
|
||||
// If the chip is asleep and we want to change mode then a manual wake needs to be done
|
||||
// This is done by setting the wake up interrupt flag
|
||||
// This undocumented trick was found at https://github.com/mkleemann/can/blob/master/can_sleep_mcp2515.c
|
||||
m, err := d.getMode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m == modeSleep && newMode != modeSleep {
|
||||
r, err := d.readRegister(mcpCANINTE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wakeIntEnabled := (r & mcpWAKIF) == 0x00
|
||||
if !wakeIntEnabled {
|
||||
d.modifyRegister(mcpCANINTE, mcpWAKIF, mcpWAKIF)
|
||||
}
|
||||
// Set wake flag (this does the actual waking up)
|
||||
d.modifyRegister(mcpCANINTF, mcpWAKIF, mcpWAKIF)
|
||||
|
||||
// Wait for the chip to exit SLEEP and enter LISTENONLY mode.
|
||||
|
||||
// If the chip is not connected to a CAN bus (or the bus has no other powered nodes) it will sometimes trigger the wake interrupt as soon
|
||||
// as it's put to sleep, but it will stay in SLEEP mode instead of automatically switching to LISTENONLY mode.
|
||||
// In this situation the mode needs to be manually set to LISTENONLY.
|
||||
|
||||
if err := d.requestNewMode(modeListenOnly); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Turn wake interrupt back off if it was originally off
|
||||
if !wakeIntEnabled {
|
||||
d.modifyRegister(mcpCANINTE, mcpWAKIF, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear wake flag
|
||||
d.modifyRegister(mcpCANINTF, mcpWAKIF, 0)
|
||||
|
||||
return d.requestNewMode(newMode)
|
||||
}
|
||||
|
||||
func (d *Device) setMode(opMode byte) error {
|
||||
if opMode != modeSleep {
|
||||
d.mcpMode = opMode
|
||||
}
|
||||
|
||||
err := d.setCANCTRLMode(opMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) getMode() (byte, error) {
|
||||
r, err := d.readRegister(mcpCANSTAT)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r & modeMask, nil
|
||||
}
|
||||
|
||||
func (d *Device) configRate(speed, clock byte) error {
|
||||
// TODO: add another baudrate
|
||||
var cfg1, cfg2, cfg3 byte
|
||||
set := true
|
||||
switch clock {
|
||||
case Clock16MHz:
|
||||
switch speed {
|
||||
case CAN500kBps:
|
||||
cfg1 = mcp16mHz500kBpsCfg1
|
||||
cfg2 = mcp16mHz500kBpsCfg2
|
||||
cfg3 = mcp16mHz500kBpsCfg3
|
||||
case CAN1000kBps:
|
||||
cfg1 = mcp16mHz1000kBpsCfg1
|
||||
cfg2 = mcp16mHz1000kBpsCfg2
|
||||
cfg3 = mcp16mHz1000kBpsCfg3
|
||||
default:
|
||||
set = false
|
||||
}
|
||||
case Clock8MHz:
|
||||
switch speed {
|
||||
case CAN500kBps:
|
||||
cfg1 = mcp8mHz500kBpsCfg1
|
||||
cfg2 = mcp8mHz500kBpsCfg2
|
||||
cfg3 = mcp8mHz500kBpsCfg3
|
||||
case CAN1000kBps:
|
||||
cfg1 = mcp8mHz1000kBpsCfg1
|
||||
cfg2 = mcp8mHz1000kBpsCfg2
|
||||
cfg3 = mcp8mHz1000kBpsCfg3
|
||||
default:
|
||||
set = false
|
||||
}
|
||||
default:
|
||||
set = false
|
||||
}
|
||||
if !set {
|
||||
return errors.New("invalid parameter")
|
||||
}
|
||||
if err := d.setRegister(mcpCNF1, cfg1); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.setRegister(mcpCNF2, cfg2); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.setRegister(mcpCNF3, cfg3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) initCANBuffers() error {
|
||||
a1 := byte(mcpTXB0CTRL)
|
||||
a2 := byte(mcpTXB1CTRL)
|
||||
a3 := byte(mcpTXB2CTRL)
|
||||
for i := 0; i < 14; i++ {
|
||||
if err := d.setRegister(a1, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.setRegister(a2, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.setRegister(a3, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
a1++
|
||||
a2++
|
||||
a3++
|
||||
}
|
||||
|
||||
if err := d.setRegister(mcpRXB0CTRL, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.setRegister(mcpRXB1CTRL, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) readMsg() error {
|
||||
status, err := d.readRxTxStatus()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status & mcpRX0IF) == 0x01 {
|
||||
err := d.readRxBuffer(mcpReadRx0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if (status & mcpRX1IF) == 0x02 {
|
||||
err := d.readRxBuffer(mcpReadRx1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("readMsg: nothing is received")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) readRxBuffer(loadAddr uint8) error {
|
||||
msg := d.msg
|
||||
d.cs.Low()
|
||||
defer d.cs.High()
|
||||
_, err := d.spi.readWrite(loadAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.spi.read(4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := d.spi.rx
|
||||
msg.ID = uint32((uint32(buf[0]) << 3) + (uint32(buf[1]) >> 5))
|
||||
msg.Ext = false
|
||||
if (buf[1] & mcpTxbExideM) == mcpTxbExideM {
|
||||
// extended id
|
||||
msg.ID = uint32(uint32(msg.ID<<2) + uint32(buf[1]&0x03))
|
||||
msg.ID = uint32(uint32(msg.ID<<8) + uint32(buf[2]))
|
||||
msg.ID = uint32(uint32(msg.ID<<8) + uint32(buf[3]))
|
||||
msg.Ext = true
|
||||
}
|
||||
err = d.spi.read(1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgSize := d.spi.rx[0]
|
||||
msg.Dlc = uint8(msgSize & mcpDlcMask)
|
||||
msg.Rtr = false
|
||||
if (msgSize & mcpRtrMask) == 0x40 {
|
||||
msg.Rtr = true
|
||||
}
|
||||
readLen := uint8(canMaxCharInMessage)
|
||||
if msg.Dlc < canMaxCharInMessage {
|
||||
readLen = msg.Dlc
|
||||
}
|
||||
err = d.spi.read(int(readLen))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msg.Data = d.spi.rx
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) getNextFreeTxBuf() (uint8, uint8, error) {
|
||||
status, err := d.readStatus()
|
||||
if err != nil {
|
||||
return 0, mcpAlltxbusy, err
|
||||
}
|
||||
status &= mcpStatTxPendingMask
|
||||
|
||||
bufNum := uint8(0x00)
|
||||
|
||||
if status == mcpStatTxPendingMask {
|
||||
return 0, mcpAlltxbusy, nil
|
||||
}
|
||||
|
||||
for i := 0; i < int(mcpNTxbuffers-nReservedTx(0)); i++ {
|
||||
if (status & txStatusPendingFlag(uint8(i))) == 0 {
|
||||
bufNum = txCtrlReg(uint8(i)) + 1
|
||||
d.modifyRegister(mcpCANINTF, txIfFlag(uint8(i)), 0)
|
||||
return bufNum, mcp2515Ok, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, mcpAlltxbusy, nil
|
||||
}
|
||||
|
||||
func (d *Device) writeCANMsg(bufNum uint8, canid uint32, ext, rtrBit, dlc uint8, data []byte) error {
|
||||
d.cs.Low()
|
||||
defer d.cs.High()
|
||||
_, err := d.spi.readWrite(txSidhToLoad(bufNum))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.spi.clearBuffer(tx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.spi.setTxBufData(canid, ext, rtrBit, dlc, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.spi.write()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Since cs.Low and cs.High are executed in d.startTransmission,
|
||||
// it is necessary to set cs.High once to separate the instruction of mcp2515.
|
||||
d.cs.High()
|
||||
|
||||
err = d.startTransmission(bufNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SPI) setTxBufData(canid uint32, ext, rtrBit, dlc uint8, data []byte) error {
|
||||
canid = canid & 0x0FFFF
|
||||
if ext == 1 {
|
||||
// TODO: add Extended ID
|
||||
err := s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err := s.setTxData(byte(canid >> 3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(byte((canid & 0x07) << 5))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setTxData(0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if rtrBit == 1 {
|
||||
dlc |= mcpRtrMask
|
||||
} else {
|
||||
dlc |= (0)
|
||||
}
|
||||
err := s.setTxData(dlc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, d := range data {
|
||||
err := s.setTxData(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) startTransmission(bufNum uint8) error {
|
||||
d.cs.Low()
|
||||
_, err := d.spi.readWrite(txSidhToRTS(bufNum))
|
||||
d.cs.High()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func nReservedTx(number uint8) uint8 {
|
||||
if number < mcpNTxbuffers {
|
||||
return number
|
||||
}
|
||||
return mcpNTxbuffers - 1
|
||||
}
|
||||
|
||||
func txStatusPendingFlag(i uint8) uint8 {
|
||||
ret := uint8(0)
|
||||
switch i {
|
||||
case 0:
|
||||
ret = mcpStatTx0Pending
|
||||
case 1:
|
||||
ret = mcpStatTx1Pending
|
||||
case 2:
|
||||
ret = mcpStatTx2Pending
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func txCtrlReg(status uint8) uint8 {
|
||||
ret := uint8(0)
|
||||
switch status {
|
||||
case 0:
|
||||
ret = mcpTXB0CTRL
|
||||
case 1:
|
||||
ret = mcpTXB1CTRL
|
||||
case 2:
|
||||
ret = mcpTXB2CTRL
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func txIfFlag(i uint8) uint8 {
|
||||
ret := uint8(0)
|
||||
switch i {
|
||||
case 0:
|
||||
ret = mcpTX0IF
|
||||
case 1:
|
||||
ret = mcpTX1IF
|
||||
case 2:
|
||||
ret = mcpTX2IF
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func txSidhToSidh(i uint8) uint8 {
|
||||
ret := uint8(0)
|
||||
switch i {
|
||||
case mcpTX0IF:
|
||||
ret = mcpTXB0SIDH
|
||||
case mcpTX1IF:
|
||||
ret = mcpTXB1SIDH
|
||||
case mcpTX2IF:
|
||||
ret = mcpTXB2SIDH
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func txSidhToRTS(i uint8) uint8 {
|
||||
ret := uint8(0)
|
||||
switch i {
|
||||
case mcpTXB0SIDH:
|
||||
ret = mcpRtsTx0
|
||||
case mcpTXB1SIDH:
|
||||
ret = mcpRtsTx1
|
||||
case mcpTXB2SIDH:
|
||||
ret = mcpRtsTx2
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func txSidhToLoad(i uint8) uint8 {
|
||||
ret := uint8(0)
|
||||
switch i {
|
||||
case mcpTXB0SIDH:
|
||||
ret = mcpLoadTx0
|
||||
case mcpTXB1SIDH:
|
||||
ret = mcpLoadTx1
|
||||
case mcpTXB2SIDH:
|
||||
ret = mcpLoadTx2
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (d *Device) setRegister(addr, value byte) error {
|
||||
d.cs.Low()
|
||||
defer d.cs.High()
|
||||
_, err := d.spi.readWrite(mcpWrite)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = d.spi.readWrite(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = d.spi.readWrite(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// time.Sleep(time.Microsecond * 4)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) readRegister(addr byte) (byte, error) {
|
||||
d.cs.Low()
|
||||
defer d.cs.High()
|
||||
_, err := d.spi.readWrite(mcpRead)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
_, err = d.spi.readWrite(addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = d.spi.read(1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// time.Sleep(time.Microsecond * 4)
|
||||
return d.spi.rx[0], nil
|
||||
}
|
||||
|
||||
func (d *Device) modifyRegister(addr, mask, data byte) error {
|
||||
d.cs.Low()
|
||||
defer d.cs.High()
|
||||
_, err := d.spi.readWrite(mcpBitMod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = d.spi.readWrite(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = d.spi.readWrite(mask)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = d.spi.readWrite(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// time.Sleep(time.Microsecond * 4)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) requestNewMode(newMode byte) error {
|
||||
s := time.Now()
|
||||
for {
|
||||
err := d.modifyRegister(mcpCANCTRL, modeMask, newMode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r, err := d.readRegister(mcpCANSTAT)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r&modeMask == newMode {
|
||||
return nil
|
||||
} else if e := time.Now(); e.Sub(s) > 200*time.Millisecond {
|
||||
return errors.New("requestNewMode max time expired")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) readStatus() (byte, error) {
|
||||
d.cs.Low()
|
||||
defer d.cs.High()
|
||||
_, err := d.spi.readWrite(mcpReadStatus)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = d.spi.read(1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return d.spi.rx[0], nil
|
||||
}
|
||||
|
||||
func (d *Device) readRxTxStatus() (byte, error) {
|
||||
status, err := d.readStatus()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
ret := status & (mcpStatTxifMask | mcpStatRxifMask)
|
||||
if (status & mcpStatTx0if) == 0x08 {
|
||||
ret |= mcpTX0IF
|
||||
}
|
||||
if (status & mcpStatTx1if) == 0x20 {
|
||||
ret |= mcpTX1IF
|
||||
}
|
||||
if (status & mcpStatTx2if) == 0x80 {
|
||||
ret |= mcpTX2IF
|
||||
}
|
||||
ret |= ret & mcpStatRxifMask
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type SPI struct {
|
||||
bus drivers.SPI
|
||||
tx []byte
|
||||
rx []byte
|
||||
}
|
||||
|
||||
const (
|
||||
tx = iota
|
||||
rx
|
||||
)
|
||||
|
||||
func (s *SPI) readWrite(w byte) (byte, error) {
|
||||
return s.bus.Transfer(w)
|
||||
}
|
||||
|
||||
func (s *SPI) read(readLength int) error {
|
||||
err := s.clearBuffer(rx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.setBufferLength(readLength, rx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.bus.Tx(nil, s.rx)
|
||||
}
|
||||
|
||||
func (s *SPI) write() error {
|
||||
return s.bus.Tx(s.tx, nil)
|
||||
}
|
||||
|
||||
func (s *SPI) clearBuffer(dir int) error { return s.setBufferLength(0, dir) }
|
||||
|
||||
func (s *SPI) setBufferLength(length int, dir int) error {
|
||||
if dir == tx {
|
||||
if length > cap(s.tx) {
|
||||
return fmt.Errorf("length is longer than capacity")
|
||||
}
|
||||
s.tx = s.tx[:length]
|
||||
} else if dir == rx {
|
||||
if length > cap(s.rx) {
|
||||
return fmt.Errorf("length is longer than capacity")
|
||||
}
|
||||
s.rx = s.rx[:length]
|
||||
} else {
|
||||
return fmt.Errorf("invalid direction")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SPI) setTxData(data byte) error {
|
||||
if len(s.tx) >= bufferSize {
|
||||
return fmt.Errorf("cannot expand buffer (to avoid memory allocation)")
|
||||
}
|
||||
s.tx = append(s.tx, data)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) dumpMode() error {
|
||||
m, err := d.getMode()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Mode: %02X\r\n", m)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Device) dumpRegister(addr byte) error {
|
||||
r, err := d.readRegister(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Register: %02X = %02X\r\n", addr, r)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
// Package mcp2515 implements a driver for the MCP2515 CAN Controller.
|
||||
//
|
||||
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2515-Stand-Alone-CAN-Controller-with-SPI-20001801J.pdf
|
||||
//
|
||||
package mcp2515 // import "tinygo.org/x/drivers/mcp2515"
|
||||
|
||||
const DebugEn = 0
|
||||
|
||||
const (
|
||||
// begin mt
|
||||
|
||||
timeoutvalue = 50
|
||||
mcpSidh = 0
|
||||
mcpSidl = 1
|
||||
mcpEid8 = 2
|
||||
mcpEid0 = 3
|
||||
|
||||
mcpTxbExideM = 0x08 // in txbnsidl
|
||||
mcpDlcMask = 0x0f //= 4 lsbits
|
||||
mcpRtrMask = 0x40 // =(1<=<6) bit= 6
|
||||
|
||||
mcpRxbRxAny = 0x60
|
||||
mcpRxbRxExt = 0x40
|
||||
mcpRxbRxStd = 0x20
|
||||
mcpRxbRxStdExt = 0x00
|
||||
mcpRxbRxMask = 0x60
|
||||
mcpRxbBuktMask = 1 << 2
|
||||
|
||||
// bits in the txbnctrl registers.
|
||||
|
||||
mcpTxbTxbufeM = 0x80
|
||||
mcpTxbAbtfM = 0x40
|
||||
mcpTxbMloaM = 0x20
|
||||
mcpTxbTxerrM = 0x10
|
||||
mcpTxbTxreqM = 0x08
|
||||
mcpTxbTxieM = 0x04
|
||||
mcpTxbTxp10M = 0x03
|
||||
|
||||
mcpTxbRtrM = 0x40 // in txbndlc
|
||||
mcpRxbIdeM = 0x08 // in rxbnsidl
|
||||
mcpRxbRtrM = 0x40 // in rxbndlc
|
||||
|
||||
mcpStatTxPendingMask = 0x54
|
||||
mcpStatTx0Pending = 0x04
|
||||
mcpStatTx1Pending = 0x10
|
||||
mcpStatTx2Pending = 0x40
|
||||
mcpStatTxifMask = 0xa8
|
||||
mcpStatTx0if = 0x08
|
||||
mcpStatTx1if = 0x20
|
||||
mcpStatTx2if = 0x80
|
||||
mcpStatRxifMask = 0x03
|
||||
mcpStatRx0if = 1 << 0
|
||||
mcpStatRx1if = 1 << 1
|
||||
|
||||
mcpEflgRx1ovr = 1 << 7
|
||||
mcpEflgRx0ovr = 1 << 6
|
||||
mcpEflgTxbo = 1 << 5
|
||||
mcpEflgTxep = 1 << 4
|
||||
mcpEflgRxep = 1 << 3
|
||||
mcpEflgTxwar = 1 << 2
|
||||
mcpEflgRxwar = 1 << 1
|
||||
mcpEflgEwarn = 1 << 0
|
||||
mcpEflgErrormask = 0xf8 //= 5 ms-bits
|
||||
|
||||
// define mcp2515 register addresses
|
||||
|
||||
mcpRXF0SIDH = 0x00
|
||||
mcpRXF0SIDL = 0x01
|
||||
mcpRXF0EID8 = 0x02
|
||||
mcpRXF0EID0 = 0x03
|
||||
mcpRXF1SIDH = 0x04
|
||||
mcpRXF1SIDL = 0x05
|
||||
mcpRXF1EID8 = 0x06
|
||||
mcpRXF1EID0 = 0x07
|
||||
mcpRXF2SIDH = 0x08
|
||||
mcpRXF2SIDL = 0x09
|
||||
mcpRXF2EID8 = 0x0a
|
||||
mcpRXF2EID0 = 0x0b
|
||||
mcpBFPCTRL = 0x0c
|
||||
mcpTXRTSCTRl = 0x0d
|
||||
mcpCANSTAT = 0x0e
|
||||
mcpCANCTRL = 0x0f
|
||||
mcpRXF3SIDH = 0x10
|
||||
mcpRXF3SIDL = 0x11
|
||||
mcpRXF3EID8 = 0x12
|
||||
mcpRXF3EID0 = 0x13
|
||||
mcpRXF4SIDH = 0x14
|
||||
mcpRXF4SIDL = 0x15
|
||||
mcpRXF4EID8 = 0x16
|
||||
mcpRXF4EID0 = 0x17
|
||||
mcpRXF5SIDH = 0x18
|
||||
mcpRXF5SIDL = 0x19
|
||||
mcpRXF5EID8 = 0x1a
|
||||
mcpRXF5EID0 = 0x1b
|
||||
mcpTEC = 0x1c
|
||||
mcpREC = 0x1d
|
||||
mcpRXM0SIDH = 0x20
|
||||
mcpRXM0SIDL = 0x21
|
||||
mcpRXM0EID8 = 0x22
|
||||
mcpRXM0EID0 = 0x23
|
||||
mcpRXM1SIDH = 0x24
|
||||
mcpRXM1SIDL = 0x25
|
||||
mcpRXM1EID8 = 0x26
|
||||
mcpRXM1EID0 = 0x27
|
||||
mcpCNF3 = 0x28
|
||||
mcpCNF2 = 0x29
|
||||
mcpCNF1 = 0x2a
|
||||
mcpCANINTE = 0x2b
|
||||
mcpCANINTF = 0x2c
|
||||
mcpEFLG = 0x2d
|
||||
mcpTXB0CTRL = 0x30
|
||||
mcpTXB0SIDH = 0x31
|
||||
mcpTXB1CTRL = 0x40
|
||||
mcpTXB1SIDH = 0x41
|
||||
mcpTXB2CTRL = 0x50
|
||||
mcpTXB2SIDH = 0x51
|
||||
mcpRXB0CTRL = 0x60
|
||||
mcpRXB0SIDH = 0x61
|
||||
mcpRXB1CTRL = 0x70
|
||||
mcpRXB1SIDH = 0x71
|
||||
|
||||
mcpTxInt = 0x1c // enable all transmit interrup ts
|
||||
mcpTx01Int = 0x0c // enable txb0 and txb1 interru pts
|
||||
mcpRxInt = 0x03 // enable receive interrupts
|
||||
mcpNoInt = 0x00 // disable all interrupts
|
||||
|
||||
mcpTx01Mask = 0x14
|
||||
mcpTxMask = 0x54
|
||||
|
||||
// define spi instruction set
|
||||
mcpWrite = 0x02
|
||||
mcpRead = 0x03
|
||||
mcpBitMod = 0x05
|
||||
mcpLoadTx0 = 0x40
|
||||
mcpLoadTx1 = 0x42
|
||||
mcpLoadTx2 = 0x44
|
||||
|
||||
mcpRtsTx0 = 0x81
|
||||
mcpRtsTx1 = 0x82
|
||||
mcpRtsTx2 = 0x84
|
||||
mcpRtsAll = 0x87
|
||||
mcpReadRx0 = 0x90
|
||||
mcpReadRx1 = 0x94
|
||||
mcpReadStatus = 0xa0
|
||||
mcpRxStatus = 0xb0
|
||||
mcpReset = 0xc0
|
||||
|
||||
// canctrl register values
|
||||
|
||||
modeNormal = 0x00
|
||||
modeSleep = 0x20
|
||||
modeLoopBack = 0x40
|
||||
modeListenOnly = 0x60
|
||||
modeConfig = 0x80
|
||||
modePowerUp = 0xe0
|
||||
modeMask = 0xe0
|
||||
abortTx = 0x10
|
||||
modeOneShot = 0x08
|
||||
clkoutEnable = 0x04
|
||||
clkoutDisable = 0x00
|
||||
clkoutPs1 = 0x00
|
||||
clkoutPs2 = 0x01
|
||||
clkoutPs4 = 0x02
|
||||
clkoutPs8 = 0x03
|
||||
|
||||
// cnf1 register values
|
||||
|
||||
sjw1 = 0x00
|
||||
sjw2 = 0x40
|
||||
sjw3 = 0x80
|
||||
sjw4 = 0xc0
|
||||
|
||||
// cnf2 register values
|
||||
|
||||
btlmode = 0x80
|
||||
sample1x = 0x00
|
||||
sample3x = 0x40
|
||||
|
||||
// cnf3 register values
|
||||
|
||||
sofEnable = 0x80
|
||||
sofDisable = 0x00
|
||||
wakfilEnable = 0x40
|
||||
wakfilDisable = 0x00
|
||||
|
||||
// canintf register bits
|
||||
|
||||
mcpRX0IF = 0x01
|
||||
mcpRX1IF = 0x02
|
||||
mcpTX0IF = 0x04
|
||||
mcpTX1IF = 0x08
|
||||
mcpTX2IF = 0x10
|
||||
mcpERRIF = 0x20
|
||||
mcpWAKIF = 0x40
|
||||
mcpMERRF = 0x80
|
||||
|
||||
// bfpctrl register bits
|
||||
|
||||
b1bfs = 0x20
|
||||
b0bfs = 0x10
|
||||
b1bfe = 0x08
|
||||
b0bfe = 0x04
|
||||
b1bfm = 0x02
|
||||
b0bfm = 0x01
|
||||
|
||||
// txrtctrl register bits
|
||||
|
||||
b2rts = 0x20
|
||||
b1rts = 0x10
|
||||
b0rts = 0x08
|
||||
b2rtsm = 0x04
|
||||
b1rtsm = 0x02
|
||||
b0rtsm = 0x01
|
||||
|
||||
// clock
|
||||
|
||||
Clock16MHz = 1
|
||||
Clock8MHz = 2
|
||||
|
||||
// speed= 16m
|
||||
|
||||
mcp16mHz1000kBpsCfg1 = 0x00
|
||||
mcp16mHz1000kBpsCfg2 = 0xd0
|
||||
mcp16mHz1000kBpsCfg3 = 0x82
|
||||
|
||||
mcp16mHz500kBpsCfg1 = 0x00
|
||||
mcp16mHz500kBpsCfg2 = 0xf0
|
||||
mcp16mHz500kBpsCfg3 = 0x86
|
||||
|
||||
mcp16mHz250kBpsCfg1 = 0x41
|
||||
mcp16mHz250kBpsCfg2 = 0xf1
|
||||
mcp16mHz250kBpsCfg3 = 0x85
|
||||
|
||||
mcp16mHz200kBpsCfg1 = 0x01
|
||||
mcp16mHz200kBpsCfg2 = 0xfa
|
||||
mcp16mHz200kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz125kBpsCfg1 = 0x03
|
||||
mcp16mHz125kBpsCfg2 = 0xf0
|
||||
mcp16mHz125kBpsCfg3 = 0x86
|
||||
|
||||
mcp16mHz100kBpsCfg1 = 0x03
|
||||
mcp16mHz100kBpsCfg2 = 0xfa
|
||||
mcp16mHz100kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz95kBpsCfg1 = 0x03
|
||||
mcp16mHz95kBpsCfg2 = 0xad
|
||||
mcp16mHz95kBpsCfg3 = 0x07
|
||||
|
||||
mcp16mHz83k3BpsCfg1 = 0x03
|
||||
mcp16mHz83k3BpsCfg2 = 0xbe
|
||||
mcp16mHz83k3BpsCfg3 = 0x07
|
||||
|
||||
mcp16mHz80kBpsCfg1 = 0x03
|
||||
mcp16mHz80kBpsCfg2 = 0xff
|
||||
mcp16mHz80kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz50kBpsCfg1 = 0x07
|
||||
mcp16mHz50kBpsCfg2 = 0xfa
|
||||
mcp16mHz50kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz40kBpsCfg1 = 0x07
|
||||
mcp16mHz40kBpsCfg2 = 0xff
|
||||
mcp16mHz40kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz33kBpsCfg1 = 0x09
|
||||
mcp16mHz33kBpsCfg2 = 0xbe
|
||||
mcp16mHz33kBpsCfg3 = 0x07
|
||||
|
||||
mcp16mHz31k25BpsCfg1 = 0x0f
|
||||
mcp16mHz31k25BpsCfg2 = 0xf1
|
||||
mcp16mHz31k25BpsCfg3 = 0x85
|
||||
|
||||
mcp16mHz25kBpsCfg1 = 0x0f
|
||||
mcp16mHz25kBpsCfg2 = 0xba
|
||||
mcp16mHz25kBpsCfg3 = 0x07
|
||||
|
||||
mcp16mHz20kBpsCfg1 = 0x0f
|
||||
mcp16mHz20kBpsCfg2 = 0xff
|
||||
mcp16mHz20kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz10kBpsCfg1 = 0x1f
|
||||
mcp16mHz10kBpsCfg2 = 0xff
|
||||
mcp16mHz10kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz5kBpsCfg1 = 0x3f
|
||||
mcp16mHz5kBpsCfg2 = 0xff
|
||||
mcp16mHz5kBpsCfg3 = 0x87
|
||||
|
||||
mcp16mHz666kBpsCfg1 = 0x00
|
||||
mcp16mHz666kBpsCfg2 = 0xa0
|
||||
mcp16mHz666kBpsCfg3 = 0x04
|
||||
|
||||
// speed= 8m
|
||||
|
||||
mcp8mHz1000kBpsCfg1 = 0x00
|
||||
mcp8mHz1000kBpsCfg2 = 0x80
|
||||
mcp8mHz1000kBpsCfg3 = 0x00
|
||||
|
||||
mcp8mHz500kBpsCfg1 = 0x00
|
||||
mcp8mHz500kBpsCfg2 = 0x90
|
||||
mcp8mHz500kBpsCfg3 = 0x02
|
||||
|
||||
mcp8mHz250kBpsCfg1 = 0x00
|
||||
mcp8mHz250kBpsCfg2 = 0xb1
|
||||
mcp8mHz250kBpsCfg3 = 0x05
|
||||
|
||||
mcp8mHz200kBpsCfg1 = 0x00
|
||||
mcp8mHz200kBpsCfg2 = 0xb4
|
||||
mcp8mHz200kBpsCfg3 = 0x06
|
||||
|
||||
mcp8mHz125kBpsCfg1 = 0x01
|
||||
mcp8mHz125kBpsCfg2 = 0xb1
|
||||
mcp8mHz125kBpsCfg3 = 0x05
|
||||
|
||||
mcp8mHz100kBpsCfg1 = 0x01
|
||||
mcp8mHz100kBpsCfg2 = 0xb4
|
||||
mcp8mHz100kBpsCfg3 = 0x06
|
||||
|
||||
mcp8mHz80kBpsCfg1 = 0x01
|
||||
mcp8mHz80kBpsCfg2 = 0xbf
|
||||
mcp8mHz80kBpsCfg3 = 0x07
|
||||
|
||||
mcp8mHz50kBpsCfg1 = 0x03
|
||||
mcp8mHz50kBpsCfg2 = 0xb4
|
||||
mcp8mHz50kBpsCfg3 = 0x06
|
||||
|
||||
mcp8mHz40kBpsCfg1 = 0x03
|
||||
mcp8mHz40kBpsCfg2 = 0xbf
|
||||
mcp8mHz40kBpsCfg3 = 0x07
|
||||
|
||||
mcp8mHz31k25BpsCfg1 = 0x07
|
||||
mcp8mHz31k25BpsCfg2 = 0xa4
|
||||
mcp8mHz31k25BpsCfg3 = 0x04
|
||||
|
||||
mcp8mHz20kBpsCfg1 = 0x07
|
||||
mcp8mHz20kBpsCfg2 = 0xbf
|
||||
mcp8mHz20kBpsCfg3 = 0x07
|
||||
|
||||
mcp8mHz10kBpsCfg1 = 0x0f
|
||||
mcp8mHz10kBpsCfg2 = 0xbf
|
||||
mcp8mHz10kBpsCfg3 = 0x07
|
||||
|
||||
mcp8mHz5kBpsCfg1 = 0x1f
|
||||
mcp8mHz5kBpsCfg2 = 0xbf
|
||||
mcp8mHz5kBpsCfg3 = 0x07
|
||||
|
||||
mcp16mHz47kBpsCfg1 = 0x06
|
||||
mcp16mHz47kBpsCfg2 = 0xbe
|
||||
mcp16mHz47kBpsCfg3 = 0x07
|
||||
|
||||
mcpdebug = 0
|
||||
mcpdebugTxbuf = 0
|
||||
mcpNTxbuffers = 3
|
||||
|
||||
mcpRxbuf0 = 0x61
|
||||
mcpRxbuf1 = 0x71
|
||||
|
||||
mcp2515Ok = 0
|
||||
mcp2515Fail = 1
|
||||
mcpAlltxbusy = 2
|
||||
|
||||
candebug = 1
|
||||
|
||||
canuseloop = 0
|
||||
|
||||
cansendtimeout = 200 // milliseconds
|
||||
|
||||
mcpPinHiz = 0
|
||||
mcpPinInt = 1
|
||||
mcpPinOut = 2
|
||||
mcpPinIn = 3
|
||||
|
||||
mcpRx0bf = 0
|
||||
mcpRx1bf = 1
|
||||
mcpTx0rts = 2
|
||||
mcpTx1rts = 3
|
||||
mcpTx2rts = 4
|
||||
|
||||
// initial value of gcanautoprocess
|
||||
|
||||
canautoprocess = 1
|
||||
canautoon = 1
|
||||
canautooff = 0
|
||||
canStdid = 0
|
||||
canExtid = 1
|
||||
candefaultident = 0x55cc
|
||||
candefaultidentext = 1
|
||||
|
||||
CAN5kBps = 1
|
||||
CAN10kBps = 2
|
||||
CAN20kBps = 3
|
||||
CAN25kBps = 4
|
||||
CAN31k25Bps = 5
|
||||
CAN33kBps = 6
|
||||
CAN40kBps = 7
|
||||
CAN50kBps = 8
|
||||
CAN80kBps = 9
|
||||
CAN83k3Bps = 10
|
||||
CAN95kBps = 11
|
||||
CAN100kBps = 12
|
||||
CAN125kBps = 13
|
||||
CAN200kBps = 14
|
||||
CAN250kBps = 15
|
||||
CAN500kBps = 16
|
||||
CAN666kBps = 17
|
||||
CAN1000kBps = 18
|
||||
CAN47kBps = 19
|
||||
|
||||
canOk = 0
|
||||
canFailinit = 1
|
||||
canFailtx = 2
|
||||
canMsgavail = 3
|
||||
canNomsg = 4
|
||||
canCtrlerror = 5
|
||||
canGettxbftimeout = 6
|
||||
canSendmsgtimeout = 7
|
||||
canFail = 0xff
|
||||
|
||||
canMaxCharInMessage = 8
|
||||
)
|
||||
+6
-2
@@ -4,6 +4,7 @@ package tls
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/drivers/net"
|
||||
)
|
||||
@@ -17,14 +18,17 @@ func Dial(network, address string, config *Config) (*net.TCPSerialConn, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
addr := raddr.IP.String()
|
||||
hostname := strings.Split(address, ":")[0]
|
||||
sendport := strconv.Itoa(raddr.Port)
|
||||
if sendport == "0" {
|
||||
sendport = "443"
|
||||
}
|
||||
|
||||
// disconnect any old socket
|
||||
net.ActiveDevice.DisconnectSocket()
|
||||
|
||||
// connect new socket
|
||||
err = net.ActiveDevice.ConnectSSLSocket(addr, sendport)
|
||||
err = net.ActiveDevice.ConnectSSLSocket(hostname, sendport)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
package p1am
|
||||
|
||||
//go:generate go run ./internal/cmd/gen_defines
|
||||
|
||||
type ModuleProps struct {
|
||||
ModuleID uint32
|
||||
DI, DO, AI, AO, Status, Config, DataSize byte
|
||||
Name string
|
||||
}
|
||||
|
||||
var modules = []ModuleProps{
|
||||
//{0x000000ID,di,do,ai,ao,st,cf,ds}
|
||||
{0x00000000, 0, 0, 0, 0, 0, 0, 0, "Empty"}, //Empty first entry for defaultgs
|
||||
|
||||
{0x04A00081, 1, 0, 0, 0, 0, 0, 1, "P1-08ND3"}, //P1-08ND3
|
||||
|
||||
{0x04A00085, 1, 0, 0, 0, 0, 0, 1, "P1-08NA"}, //P1-08NA
|
||||
|
||||
{0x04A00087, 1, 0, 0, 0, 0, 0, 1, "P1-08SIM"}, //P1-08SIM
|
||||
|
||||
{0x04A00088, 1, 0, 0, 0, 0, 0, 1, "P1-08NE3"}, //P1-08NE3
|
||||
|
||||
{0x05200082, 2, 0, 0, 0, 0, 0, 1, "P1-16ND3"}, //P1-16ND3
|
||||
|
||||
{0x05200089, 2, 0, 0, 0, 0, 0, 1, "P1-16NE3"}, //P1-16NE3
|
||||
|
||||
{0x1403F481, 0, 0, 0, 32, 4, 4, 0xA0, "P1-04PWM"}, //P1-04PWM
|
||||
|
||||
{0x1404008D, 0, 1, 0, 0, 0, 0, 1, "P1-08TA"}, //P1-08TA
|
||||
|
||||
{0x1404008F, 0, 1, 0, 0, 0, 0, 1, "P1-08TRS"}, //P1-08TRS
|
||||
|
||||
{0x14040091, 0, 2, 0, 0, 0, 0, 1, "P1-16TR"}, //P1-16TR
|
||||
|
||||
{0x14050081, 0, 1, 0, 0, 0, 0, 1, "P1-08TD1"}, //P1-08TD1
|
||||
|
||||
{0x14050082, 0, 1, 0, 0, 0, 0, 1, "P1-08TD2"}, //P1-08TD2
|
||||
|
||||
{0x14080085, 0, 2, 0, 0, 0, 0, 1, "P1-15TD1"}, //P1-15TD1
|
||||
|
||||
{0x14080086, 0, 2, 0, 0, 0, 0, 1, "P1-15TD2"}, //P1-15TD2
|
||||
|
||||
{0x24A50081, 1, 1, 0, 0, 0, 0, 1, "P1-16CDR"}, //P1-16CDR
|
||||
|
||||
{0x24A50082, 1, 1, 0, 0, 0, 0, 1, "P1-15CDD1"}, //P1-15CDD1
|
||||
|
||||
{0x24A50083, 1, 1, 0, 0, 0, 0, 1, "P1-15CDD2"}, //P1-15CDD2
|
||||
|
||||
{0x34605581, 0, 0, 16, 0, 12, 18, 16, "P1-04AD"}, //P1-04AD
|
||||
|
||||
{0x34605588, 0, 0, 16, 0, 12, 8, 16, "P1-04RTD"}, //P1-04RTD
|
||||
|
||||
{0x3460558F, 0, 0, 16, 0, 12, 2, 12, "P1-04ADL-1"}, //P1-04ADL-1
|
||||
|
||||
{0x34605590, 0, 0, 16, 0, 12, 2, 12, "P1-04ADL-2"}, //P1-04ADL-2
|
||||
|
||||
{0x34608C81, 0, 0, 16, 0, 12, 20, 32, "P1-04THM"}, //P1-04THM
|
||||
|
||||
{0x34608C8E, 0, 0, 16, 0, 12, 8, 32, "P1-04NTC"}, //P1-04NTC
|
||||
|
||||
{0x34A0558A, 0, 0, 32, 0, 12, 2, 12, "P1-08ADL-1"}, //P1-08ADL-1
|
||||
|
||||
{0x34A0558B, 0, 0, 32, 0, 12, 2, 12, "P1-08ADL-2"}, //P1-08ADL-2
|
||||
|
||||
{0x34A5A481, 2, 0, 36, 36, 4, 12, 0xC0, "P1-02HSC"}, //P1-02HSC
|
||||
|
||||
{0x44035583, 0, 0, 0, 16, 4, 0, 12, "P1-04DAL-1"}, //P1-04DAL-1
|
||||
|
||||
{0x44035584, 0, 0, 0, 16, 4, 0, 12, "P1-04DAL-2"}, //P1-04DAL-2
|
||||
|
||||
{0x44055588, 0, 0, 0, 32, 4, 0, 12, "P1-08DAL-1"}, //P1-08DAL-1
|
||||
|
||||
{0x44055589, 0, 0, 0, 32, 4, 0, 12, "P1-08DAL-2"}, //P1-08DAL-2
|
||||
|
||||
{0x5461A783, 0, 0, 16, 8, 12, 2, 12, "P1-4ADL2DAL-1"}, //P1-4ADL2DAL-1
|
||||
|
||||
{0x5461A784, 0, 0, 16, 8, 12, 2, 12, "P1-4ADL2DAL-2"}, //P1-4ADL2DAL-2
|
||||
|
||||
{0xFFFFFFFF, 0, 0, 0, 0, 0, 0, 0, "BAD SLOT"}, //empty in case no modules are defined.
|
||||
|
||||
{0x00000000, 0, 0, 0, 0, 0, 0, 0, "BAD SLOT"}, //empty in case no modules are defined.
|
||||
}
|
||||
|
||||
var defaultConfig = map[uint32][]byte{
|
||||
0x34605590:// P1_04ADL_2_DEFAULT_CONFIG
|
||||
{0x40, 0x03},
|
||||
0x34608C8E: // P1_04NTC_DEFAULT_CONFIG
|
||||
{0x40, 0x03, 0x60, 0x05,
|
||||
0x20, 0x00, 0x80, 0x02},
|
||||
0x34608C81: // P1_04THM_DEFAULT_CONFIG
|
||||
{0x40, 0x03, 0x60, 0x05,
|
||||
0x21, 0x00, 0x22, 0x00,
|
||||
0x23, 0x00, 0x24, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00},
|
||||
0x34605588: // P1_04RTD_DEFAULT_CONFIG
|
||||
{0x40, 0x03, 0x60, 0x05,
|
||||
0x20, 0x01, 0x80, 0x00},
|
||||
0x34605581: // P1_04AD_DEFAULT_CONFIG
|
||||
{0x40, 0x03, 0x00, 0x00,
|
||||
0x20, 0x03, 0x00, 0x00,
|
||||
0x21, 0x03, 0x00, 0x00,
|
||||
0x22, 0x03, 0x00, 0x00,
|
||||
0x23, 0x03},
|
||||
0x3460558F:// P1_04ADL_1_DEFAULT_CONFIG
|
||||
{0x40, 0x03},
|
||||
0x34A0558A:// P1_08ADL_1_DEFAULT_CONFIG
|
||||
{0x40, 0x07},
|
||||
0x34A0558B:// P1_08ADL_2_DEFAULT_CONFIG
|
||||
{0x40, 0x07},
|
||||
0x5461A783:// P1_04ADL2DAL_1_DEFAULT_CONFIG
|
||||
{0x40, 0x03},
|
||||
0x5461A784:// P1_04ADL2DAL_2_DEFAULT_CONFIG
|
||||
{0x40, 0x03},
|
||||
0x1403F481:// P1_04PWM_DEFAULT_CONFIG
|
||||
{0x02, 0x02, 0x02, 0x02},
|
||||
0x34A5A481: // P1_02HSC_DEFAULT_CONFIG
|
||||
{0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x01},
|
||||
}
|
||||
|
||||
const NUMBER_OF_MODULES = 15 //Current max 15 Modules
|
||||
const SWITCH_BUILTIN = 31
|
||||
const baseEnable = 33
|
||||
const MOD_HDR = 0x02
|
||||
const VERSION_HDR = 0x03
|
||||
const ACTIVE_HDR = 0x04
|
||||
const DROPOUT_HDR = 0x05
|
||||
const CFG_HDR = 0x10
|
||||
const READ_CFG_HDR = 0x11
|
||||
const PETWD_HDR = 0x30
|
||||
const STARTWD_HDR = 0x31
|
||||
const STOPWD_HDR = 0x32
|
||||
const CONFIGWD_HDR = 0x33
|
||||
const READ_STATUS_HDR = 0x40
|
||||
const READ_DISCRETE_HDR = 0x50
|
||||
const READ_ANALOG_HDR = 0x51
|
||||
const READ_BLOCK_HDR = 0x52
|
||||
const WRITE_DISCRETE_HDR = 0x60
|
||||
const WRITE_ANALOG_HDR = 0x61
|
||||
const WRITE_BLOCK_HDR = 0x62
|
||||
const FW_UPDATE_HDR = 0xAA
|
||||
const DUMMY = 0xFF
|
||||
const EMPTY_SLOT_ID = 0xFFFFFFFE
|
||||
const MAX_TIMEOUT = 0xFFFFFFFF
|
||||
const DISCRETE_IN_BLOCK = 0
|
||||
const ANALOG_IN_BLOCK = 1
|
||||
const DISCRETE_OUT_BLOCK = 2
|
||||
const ANALOG_OUT_BLOCK = 3
|
||||
const STATUS_IN_BLOCK = 4
|
||||
const MISSING24V_STATUS = 3
|
||||
const BURNOUT_STATUS = 5
|
||||
const UNDER_RANGE_STATUS = 7
|
||||
const OVER_RANGE_STATUS = 11
|
||||
const TOGGLE = 0x01
|
||||
const HOLD = 0x00
|
||||
@@ -0,0 +1,133 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var tmpl = template.Must(template.New("main").Parse(`package p1am
|
||||
|
||||
//go:generate go run ./internal/cmd/gen_defines
|
||||
|
||||
type ModuleProps struct {
|
||||
ModuleID uint32
|
||||
DI, DO, AI, AO, Status, Config, DataSize byte
|
||||
Name string
|
||||
}
|
||||
|
||||
var modules = []ModuleProps{
|
||||
{{.MDB -}}
|
||||
}
|
||||
|
||||
var defaultConfig = map[uint32][]byte{
|
||||
{{range .Configs -}}
|
||||
0x{{.ID}}: // {{.Name}}
|
||||
{{index $.DefaultConfigs .Name}},
|
||||
{{end}}
|
||||
}
|
||||
|
||||
{{range .Defines}}
|
||||
const {{.Name}} = {{.Value}}{{.Comment -}}
|
||||
{{end}}
|
||||
`))
|
||||
|
||||
func findLibrary() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, dir := range []string{
|
||||
"Documents/Arduino",
|
||||
"Arduino",
|
||||
} {
|
||||
dir = filepath.Join(home, dir, "libraries/P1AM/src")
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
return dir
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func definitions(path string, delim string) []string {
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return strings.Split(string(data), delim)
|
||||
}
|
||||
|
||||
var (
|
||||
mdbRE = regexp.MustCompile(`(?s)mdb\[\] = \{\s*(.+)\}`)
|
||||
configRE = regexp.MustCompile(`(?s)const char (.*?)\[\] = (.+)`)
|
||||
caseRE = regexp.MustCompile(`(?s)case 0x([^:]+):\s+return \(char\*\)(.+)`)
|
||||
defineRE = regexp.MustCompile(`(?ms)^\s*#define (\S+)\s+(\d+|0x[0-9a-fA-F]+)(\s+.*?)?\s*$`)
|
||||
)
|
||||
|
||||
func main() {
|
||||
base := findLibrary()
|
||||
if base == "" {
|
||||
log.Fatal("can't find Arduino library")
|
||||
}
|
||||
var data = struct {
|
||||
MDB string
|
||||
DefaultConfigs map[string]string
|
||||
Configs []struct {
|
||||
ID string
|
||||
Name string
|
||||
}
|
||||
Defines []struct {
|
||||
Name string
|
||||
Value string
|
||||
Comment string
|
||||
}
|
||||
}{
|
||||
DefaultConfigs: make(map[string]string),
|
||||
}
|
||||
for _, line := range definitions(filepath.Join(base, "Module_List.h"), ";") {
|
||||
if matches := mdbRE.FindStringSubmatch(line); matches != nil {
|
||||
data.MDB = regexp.MustCompile(`}\s*//`).ReplaceAllString(matches[1], `}, //`)
|
||||
}
|
||||
if matches := configRE.FindStringSubmatch(line); matches != nil {
|
||||
data.DefaultConfigs[matches[1]] = matches[2]
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range definitions(filepath.Join(base, "P1AM.cpp"), ";") {
|
||||
if matches := caseRE.FindStringSubmatch(line); matches != nil {
|
||||
data.Configs = append(data.Configs, struct{ ID, Name string }{
|
||||
ID: matches[1],
|
||||
Name: matches[2],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range definitions(filepath.Join(base, "defines.h"), "\n") {
|
||||
if matches := defineRE.FindStringSubmatch(line); matches != nil {
|
||||
data.Defines = append(data.Defines, struct{ Name, Value, Comment string }{
|
||||
Name: matches[1],
|
||||
Value: matches[2],
|
||||
Comment: matches[3],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := tmpl.Execute(&buf, &data); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
formatted, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Printf("failed to compile %s", buf.Bytes())
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile("defines.go", formatted, 0666); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
+429
@@ -0,0 +1,429 @@
|
||||
// Driver for the P1AM-100 base controller.
|
||||
//
|
||||
// This is an embedded device on the P1AM-100 board.
|
||||
// Based on v1.0.1 of the Arduino library: https://github.com/facts-engineering/P1AM/tree/1.0.1
|
||||
|
||||
package p1am
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
type P1AM struct {
|
||||
bus machine.SPI
|
||||
slaveSelectPin, slaveAckPin, baseEnablePin machine.Pin
|
||||
|
||||
// SkipAutoConfig will skip loading a default configuration into each module.
|
||||
SkipAutoConfig bool
|
||||
|
||||
Slots int
|
||||
// Access slots via Slot()
|
||||
slots []Slot
|
||||
}
|
||||
|
||||
var Controller = P1AM{
|
||||
bus: machine.SPI0,
|
||||
slaveSelectPin: machine.BASE_SLAVE_SELECT_PIN,
|
||||
slaveAckPin: machine.BASE_SLAVE_ACK_PIN,
|
||||
baseEnablePin: machine.BASE_ENABLE_PIN,
|
||||
}
|
||||
|
||||
type baseSlotConstants struct {
|
||||
DI, DO, AI, AO, Status, Config, DataSize byte
|
||||
}
|
||||
|
||||
func (p *P1AM) Initialize() error {
|
||||
p.slaveSelectPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
p.slaveAckPin.Configure(machine.PinConfig{Mode: machine.PinInput})
|
||||
p.baseEnablePin.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
if err := p.bus.Configure(machine.SPIConfig{
|
||||
Frequency: 1000000,
|
||||
Mode: 2,
|
||||
LSBFirst: false,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.SetEnabled(true)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
if err := p.waitAck(5 * time.Second); err != nil {
|
||||
return errors.New("no base controller activity; check external supply connection")
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if err := p.handleHDR(MOD_HDR); err == nil {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
slots, err := p.spiSendRecvByte(0xFF)
|
||||
if err == nil && slots > 0 && slots <= 15 {
|
||||
p.Slots = int(slots)
|
||||
break
|
||||
}
|
||||
}
|
||||
if i > 2 {
|
||||
// Try restarting the base controller
|
||||
p.SetEnabled(false)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
p.SetEnabled(true)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
if p.Slots <= 0 || p.Slots > 15 {
|
||||
return errors.New("zero modules in the base")
|
||||
}
|
||||
|
||||
moduleIDs := make([]uint32, p.Slots)
|
||||
|
||||
p.waitAck(200 * time.Millisecond)
|
||||
if err := binary.Read(p, binary.LittleEndian, &moduleIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
baseConstants := make([]baseSlotConstants, p.Slots)
|
||||
p.slots = make([]Slot, p.Slots)
|
||||
|
||||
for i := 1; i <= p.Slots; i++ {
|
||||
slot := p.Slot(i)
|
||||
slot.p = p
|
||||
slot.slot = byte(i)
|
||||
slot.ID = moduleIDs[i-1]
|
||||
// What if 0xFFFFFFFF isn't at position -2?
|
||||
slot.Props = &modules[len(modules)-2]
|
||||
for j := 0; j < len(modules); j++ {
|
||||
if modules[j].ModuleID == slot.ID {
|
||||
slot.Props = &modules[j]
|
||||
}
|
||||
bc := &baseConstants[i-1]
|
||||
bc.DI = slot.Props.DI
|
||||
bc.DO = slot.Props.DO
|
||||
bc.AI = slot.Props.AI
|
||||
bc.AO = slot.Props.AO
|
||||
bc.Status = slot.Props.Status
|
||||
bc.Config = slot.Props.Config
|
||||
bc.DataSize = slot.Props.DataSize
|
||||
}
|
||||
}
|
||||
|
||||
p.waitAck(200 * time.Millisecond)
|
||||
if err := binary.Write(p, binary.LittleEndian, &baseConstants); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !p.SkipAutoConfig {
|
||||
for i := 1; i <= p.Slots; i++ {
|
||||
s := p.Slot(i)
|
||||
if s.Props.Config > 0 {
|
||||
cfg := defaultConfig[s.ID]
|
||||
if cfg != nil {
|
||||
s.Configure(cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P1AM) Version() ([3]byte, error) {
|
||||
if err := p.handleHDR(VERSION_HDR); err != nil {
|
||||
return [3]byte{}, err
|
||||
}
|
||||
var buf [4]byte
|
||||
if err := p.spiSendRecvBuf(nil, buf[:]); err != nil {
|
||||
return [3]byte{}, err
|
||||
}
|
||||
return [3]byte{
|
||||
byte(buf[1] >> 4),
|
||||
byte(buf[1] & 0xF),
|
||||
byte(buf[0]),
|
||||
}, p.dataSync()
|
||||
}
|
||||
|
||||
func (p *P1AM) Active() (bool, error) {
|
||||
if _, err := p.spiSendRecvByte(ACTIVE_HDR); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := p.waitAck(200 * time.Millisecond); err != nil {
|
||||
return false, err
|
||||
}
|
||||
buf, err := p.spiSendRecvByte(DUMMY)
|
||||
defer p.dataSync()
|
||||
return buf != 0, err
|
||||
}
|
||||
|
||||
const wdToggleTime = 100 * time.Millisecond
|
||||
|
||||
func (p *P1AM) ConfigureWatchdog(interval time.Duration, reset bool) error {
|
||||
ms := interval / time.Millisecond
|
||||
toggleMs := wdToggleTime / time.Millisecond
|
||||
resetB := byte(0)
|
||||
if reset {
|
||||
resetB = 1
|
||||
}
|
||||
buf := [6]byte{
|
||||
CONFIGWD_HDR,
|
||||
byte(ms),
|
||||
byte(ms >> 8),
|
||||
byte(toggleMs),
|
||||
byte(toggleMs >> 8),
|
||||
resetB,
|
||||
}
|
||||
if err := p.spiSendRecvBuf(buf[:], nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return p.dataSync()
|
||||
}
|
||||
|
||||
func (p *P1AM) sendWatchdog(hdr byte) error {
|
||||
if _, err := p.spiSendRecvByte(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := p.waitAck(200 * time.Millisecond); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := p.spiSendRecvByte(DUMMY); err != nil {
|
||||
return err
|
||||
}
|
||||
return p.dataSync()
|
||||
}
|
||||
|
||||
func (p *P1AM) StartWatchdog() error {
|
||||
return p.sendWatchdog(STARTWD_HDR)
|
||||
}
|
||||
|
||||
func (p *P1AM) StopWatchdog() error {
|
||||
return p.sendWatchdog(STOPWD_HDR)
|
||||
}
|
||||
|
||||
func (p *P1AM) PetWatchdog() error {
|
||||
return p.sendWatchdog(PETWD_HDR)
|
||||
}
|
||||
|
||||
func (p *P1AM) Slot(i int) *Slot {
|
||||
if i < 1 || i > p.Slots {
|
||||
return nil
|
||||
}
|
||||
return &p.slots[i-1]
|
||||
}
|
||||
|
||||
type Slot struct {
|
||||
p *P1AM
|
||||
slot byte
|
||||
ID uint32
|
||||
// TODO: Embed this?
|
||||
Props *ModuleProps
|
||||
}
|
||||
|
||||
func (s *Slot) Configure(data []byte) error {
|
||||
if s == nil {
|
||||
return errors.New("invalid slot")
|
||||
}
|
||||
if len(data) != int(s.Props.Config) {
|
||||
return fmt.Errorf("expected %d config bytes, got %d", s.Props.Config, len(data))
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return errors.New("no config bytes")
|
||||
}
|
||||
|
||||
out := make([]byte, len(data)+2)
|
||||
out[0] = CFG_HDR
|
||||
out[1] = s.slot
|
||||
copy(out[2:], data)
|
||||
|
||||
if err := s.p.spiSendRecvBuf(out, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
s.p.dataSync()
|
||||
s.p.dataSync()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Slot) ReadDiscrete() (uint32, error) {
|
||||
if s == nil {
|
||||
return 0, errors.New("invalid slot")
|
||||
}
|
||||
bytes := s.Props.DI
|
||||
out := [2]byte{
|
||||
READ_DISCRETE_HDR,
|
||||
s.slot,
|
||||
}
|
||||
if err := s.p.spiSendRecvBuf(out[:], nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if err := s.p.waitAck(200 * time.Millisecond); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var data [4]byte
|
||||
if err := s.p.spiSendRecvBuf(nil, data[:bytes]); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err := s.p.dataSync()
|
||||
return binary.LittleEndian.Uint32(data[:]), err
|
||||
}
|
||||
|
||||
func (s *Slot) WriteDiscrete(value uint32) error {
|
||||
return s.writeDiscrete(0, value)
|
||||
}
|
||||
|
||||
func (s *Slot) writeDiscrete(channel byte, value uint32) error {
|
||||
if s == nil {
|
||||
return errors.New("invalid slot")
|
||||
}
|
||||
bytes := s.Props.DO
|
||||
buf := [7]byte{
|
||||
WRITE_DISCRETE_HDR,
|
||||
s.slot,
|
||||
channel,
|
||||
}
|
||||
binary.LittleEndian.PutUint32(buf[3:], value)
|
||||
out := buf[:3+bytes]
|
||||
if channel != 0 {
|
||||
out = buf[:4]
|
||||
out[3] &= 1
|
||||
}
|
||||
if err := s.p.spiSendRecvBuf(out, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
return s.p.dataSync()
|
||||
}
|
||||
|
||||
type Channel struct {
|
||||
s *Slot
|
||||
channel int
|
||||
}
|
||||
|
||||
func (s *Slot) Channel(channel int) Channel {
|
||||
return Channel{
|
||||
s: s,
|
||||
channel: channel,
|
||||
}
|
||||
}
|
||||
|
||||
func (c Channel) ReadDiscrete() (bool, error) {
|
||||
if c.channel < 1 || c.channel > int(c.s.Props.DI)*8 {
|
||||
return false, errors.New("invalid channel")
|
||||
}
|
||||
data, err := c.s.ReadDiscrete()
|
||||
return (data>>(c.channel-1))&1 == 1, err
|
||||
}
|
||||
|
||||
func (c Channel) WriteDiscrete(value bool) error {
|
||||
if c.channel < 1 || c.channel > int(c.s.Props.DO)*8 {
|
||||
return errors.New("invalid channel")
|
||||
}
|
||||
data := uint32(0)
|
||||
if value {
|
||||
data = 1
|
||||
}
|
||||
return c.s.writeDiscrete(byte(c.channel), data)
|
||||
}
|
||||
|
||||
const ackTimeout = 200 * time.Millisecond
|
||||
|
||||
func awaitPin(pin machine.Pin, state bool, timeout time.Duration) bool {
|
||||
start := time.Now()
|
||||
for pin.Get() != state {
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
if time.Since(start) > timeout {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
// TODO: Use channels when https://github.com/tinygo-org/tinygo/pull/1402 is merged.
|
||||
// edge := machine.PinRising
|
||||
// if state {
|
||||
// edge = machine.PinFalling
|
||||
// }
|
||||
// ch := make(chan struct{}, 1)
|
||||
// defer close(ch)
|
||||
// pin.SetInterrupt(edge, func(machine.Pin) {
|
||||
// ch <- struct{}{}
|
||||
// })
|
||||
// defer pin.SetInterrupt(0, nil)
|
||||
// select {
|
||||
// case <-ch:
|
||||
// return true
|
||||
// case <-time.After(timeout):
|
||||
// return false
|
||||
// }
|
||||
}
|
||||
|
||||
var dataSyncErr = errors.New("base sync timeout")
|
||||
|
||||
func (p *P1AM) dataSync() error {
|
||||
if !awaitPin(p.slaveAckPin, true, ackTimeout) {
|
||||
return dataSyncErr
|
||||
}
|
||||
time.Sleep(time.Microsecond)
|
||||
if !awaitPin(p.slaveAckPin, false, ackTimeout) {
|
||||
return dataSyncErr
|
||||
}
|
||||
time.Sleep(time.Microsecond)
|
||||
if !awaitPin(p.slaveAckPin, true, ackTimeout) {
|
||||
return dataSyncErr
|
||||
}
|
||||
time.Sleep(time.Microsecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *P1AM) handleHDR(HDR byte) error {
|
||||
for !p.slaveAckPin.Get() {
|
||||
}
|
||||
if _, err := p.spiSendRecvByte(HDR); err != nil {
|
||||
return err
|
||||
}
|
||||
return p.spiTimeout(MAX_TIMEOUT*time.Millisecond, HDR, 2*time.Second)
|
||||
}
|
||||
|
||||
func (p *P1AM) Read(data []byte) (int, error) {
|
||||
return len(data), p.spiSendRecvBuf(nil, data)
|
||||
}
|
||||
|
||||
func (p *P1AM) Write(data []byte) (int, error) {
|
||||
return len(data), p.spiSendRecvBuf(data, nil)
|
||||
}
|
||||
|
||||
func (p *P1AM) spiSendRecvBuf(w, r []byte) error {
|
||||
p.slaveSelectPin.Low()
|
||||
defer p.slaveSelectPin.High()
|
||||
return p.bus.Tx(w, r)
|
||||
}
|
||||
|
||||
func (p *P1AM) spiSendRecvByte(data byte) (byte, error) {
|
||||
p.slaveSelectPin.Low()
|
||||
defer p.slaveSelectPin.High()
|
||||
return p.bus.Transfer(data)
|
||||
}
|
||||
|
||||
func (p *P1AM) waitAck(timeout time.Duration) error {
|
||||
return p.spiTimeout(timeout, 0, 0)
|
||||
}
|
||||
|
||||
var timeoutErr = errors.New("timeout")
|
||||
|
||||
func (p *P1AM) spiTimeout(timeout time.Duration, resendMsg byte, retryPeriod time.Duration) error {
|
||||
end := time.Now().Add(timeout)
|
||||
retry := time.Now().Add(retryPeriod)
|
||||
for time.Now().Before(end) {
|
||||
if p.slaveAckPin.Get() {
|
||||
time.Sleep(50 * time.Microsecond)
|
||||
return nil
|
||||
}
|
||||
if retryPeriod > 0 && time.Now().After(retry) {
|
||||
p.spiSendRecvByte(resendMsg)
|
||||
retry = retry.Add(retryPeriod)
|
||||
}
|
||||
}
|
||||
return timeoutErr
|
||||
}
|
||||
|
||||
func (p *P1AM) SetEnabled(enabled bool) {
|
||||
p.baseEnablePin.Set(enabled)
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// Package pcf8563 implements a driver for the PCF8563 CMOS Real-Time Clock (RTC)
|
||||
//
|
||||
// Datasheet: https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf
|
||||
//
|
||||
|
||||
package pcf8563
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps an I2C connection to a PCF8563 device.
|
||||
type Device struct {
|
||||
bus drivers.I2C
|
||||
Address uint16
|
||||
}
|
||||
|
||||
// New creates a new PCF8563 connection. I2C bus must be already configured.
|
||||
func New(i2c drivers.I2C) Device {
|
||||
return Device{
|
||||
bus: i2c,
|
||||
Address: PCF8563_ADDR,
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets the `control and status registers`. When this method is
|
||||
// called, it writes `0x00` to the `control and status registers`. This will
|
||||
// cause `Alarm` and `Timer` to become Inactive. Please refer to the datasheet
|
||||
// for details.
|
||||
func (d *Device) Reset() (err error) {
|
||||
return d.bus.Tx(d.Address, []byte{0x00, 0x00, 0x00}, nil)
|
||||
}
|
||||
|
||||
// SetTime sets the time and date
|
||||
func (d *Device) SetTime(t time.Time) error {
|
||||
var buf [9]byte
|
||||
buf[0] = 0x02
|
||||
buf[1] = decToBcd(t.Second())
|
||||
buf[2] = decToBcd(t.Minute())
|
||||
buf[3] = decToBcd(t.Hour())
|
||||
buf[4] = decToBcd(t.Day())
|
||||
buf[5] = decToBcd(int(t.Weekday() + 1))
|
||||
buf[6] = decToBcd(int(t.Month()))
|
||||
buf[7] = decToBcd(t.Year() - 2000)
|
||||
err := d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadTime returns the date and time
|
||||
func (d *Device) ReadTime() (time.Time, error) {
|
||||
var buf [9]byte
|
||||
err := d.bus.Tx(d.Address, []byte{0x00}, buf[:])
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
seconds := bcdToDec(buf[2] & 0x7F)
|
||||
minute := bcdToDec(buf[3] % 0x7F)
|
||||
hour := bcdToDec(buf[4] & 0x3F)
|
||||
day := bcdToDec(buf[5] & 0x3F)
|
||||
month := time.Month(bcdToDec(buf[7] & 0x0F))
|
||||
year := int(bcdToDec(buf[8])) + 2000
|
||||
|
||||
t := time.Date(year, month, day, hour, minute, seconds, 0, time.UTC)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// SetAlarm sets the alarm
|
||||
func (d *Device) SetAlarm(t time.Time) error {
|
||||
var buf [5]byte
|
||||
buf[0] = 0x09
|
||||
buf[1] = RTC_ALARM_ENABLE | decToBcd(t.Minute())
|
||||
buf[2] = RTC_ALARM_ENABLE | decToBcd(t.Hour())
|
||||
buf[3] = RTC_ALARM_ENABLE | decToBcd(t.Day())
|
||||
buf[4] = RTC_ALARM_DISABLE
|
||||
err := d.bus.Tx(d.Address, buf[:], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// enable alarm
|
||||
buf[0] = 0x01
|
||||
err = d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] |= RTC_CTRL_AF
|
||||
err = d.bus.Tx(d.Address, buf[:2], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// ClearAlarm disables alarm.
|
||||
func (d *Device) ClearAlarm() error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] &= ^uint8(RTC_CTRL_AF)
|
||||
err = d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// EnableAlarmInterrupt enables alarm interrupt. When triggered, INT pin (3)
|
||||
// goes low.
|
||||
func (d *Device) EnableAlarmInterrupt() error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] |= RTC_CTRL_AIE
|
||||
err = d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableAlarmInterrupt disable alarm interrupt.
|
||||
func (d *Device) DisableAlarmInterrupt() error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] &= ^uint8(RTC_CTRL_AIE)
|
||||
err = d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// AlarmTriggered returns whether or not an Alarm has been triggered.
|
||||
func (d *Device) AlarmTriggered() bool {
|
||||
var buf [1]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:], buf[:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return (buf[0] & RTC_CTRL_AF) != 0
|
||||
}
|
||||
|
||||
// SetTimer sets timer. The available durations are 1 to 127 seconds. If any
|
||||
// other value is specified, it will be truncated.
|
||||
func (d *Device) SetTimer(dur time.Duration) error {
|
||||
var buf [3]byte
|
||||
|
||||
sec := dur / time.Second
|
||||
if sec > 127 {
|
||||
sec = 127
|
||||
}
|
||||
|
||||
// Treat as sec timer.
|
||||
buf[0] = 0x0E
|
||||
buf[1] = RTC_TIMER_1S
|
||||
buf[2] = byte(sec)
|
||||
err := d.bus.Tx(d.Address, buf[:], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// enable alarm
|
||||
buf[0] = 0x01
|
||||
err = d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] |= RTC_CTRL_TF
|
||||
err = d.bus.Tx(d.Address, buf[:2], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// ClearTimer disables timer.
|
||||
func (d *Device) ClearTimer() error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] &= ^uint8(RTC_CTRL_TF)
|
||||
err = d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// EnableTimerInterrupt enables timer interrupt. When triggered, INT pin (3)
|
||||
// goes low.
|
||||
func (d *Device) EnableTimerInterrupt() error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] |= RTC_CTRL_TIE
|
||||
err = d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// DisableTimerInterrupt disable timer interrupt.
|
||||
func (d *Device) DisableTimerInterrupt() error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:1], buf[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf[1] &= ^uint8(RTC_CTRL_TIE)
|
||||
err = d.bus.Tx(d.Address, buf[:], nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// TimerTriggered returns whether or not an Alarm has been triggered.
|
||||
func (d *Device) TimerTriggered() bool {
|
||||
var buf [1]byte
|
||||
buf[0] = 0x01
|
||||
err := d.bus.Tx(d.Address, buf[:], buf[:])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return (buf[0] & RTC_CTRL_TF) != 0
|
||||
}
|
||||
|
||||
// SetOscillatorFrequency sets output oscillator frequency
|
||||
// Available modes: RTC_COT_DISABLE, RTC_COT_32KHZ, RTC_COT_1KHZ,
|
||||
// RTC_COT_32Hz, RTC_COT_1HZ.
|
||||
func (d *Device) SetOscillatorFrequency(sqw uint8) error {
|
||||
var buf [2]byte
|
||||
buf[0] = 0x0D
|
||||
buf[1] = sqw
|
||||
return d.bus.Tx(d.Address, buf[:], nil)
|
||||
}
|
||||
|
||||
// decToBcd converts int to BCD
|
||||
func decToBcd(dec int) uint8 {
|
||||
return uint8(dec + 6*(dec/10))
|
||||
}
|
||||
|
||||
// bcdToDec converts BCD to int
|
||||
func bcdToDec(bcd uint8) int {
|
||||
return int(bcd - 6*(bcd>>4))
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package pcf8563
|
||||
|
||||
// Registers
|
||||
const (
|
||||
PCF8563_ADDR = 0x51 // R:A3 W:A2
|
||||
|
||||
I2C_SPEED_STANDARD = 100000
|
||||
I2C_SPEED_DOUBLE = 200000
|
||||
|
||||
RTC_CTRL_STOP = 0x20
|
||||
RTC_CTRL_TITP = 0x10
|
||||
RTC_CTRL_AF = 0x08
|
||||
RTC_CTRL_TF = 0x04
|
||||
RTC_CTRL_AIE = 0x02
|
||||
RTC_CTRL_TIE = 0x01
|
||||
|
||||
RTC_COT_DISABLE = 0x00
|
||||
RTC_COT_32KHZ = 0x80
|
||||
RTC_COT_1KHZ = 0x81
|
||||
RTC_COT_32HZ = 0x82
|
||||
RTC_COT_1HZ = 0x83
|
||||
|
||||
RTC_TIMER_DISABLE = 0x00
|
||||
RTC_TIMER_4KHZ = 0x80
|
||||
RTC_TIMER_64HZ = 0x81
|
||||
RTC_TIMER_1S = 0x82
|
||||
RTC_TIMER_60S = 0x83
|
||||
|
||||
RTC_ALARM_DISABLE = 0x80
|
||||
RTC_ALARM_ENABLE = 0x00
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
# SPI sdcard/mmc driver
|
||||
|
||||
This package provides the driver for sdcard/mmc with SPI connection.
|
||||
`examples/sdcard/console` shows a low-level access example.
|
||||
`examples/sdcard/tinyfs` shows an example of using fatfs to read FAT32.
|
||||
|
||||
If you use this package, you need to set `default-stack-size` in `targets/*.json`.
|
||||
For example, `targets/wioterminal.json` has the following configuration.
|
||||
|
||||
```
|
||||
{
|
||||
"inherits": ["atsamd51p19a"],
|
||||
"build-tags": ["wioterminal"],
|
||||
"flash-1200-bps-reset": "true",
|
||||
"flash-method": "msd",
|
||||
"msd-volume-name": "Arduino",
|
||||
"msd-firmware-name": "firmware.uf2",
|
||||
"default-stack-size": 2048
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,49 @@
|
||||
package sdcard
|
||||
|
||||
import "fmt"
|
||||
|
||||
type CID struct {
|
||||
//// byte 0
|
||||
ManufacturerID byte
|
||||
//uint8_t mid; // Manufacturer ID
|
||||
//// byte 1-2
|
||||
OEMApplicationID uint16
|
||||
//char oid[2]; // OEM/Application ID
|
||||
//// byte 3-7
|
||||
ProductName string
|
||||
//char pnm[5]; // Product name
|
||||
//// byte 8
|
||||
ProductVersion string
|
||||
//unsigned prv_m : 4; // Product revision n.m
|
||||
//unsigned prv_n : 4;
|
||||
//// byte 9-12
|
||||
ProductSerialNumber uint32
|
||||
//uint32_t psn; // Product serial number
|
||||
//// byte 13
|
||||
ManufacturingYear byte
|
||||
ManufacturingMonth byte
|
||||
//unsigned mdt_year_high : 4; // Manufacturing date
|
||||
//unsigned reserved : 4;
|
||||
//// byte 14
|
||||
//unsigned mdt_month : 4;
|
||||
//unsigned mdt_year_low : 4;
|
||||
//// byte 15
|
||||
Always1 byte
|
||||
//unsigned always1 : 1;
|
||||
CRC byte
|
||||
//unsigned crc : 7;
|
||||
}
|
||||
|
||||
func NewCID(buf []byte) *CID {
|
||||
return &CID{
|
||||
ManufacturerID: buf[0],
|
||||
OEMApplicationID: (uint16(buf[0]) << 8) | uint16(buf[1]),
|
||||
ProductName: string(buf[3:8]),
|
||||
ProductVersion: fmt.Sprintf("%d.%d", (buf[8]&0xF0)>>4, buf[8]&0x0F),
|
||||
ProductSerialNumber: (uint32(buf[9]) << 24) | (uint32(buf[10]) << 16) | (uint32(buf[11]) << 8) | uint32(buf[12]),
|
||||
ManufacturingYear: (buf[13] & 0xF0) | (buf[14] & 0x0F),
|
||||
ManufacturingMonth: (buf[14] & 0xF0) >> 4,
|
||||
Always1: (buf[15] & 0x80) >> 7,
|
||||
CRC: buf[15] & 0x7F,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package sdcard
|
||||
|
||||
const (
|
||||
CMD0_GO_IDLE_STATE = 0
|
||||
CMD1_SEND_OP_CND = 1
|
||||
CMD2_ALL_SEND_CID = 2
|
||||
CMD3_SEND_RELATIVE_ADDR = 3
|
||||
CMD4_SET_DSR = 4
|
||||
CMD6_SWITCH_FUNC = 6
|
||||
CMD7_SELECT_DESELECT_CARD = 7
|
||||
CMD8_SEND_IF_COND = 8
|
||||
CMD9_SEND_CSD = 9
|
||||
CMD10_SEND_CID = 10
|
||||
CMD12_STOP_TRANSMISSION = 12
|
||||
CMD13_SEND_STATUS = 13
|
||||
CMD15_GO_INACTIVE_STATE = 15
|
||||
CMD16_SET_BLOCKLEN = 16
|
||||
CMD17_READ_SINGLE_BLOCK = 17
|
||||
CMD18_READ_MULTIPLE_BLOCK = 18
|
||||
CMD24_WRITE_BLOCK = 24
|
||||
CMD25_WRITE_MULTIPLE_BLOCK = 25
|
||||
CMD27_PROGRAM_CSD = 27
|
||||
CMD28_SET_WRITE_PROT = 28
|
||||
CMD29_CLR_WRITE_PROT = 29
|
||||
CMD30_SEND_WRITE_PROT = 30
|
||||
CMD32_ERASE_WR_BLK_START_ADDR = 32
|
||||
CMD33_ERASE_WR_BLK_END_ADDR = 33
|
||||
CMD38_ERASE = 38
|
||||
CMD42_LOCK_UNLOCK = 42
|
||||
CMD55_APP_CMD = 55
|
||||
CMD56_GEN_CMD = 56
|
||||
CMD58_READ_OCR = 58
|
||||
CMD59_CRC_ON_OFF = 59
|
||||
ACMD6_SET_BUS_WIDTH = 6
|
||||
ACMD13_SD_STATUS = 13
|
||||
ACMD22_SEND_NUM_WR_BLOCKS = 22
|
||||
ACMD23_SET_WR_BLK_ERASE_COUNT = 23
|
||||
ACMD41_SD_APP_OP_COND = 41
|
||||
ACMD42_SET_CLR_CARD_DETECT = 42
|
||||
ACMD51_SEND_SCR = 51
|
||||
ACMD18_SECURE_READ_MULTI_BLOCK = 18
|
||||
ACMD25_SECURE_WRITE_MULTI_BLOCK = 25
|
||||
ACMD26_SECURE_WRITE_MKB = 26
|
||||
ACMD38_SECURE_ERASE = 38
|
||||
ACMD43_GET_MKB = 43
|
||||
ACMD44_GET_MID = 44
|
||||
ACMD45_SET_CER_RN1 = 45
|
||||
ACMD46_SET_CER_RN2 = 46
|
||||
ACMD47_SET_CER_RES2 = 47
|
||||
ACMD48_SET_CER_RES1 = 48
|
||||
ACMD49_CHANGE_SECURE_AREA = 49
|
||||
)
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CSD struct {
|
||||
CSD_STRUCTURE byte // 2 R [127:126] 0x01 : CSD Structure
|
||||
TAAC byte // 8 R [119:112] 0x0E : Data Read Access-Time-1
|
||||
NSAC byte // 8 R [111:104] 0x00 : Data Read Access-Time-2 in CLK Cycles (NSAC*100)
|
||||
TRAN_SPEED byte // 8 R [103:96] 0x5A : Max. Data Transfer Rate
|
||||
CCC uint16 // 12 R [95:84] 0x5B5 : Card Command Classes
|
||||
READ_BL_LEN byte // 4 R [83:80] 0x09 : Max. Read Data Block Length
|
||||
READ_BL_PARTIAL byte // 1 R [79:79] 0x00 : Partial Blocks for Read Allowed
|
||||
WRITE_BLK_MISALIGN byte // 1 R [78:78] 0x00 : Write Block Misalignment
|
||||
READ_BLK_MISALIGN byte // 1 R [77:77] 0x00 : Read Block Misalignment
|
||||
DSR_IMP byte // 1 R [76:76] 0x00 : DSR Implemented
|
||||
C_SIZE uint32 // 22 R [69:48] 0xXXXXXX : Device Size
|
||||
ERASE_BLK_EN byte // 1 R [46:46] 0x01 : Erase Single Block Enable
|
||||
SECTOR_SIZE byte // 7 R [45:39] 0x7F : Erase Sector Size
|
||||
WP_GRP_SIZE byte // 7 R [38:32] 0x00 : Write Protect Group Size
|
||||
WP_GRP_ENABLE byte // 1 R [31:31] 0x00 : Write Protect Group Enable
|
||||
R2W_FACTOR byte // 3 R [28:26] 0x02 : Write Speed Factor
|
||||
WRITE_BL_LEN byte // 4 R [25:22] 0x09 : Max. Write data Block Length
|
||||
WRITE_BL_PARTIAL byte // 1 R [21:21] 0x00 : Partial Blocks for Write Allowed
|
||||
FILE_FORMAT_GRP byte // 1 R [15:15] 0x00 : File Format Group
|
||||
COPY byte // 1 RW [14:14] 0x00 :Copy Flag
|
||||
PERM_WRITE_PROTECT byte // 1 RW [13:13] 0x00 : Permanent Write Protection
|
||||
TMP_WRITE_PROTECT byte // 1 RW [12:12] 0x00 : Temporary Write Protection
|
||||
FILE_FORMAT byte // 2 R [11:10] 0x00 : File Format
|
||||
CRC byte // 7 RW [7:1] 0xXX : CRC
|
||||
}
|
||||
|
||||
func NewCSD(buf []byte) *CSD {
|
||||
return &CSD{
|
||||
CSD_STRUCTURE: (buf[0] & 0xC0) >> 6,
|
||||
TAAC: buf[1],
|
||||
NSAC: buf[2],
|
||||
TRAN_SPEED: buf[3],
|
||||
CCC: uint16(buf[4])<<4 | uint16(buf[5])>>4,
|
||||
READ_BL_LEN: buf[5] & 0x0F,
|
||||
READ_BL_PARTIAL: (buf[6] & 0x80) >> 7,
|
||||
WRITE_BLK_MISALIGN: (buf[6] & 0x40) >> 6,
|
||||
READ_BLK_MISALIGN: (buf[6] & 0x20) >> 5,
|
||||
DSR_IMP: (buf[6] & 0x10) >> 4,
|
||||
C_SIZE: uint32(buf[7]&0x3F)<<16 | uint32(buf[8])<<8 | uint32(buf[9]),
|
||||
ERASE_BLK_EN: (buf[10] & 0x40) >> 6,
|
||||
SECTOR_SIZE: (buf[10]&0x3F)<<1 | (buf[11]&0x80)>>7,
|
||||
WP_GRP_SIZE: buf[11] & 0x7F,
|
||||
WP_GRP_ENABLE: (buf[12] & 0x80) >> 7,
|
||||
R2W_FACTOR: (buf[12] & 0x1C) >> 2,
|
||||
WRITE_BL_LEN: (buf[12]&0x03)<<2 | (buf[13]&0xC0)>>6,
|
||||
WRITE_BL_PARTIAL: (buf[13] & 0x20) >> 5,
|
||||
FILE_FORMAT_GRP: (buf[14] & 0x80) >> 7,
|
||||
COPY: (buf[14] & 0x40) >> 6,
|
||||
PERM_WRITE_PROTECT: (buf[14] & 0x20) >> 5,
|
||||
TMP_WRITE_PROTECT: (buf[14] & 0x10) >> 4,
|
||||
FILE_FORMAT: (buf[14] & 0x0C) >> 2,
|
||||
CRC: (buf[15] & 0xFE) >> 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CSD) Dump() {
|
||||
fmt.Printf("CSD_STRUCTURE: %X\r\n", c.CSD_STRUCTURE)
|
||||
fmt.Printf("TAAC: %X\r\n", c.TAAC)
|
||||
fmt.Printf("NSAC: %X\r\n", c.NSAC)
|
||||
fmt.Printf("TRAN_SPEED: %X\r\n", c.TRAN_SPEED)
|
||||
fmt.Printf("CCC: %X\r\n", c.CCC)
|
||||
fmt.Printf("READ_BL_LEN: %X\r\n", c.READ_BL_LEN)
|
||||
fmt.Printf("READ_BL_PARTIAL: %X\r\n", c.READ_BL_PARTIAL)
|
||||
fmt.Printf("WRITE_BLK_MISALIGN: %X\r\n", c.WRITE_BLK_MISALIGN)
|
||||
fmt.Printf("READ_BLK_MISALIGN: %X\r\n", c.READ_BLK_MISALIGN)
|
||||
fmt.Printf("DSR_IMP: %X\r\n", c.DSR_IMP)
|
||||
fmt.Printf("C_SIZE: %X\r\n", c.C_SIZE)
|
||||
fmt.Printf("ERASE_BLK_EN: %X\r\n", c.ERASE_BLK_EN)
|
||||
fmt.Printf("SECTOR_SIZE: %X\r\n", c.SECTOR_SIZE)
|
||||
fmt.Printf("WP_GRP_SIZE: %X\r\n", c.WP_GRP_SIZE)
|
||||
fmt.Printf("WP_GRP_ENABLE: %X\r\n", c.WP_GRP_ENABLE)
|
||||
fmt.Printf("R2W_FACTOR: %X\r\n", c.R2W_FACTOR)
|
||||
fmt.Printf("WRITE_BL_LEN: %X\r\n", c.WRITE_BL_LEN)
|
||||
fmt.Printf("WRITE_BL_PARTIAL: %X\r\n", c.WRITE_BL_PARTIAL)
|
||||
fmt.Printf("FILE_FORMAT_GRP: %X\r\n", c.FILE_FORMAT_GRP)
|
||||
fmt.Printf("COPY: %X\r\n", c.COPY)
|
||||
fmt.Printf("PERM_WRITE_PROTECT: %X\r\n", c.PERM_WRITE_PROTECT)
|
||||
fmt.Printf("TMP_WRITE_PROTECT: %X\r\n", c.TMP_WRITE_PROTECT)
|
||||
fmt.Printf("FILE_FORMAT: %X\r\n", c.FILE_FORMAT)
|
||||
fmt.Printf("CRC: %X\r\n", c.CRC)
|
||||
}
|
||||
|
||||
func (c *CSD) Sectors() (int64, error) {
|
||||
sectors := int64(0)
|
||||
if c.CSD_STRUCTURE == 0x01 {
|
||||
// CSD version 2.0
|
||||
sectors = (int64(c.C_SIZE) + 1) * 1024
|
||||
} else if c.CSD_STRUCTURE == 0x00 {
|
||||
// CSD version 1.0 (old, <=2GB)
|
||||
return 0, fmt.Errorf("CSD format version 1.0 is not supported")
|
||||
} else {
|
||||
return 0, fmt.Errorf("unknown CSD format")
|
||||
}
|
||||
return sectors, nil
|
||||
}
|
||||
|
||||
func (c *CSD) Size() uint64 {
|
||||
return uint64(c.C_SIZE) * 512 * 1024
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"machine"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
_CMD_TIMEOUT = 100
|
||||
|
||||
_R1_IDLE_STATE = 1 << 0
|
||||
_R1_ERASE_RESET = 1 << 1
|
||||
_R1_ILLEGAL_COMMAND = 1 << 2
|
||||
_R1_COM_CRC_ERROR = 1 << 3
|
||||
_R1_ERASE_SEQUENCE_ERROR = 1 << 4
|
||||
_R1_ADDRESS_ERROR = 1 << 5
|
||||
_R1_PARAMETER_ERROR = 1 << 6
|
||||
|
||||
// card types
|
||||
SD_CARD_TYPE_SD1 = 1 // Standard capacity V1 SD card
|
||||
SD_CARD_TYPE_SD2 = 2 // Standard capacity V2 SD card
|
||||
SD_CARD_TYPE_SDHC = 3 // High Capacity SD card
|
||||
)
|
||||
|
||||
var (
|
||||
dummy [512]byte
|
||||
)
|
||||
|
||||
type Device struct {
|
||||
bus machine.SPI
|
||||
sck machine.Pin
|
||||
sdo machine.Pin
|
||||
sdi machine.Pin
|
||||
cs machine.Pin
|
||||
cmdbuf []byte
|
||||
dummybuf []byte
|
||||
tokenbuf []byte
|
||||
sdCardType byte
|
||||
CID *CID
|
||||
CSD *CSD
|
||||
}
|
||||
|
||||
func New(b machine.SPI, sck, sdo, sdi, cs machine.Pin) Device {
|
||||
return Device{
|
||||
bus: b,
|
||||
cs: cs,
|
||||
sck: sck,
|
||||
sdo: sdo,
|
||||
sdi: sdi,
|
||||
cmdbuf: make([]byte, 6),
|
||||
dummybuf: make([]byte, 512),
|
||||
tokenbuf: make([]byte, 1),
|
||||
sdCardType: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Device) Configure() error {
|
||||
return d.initCard()
|
||||
}
|
||||
|
||||
func (d *Device) initCard() error {
|
||||
d.bus.Configure(machine.SPIConfig{
|
||||
SCK: d.sck,
|
||||
SDO: d.sdo,
|
||||
SDI: d.sdi,
|
||||
Frequency: 250000,
|
||||
LSBFirst: false,
|
||||
Mode: 0, // phase=0, polarity=0
|
||||
})
|
||||
|
||||
// set pin modes
|
||||
d.cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
d.cs.High()
|
||||
|
||||
for i := range dummy {
|
||||
dummy[i] = 0xFF
|
||||
}
|
||||
|
||||
// clock card at least 100 cycles with cs high
|
||||
d.bus.Tx(dummy[:10], nil)
|
||||
|
||||
d.cs.Low()
|
||||
d.bus.Tx(dummy[:], nil)
|
||||
|
||||
// CMD0: init card; sould return _R1_IDLE_STATE (allow 5 attempts)
|
||||
ok := false
|
||||
tm := setTimeout(0, 2*time.Second)
|
||||
for !tm.expired() {
|
||||
// Wait up to 2 seconds to be the same as the Arduino
|
||||
if d.cmd(CMD0_GO_IDLE_STATE, 0, 0x95) == _R1_IDLE_STATE {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("no SD card")
|
||||
}
|
||||
|
||||
// CMD8: determine card version
|
||||
r := d.cmd(CMD8_SEND_IF_COND, 0x01AA, 0x87)
|
||||
if (r & _R1_ILLEGAL_COMMAND) == _R1_ILLEGAL_COMMAND {
|
||||
d.sdCardType = SD_CARD_TYPE_SD1
|
||||
return fmt.Errorf("init_card_v1 not impl\r\n")
|
||||
} else {
|
||||
// r7 response
|
||||
status := byte(0)
|
||||
for i := 0; i < 3; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if (status & 0x0F) != 0x01 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
|
||||
}
|
||||
|
||||
for i := 3; i < 4; i++ {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if status != 0xAA {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD8 %02X", status)
|
||||
}
|
||||
d.sdCardType = SD_CARD_TYPE_SD2
|
||||
}
|
||||
|
||||
// initialize card and send host supports SDHC if SD2
|
||||
arg := uint32(0)
|
||||
if d.sdCardType == SD_CARD_TYPE_SD2 {
|
||||
arg = 0x40000000
|
||||
}
|
||||
|
||||
// check for timeout
|
||||
ok = false
|
||||
tm = setTimeout(0, 2*time.Second)
|
||||
for !tm.expired() {
|
||||
if d.acmd(ACMD41_SD_APP_OP_COND, arg) == 0 {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("SD_CARD_ERROR_ACMD41")
|
||||
}
|
||||
|
||||
// if SD2 read OCR register to check for SDHC card
|
||||
if d.sdCardType == SD_CARD_TYPE_SD2 {
|
||||
if d.cmd(CMD58_READ_OCR, 0, 0xFF) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD58")
|
||||
}
|
||||
|
||||
status, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status & 0xC0) == 0xC0 {
|
||||
d.sdCardType = SD_CARD_TYPE_SDHC
|
||||
}
|
||||
// discard rest of ocr - contains allowed voltage range
|
||||
for i := 1; i < 4; i++ {
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
if d.cmd(CMD16_SET_BLOCKLEN, 0x0200, 0xFF) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_CMD16")
|
||||
}
|
||||
|
||||
var buf [16]byte
|
||||
// read CID
|
||||
err := d.ReadCID(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CID = NewCID(buf[:])
|
||||
|
||||
// read CSD
|
||||
err = d.ReadCSD(buf[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.CSD = NewCSD(buf[:])
|
||||
|
||||
d.cs.High()
|
||||
|
||||
d.bus.Configure(machine.SPIConfig{
|
||||
SCK: d.sck,
|
||||
SDO: d.sdo,
|
||||
SDI: d.sdi,
|
||||
Frequency: 4000000,
|
||||
LSBFirst: false,
|
||||
Mode: 0, // phase=0, polarity=0
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Device) acmd(cmd byte, arg uint32) byte {
|
||||
d.cmd(CMD55_APP_CMD, 0, 0xFF)
|
||||
return d.cmd(cmd, arg, 0xFF)
|
||||
}
|
||||
|
||||
func (d Device) cmd(cmd byte, arg uint32, crc byte) byte {
|
||||
d.cs.Low()
|
||||
|
||||
if cmd != 12 {
|
||||
d.waitNotBusy(300 * time.Millisecond)
|
||||
}
|
||||
|
||||
// create and send the command
|
||||
buf := d.cmdbuf
|
||||
buf[0] = 0x40 | cmd
|
||||
buf[1] = byte(arg >> 24)
|
||||
buf[2] = byte(arg >> 16)
|
||||
buf[3] = byte(arg >> 8)
|
||||
buf[4] = byte(arg)
|
||||
buf[5] = crc
|
||||
d.bus.Tx(buf, nil)
|
||||
|
||||
if cmd == 12 {
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
}
|
||||
|
||||
// wait for the response (response[7] == 0)
|
||||
for i := 0; i < 0xFFFF; i++ {
|
||||
d.bus.Tx([]byte{0xFF}, d.tokenbuf)
|
||||
response := d.tokenbuf[0]
|
||||
if (response & 0x80) == 0 {
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
//// timeout
|
||||
d.cs.High()
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
return 0xFF // -1
|
||||
}
|
||||
|
||||
func (d Device) waitNotBusy(timeout time.Duration) error {
|
||||
tm := setTimeout(1, timeout)
|
||||
for !tm.expired() {
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r == 0xFF {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d Device) waitStartBlock() error {
|
||||
status := byte(0xFF)
|
||||
|
||||
tm := setTimeout(0, 300*time.Millisecond)
|
||||
for !tm.expired() {
|
||||
var err error
|
||||
status, err = d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
d.cs.High()
|
||||
return err
|
||||
}
|
||||
if status != 0xFF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if status != 254 {
|
||||
d.cs.High()
|
||||
return fmt.Errorf("SD_CARD_START_BLOCK")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadCSD reads the CSD using CMD9.
|
||||
func (d Device) ReadCSD(csd []byte) error {
|
||||
return d.readRegister(CMD9_SEND_CSD, csd)
|
||||
}
|
||||
|
||||
// ReadCID reads the CID using CMD10
|
||||
func (d Device) ReadCID(csd []byte) error {
|
||||
return d.readRegister(CMD10_SEND_CID, csd)
|
||||
}
|
||||
|
||||
func (d Device) readRegister(cmd uint8, dst []byte) error {
|
||||
if d.cmd(cmd, 0, 0xFF) != 0 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_READ_REG")
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return err
|
||||
}
|
||||
// transfer data
|
||||
for i := uint16(0); i < 16; i++ {
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst[i] = r
|
||||
}
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadData reads 512 bytes from sdcard into dst.
|
||||
func (d Device) ReadData(block uint32, dst []byte) error {
|
||||
if len(dst) < 512 {
|
||||
return fmt.Errorf("len(dst) must be greater than or equal to 512")
|
||||
}
|
||||
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD17_READ_SINGLE_BLOCK, block, 0xFF) != 0 {
|
||||
return fmt.Errorf("CMD17 error")
|
||||
}
|
||||
if err := d.waitStartBlock(); err != nil {
|
||||
return fmt.Errorf("waitStartBlock()")
|
||||
}
|
||||
|
||||
err := d.bus.Tx(dummy[:512], dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// skip CRC (2byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// TODO: probably not necessary
|
||||
d.cs.High()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMultiStart starts the continuous write mode using CMD25.
|
||||
func (d Device) WriteMultiStart(block uint32) error {
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD25_WRITE_MULTIPLE_BLOCK, block, 0xFF) != 0 {
|
||||
return fmt.Errorf("CMD25 error")
|
||||
}
|
||||
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMulti performs continuous writing. It is necessary to call
|
||||
// WriteMultiStart() in prior.
|
||||
func (d Device) WriteMulti(buf []byte) error {
|
||||
// send Data Token for CMD25
|
||||
d.bus.Transfer(byte(0xFC))
|
||||
|
||||
for i := 0; i < 512; i++ {
|
||||
_, err := d.bus.Transfer(buf[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// send dummy CRC (2 byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// Data Resp.
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (r & 0x1F) != 0x05 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE")
|
||||
}
|
||||
|
||||
// wait no busy
|
||||
err = d.waitNotBusy(600 * time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteMultiStop exits the continuous write mode.
|
||||
func (d Device) WriteMultiStop() error {
|
||||
defer d.cs.High()
|
||||
|
||||
// Stop Tran token for CMD25
|
||||
d.bus.Transfer(0xFD)
|
||||
|
||||
// skip 1 byte
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
err := d.waitNotBusy(600 * time.Millisecond)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteData writes 512 bytes from dst to sdcard.
|
||||
func (d Device) WriteData(block uint32, src []byte) error {
|
||||
if len(src) < 512 {
|
||||
return fmt.Errorf("len(src) must be greater than or equal to 512")
|
||||
}
|
||||
|
||||
// use address if not SDHC card
|
||||
if d.sdCardType != SD_CARD_TYPE_SDHC {
|
||||
block <<= 9
|
||||
}
|
||||
if d.cmd(CMD24_WRITE_BLOCK, block, 0xFF) != 0 {
|
||||
return fmt.Errorf("CMD24 error")
|
||||
}
|
||||
|
||||
// wait 1 byte?
|
||||
token := byte(0xFE)
|
||||
d.bus.Transfer(token)
|
||||
|
||||
err := d.bus.Tx(src[:512], nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send dummy CRC (2 byte)
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
d.bus.Transfer(byte(0xFF))
|
||||
|
||||
// Data Resp.
|
||||
r, err := d.bus.Transfer(byte(0xFF))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (r & 0x1F) != 0x05 {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE")
|
||||
}
|
||||
|
||||
// wait no busy
|
||||
err = d.waitNotBusy(600 * time.Millisecond)
|
||||
if err != nil {
|
||||
return fmt.Errorf("SD_CARD_ERROR_WRITE_TIMEOUT")
|
||||
}
|
||||
|
||||
// TODO: probably not necessary
|
||||
d.cs.High()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadAt reads the given number of bytes from the sdcard.
|
||||
func (dev *Device) ReadAt(buf []byte, addr int64) (int, error) {
|
||||
block := uint64(addr)
|
||||
// use address if not SDHC card
|
||||
if dev.sdCardType == SD_CARD_TYPE_SDHC {
|
||||
block >>= 9
|
||||
}
|
||||
|
||||
idx := uint32(0)
|
||||
|
||||
start := uint32(addr % 512)
|
||||
end := uint32(0)
|
||||
remain := uint32(len(buf))
|
||||
|
||||
// If data starts in the middle
|
||||
if 0 < start {
|
||||
if start+remain <= 512 {
|
||||
end = start + remain
|
||||
} else {
|
||||
end = 512
|
||||
}
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// If more than 512 bytes left
|
||||
for 512 <= remain {
|
||||
start = 0
|
||||
end = 512
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// Read to the end
|
||||
if 0 < remain {
|
||||
start = 0
|
||||
end = remain
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(buf[idx:], dev.dummybuf[start:end])
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
return int(idx), nil
|
||||
}
|
||||
|
||||
// WriteAt writes the given number of bytes to sdcard.
|
||||
func (dev *Device) WriteAt(buf []byte, addr int64) (n int, err error) {
|
||||
block := uint64(addr)
|
||||
// use address if not SDHC card
|
||||
if dev.sdCardType == SD_CARD_TYPE_SDHC {
|
||||
block >>= 9
|
||||
}
|
||||
|
||||
idx := uint32(0)
|
||||
|
||||
start := uint32(addr % 512)
|
||||
end := uint32(0)
|
||||
remain := uint32(len(buf))
|
||||
|
||||
// If data starts in the middle
|
||||
if 0 < start {
|
||||
if start+remain <= 512 {
|
||||
end = start + remain
|
||||
} else {
|
||||
end = 512
|
||||
}
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(dev.dummybuf[start:end], buf[idx:])
|
||||
|
||||
err = dev.WriteData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// If more than 512 bytes left
|
||||
for 512 <= remain {
|
||||
start = 0
|
||||
end = 512
|
||||
|
||||
err := dev.WriteData(uint32(block), buf[idx:idx+512])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
// Write to the end
|
||||
if 0 < remain {
|
||||
start = 0
|
||||
end = remain
|
||||
|
||||
err := dev.ReadData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
copy(dev.dummybuf[start:end], buf[idx:])
|
||||
|
||||
err = dev.WriteData(uint32(block), dev.dummybuf)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remain -= end - start
|
||||
idx += end - start
|
||||
block++
|
||||
}
|
||||
|
||||
return int(idx), nil
|
||||
}
|
||||
|
||||
// Size returns the number of bytes in this sdcard.
|
||||
func (dev *Device) Size() int64 {
|
||||
return int64(dev.CSD.Size())
|
||||
}
|
||||
|
||||
// WriteBlockSize returns the block size in which data can be written to
|
||||
// memory.
|
||||
func (dev *Device) WriteBlockSize() int64 {
|
||||
return 512
|
||||
}
|
||||
|
||||
// EraseBlockSize returns the smallest erasable area on this sdcard in bytes.
|
||||
func (dev *Device) EraseBlockSize() int64 {
|
||||
return 512
|
||||
}
|
||||
|
||||
// EraseBlocks erases the given number of blocks.
|
||||
func (dev *Device) EraseBlocks(start, len int64) error {
|
||||
dev.WriteMultiStart(uint32(start))
|
||||
|
||||
for i := range dev.dummybuf {
|
||||
dev.dummybuf[i] = 0
|
||||
}
|
||||
|
||||
for i := 0; i < int(len); i++ {
|
||||
dev.WriteMulti(dev.dummybuf)
|
||||
}
|
||||
|
||||
dev.WriteMultiStop()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package sdcard
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var timeoutTimer [2]timer
|
||||
|
||||
type timer struct {
|
||||
start int64
|
||||
timeout int64
|
||||
}
|
||||
|
||||
func setTimeout(timerID int, timeout time.Duration) *timer {
|
||||
timeoutTimer[timerID].start = time.Now().UnixNano()
|
||||
timeoutTimer[timerID].timeout = timeout.Nanoseconds()
|
||||
return &timeoutTimer[timerID]
|
||||
}
|
||||
|
||||
func (t timer) expired() bool {
|
||||
return time.Now().UnixNano() > (t.start + t.timeout)
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package servo
|
||||
|
||||
import "machine"
|
||||
|
||||
// PWM is the interface necessary for controlling typical servo motors.
|
||||
type PWM interface {
|
||||
Configure(config machine.PWMConfig) error
|
||||
Channel(pin machine.Pin) (channel uint8, err error)
|
||||
Top() uint32
|
||||
Set(channel uint8, value uint32)
|
||||
}
|
||||
|
||||
// Array is an array of servos controlled by a single PWM peripheral. On most
|
||||
// chips, one PWM peripheral can control multiple servos (usually two or four).
|
||||
type Array struct {
|
||||
pwm PWM
|
||||
}
|
||||
|
||||
// Servo is a single servo (connected to one PWM output) that's part of a servo
|
||||
// array.
|
||||
type Servo struct {
|
||||
pwm PWM
|
||||
channel uint8
|
||||
}
|
||||
|
||||
const pwmPeriod = 20e6 // 20ms
|
||||
|
||||
// NewArray returns a new servo array based on the given PWM, for if you want to
|
||||
// control multiple servos from a single PWM peripheral. Using a single PWM for
|
||||
// multiple servos saves PWM peripherals for other uses and might use less power
|
||||
// depending on the chip.
|
||||
//
|
||||
// If you only want to control a single servo, you could use the New shorthand
|
||||
// instead.
|
||||
func NewArray(pwm PWM) (Array, error) {
|
||||
err := pwm.Configure(machine.PWMConfig{
|
||||
Period: pwmPeriod,
|
||||
})
|
||||
if err != nil {
|
||||
return Array{}, err
|
||||
}
|
||||
return Array{pwm}, nil
|
||||
}
|
||||
|
||||
// Add adds a new servo to the servo array. Please check the chip documentation
|
||||
// which pins can be controlled by the given PWM: depending on the chip this
|
||||
// might be rigid (only a single pin) or very flexible (you can pick any pin).
|
||||
func (array Array) Add(pin machine.Pin) (Servo, error) {
|
||||
channel, err := array.pwm.Channel(pin)
|
||||
if err != nil {
|
||||
return Servo{}, err
|
||||
}
|
||||
return Servo{
|
||||
pwm: array.pwm,
|
||||
channel: channel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// New is a shorthand for NewArray and array.Add. This is useful if you only
|
||||
// want to control just a single servo.
|
||||
func New(pwm PWM, pin machine.Pin) (Servo, error) {
|
||||
array, err := NewArray(pwm)
|
||||
if err != nil {
|
||||
return Servo{}, err
|
||||
}
|
||||
return array.Add(pin)
|
||||
}
|
||||
|
||||
// SetMicroseconds sets the output signal to be high for the given number of
|
||||
// microseconds. For many servos the range is normally between 1000µs and 2000µs
|
||||
// for 90° of rotation (with 1500µs being the 'neutral' middle position).
|
||||
//
|
||||
// In many cases they can actually go a bit further, with a wider range of
|
||||
// supported pulse ranges. For example, they might allow pulse widths from 500µs
|
||||
// to 2500µs, but be warned that going outside of the 1000µs-2000µs range might
|
||||
// break the servo as it might destroy the gears if it doesn't support this
|
||||
// range. Therefore, to be sure check the datasheet before you try values
|
||||
// outside of the 1000µs-2000µs range.
|
||||
func (s Servo) SetMicroseconds(microseconds int16) {
|
||||
value := uint64(s.pwm.Top()) * uint64(microseconds) / (pwmPeriod / 1000)
|
||||
s.pwm.Set(s.channel, uint32(value))
|
||||
}
|
||||
+7
-3
@@ -13,7 +13,7 @@ import (
|
||||
"tinygo.org/x/drivers"
|
||||
)
|
||||
|
||||
// Device wraps an SPI connection.
|
||||
// Device wraps I2C or SPI connection.
|
||||
type Device struct {
|
||||
bus Buser
|
||||
buffer []byte
|
||||
@@ -21,6 +21,7 @@ type Device struct {
|
||||
height int16
|
||||
bufferSize int16
|
||||
vccState VccMode
|
||||
canReset bool
|
||||
}
|
||||
|
||||
// Config is the configuration for the display
|
||||
@@ -98,6 +99,7 @@ func (d *Device) Configure(cfg Config) {
|
||||
}
|
||||
d.bufferSize = d.width * d.height / 8
|
||||
d.buffer = make([]byte, d.bufferSize)
|
||||
d.canReset = cfg.Address != 0 || d.width != 128 || d.height != 64 // I2C or not 128x64
|
||||
|
||||
d.bus.configure()
|
||||
|
||||
@@ -178,9 +180,11 @@ func (d *Device) ClearDisplay() {
|
||||
|
||||
// Display sends the whole buffer to the screen
|
||||
func (d *Device) Display() error {
|
||||
// Reset the screen to 0x0
|
||||
// This works fine with I2C
|
||||
// In the 128x64 (SPI) screen resetting to 0x0 after 128 times corrupt the buffer
|
||||
// Since we're printing the whole buffer, avoid resetting it
|
||||
if d.width != 128 || d.height != 64 {
|
||||
// Since we're printing the whole buffer, avoid resetting it in this case
|
||||
if d.canReset {
|
||||
d.Command(COLUMNADDR)
|
||||
d.Command(0)
|
||||
d.Command(uint8(d.width - 1))
|
||||
|
||||
+10
-53
@@ -1,61 +1,18 @@
|
||||
package tester
|
||||
|
||||
// MaxRegisters is the maximum number of registers supported for a Device.
|
||||
const MaxRegisters = 200
|
||||
const MaxRegisters = 255
|
||||
|
||||
// I2CDevice represents a mock I2C device on a mock I2C bus.
|
||||
type I2CDevice struct {
|
||||
c Failer
|
||||
// addr is the i2c device address.
|
||||
addr uint8
|
||||
// Registers holds the device registers. It can be inspected
|
||||
// or changed as desired for testing.
|
||||
Registers [MaxRegisters]uint8
|
||||
// If Err is non-nil, it will be returned as the error from the
|
||||
// I2C methods.
|
||||
Err error
|
||||
}
|
||||
type I2CDevice interface {
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
ReadRegister(r uint8, buf []byte) error
|
||||
|
||||
// NewI2CDevice returns a new mock I2C device.
|
||||
func NewI2CDevice(c Failer, addr uint8) *I2CDevice {
|
||||
return &I2CDevice{
|
||||
c: c,
|
||||
addr: addr,
|
||||
}
|
||||
}
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
WriteRegister(r uint8, buf []byte) error
|
||||
|
||||
// Addr returns the Device address.
|
||||
func (d *I2CDevice) Addr() uint8 {
|
||||
return d.addr
|
||||
}
|
||||
// Tx implements I2C.Tx
|
||||
Tx(w, r []byte) error
|
||||
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
func (d *I2CDevice) ReadRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
d.assertRegisterRange(r, buf)
|
||||
copy(buf, d.Registers[r:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
func (d *I2CDevice) WriteRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
d.assertRegisterRange(r, buf)
|
||||
copy(d.Registers[r:], buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// assertRegisterRange asserts that reading or writing the given
|
||||
// register and subsequent registers is in range of the available registers.
|
||||
func (d *I2CDevice) assertRegisterRange(r uint8, buf []byte) {
|
||||
if int(r) >= len(d.Registers) {
|
||||
d.c.Fatalf("register read/write [%#x, %#x] start out of range", r, int(r)+len(buf))
|
||||
}
|
||||
if int(r)+len(buf) > len(d.Registers) {
|
||||
d.c.Fatalf("register read/write [%#x, %#x] end out of range", r, int(r)+len(buf))
|
||||
}
|
||||
// Addr returns the Device address.
|
||||
Addr() uint8
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package tester
|
||||
|
||||
// I2CDevice represents a mock I2C device on a mock I2C bus with 16-bit registers.
|
||||
type I2CDevice16 struct {
|
||||
c Failer
|
||||
// addr is the i2c device address.
|
||||
addr uint8
|
||||
// Registers holds the device registers. It can be inspected
|
||||
// or changed as desired for testing.
|
||||
Registers map[uint8]uint16
|
||||
// If Err is non-nil, it will be returned as the error from the
|
||||
// I2C methods.
|
||||
Err error
|
||||
}
|
||||
|
||||
// NewI2CDevice returns a new mock I2C device.
|
||||
//
|
||||
// To use this mock, populate the Registers map with known / expected
|
||||
// registers. Attempts by the code under test to write to a register
|
||||
// that has not been populated into the map will be treated as an
|
||||
// error.
|
||||
func NewI2CDevice16(c Failer, addr uint8) *I2CDevice16 {
|
||||
return &I2CDevice16{
|
||||
c: c,
|
||||
addr: addr,
|
||||
Registers: map[uint8]uint16{},
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns the Device address.
|
||||
func (d *I2CDevice16) Addr() uint8 {
|
||||
return d.addr
|
||||
}
|
||||
|
||||
// ReadRegister implements I2C.ReadRegister.
|
||||
func (d *I2CDevice16) ReadRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
|
||||
if len(buf) > 2 {
|
||||
d.c.Fatalf("register read [%#x, %#x] oversized buffer", r, len(buf))
|
||||
}
|
||||
|
||||
val, ok := d.Registers[r]
|
||||
if !ok {
|
||||
d.c.Fatalf("register read [%#x] unknown register", r)
|
||||
}
|
||||
|
||||
buf[0] = byte(val >> 8)
|
||||
buf[1] = byte(val & 0xff)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRegister implements I2C.WriteRegister.
|
||||
func (d *I2CDevice16) WriteRegister(r uint8, buf []byte) error {
|
||||
if d.Err != nil {
|
||||
return d.Err
|
||||
}
|
||||
|
||||
if len(buf) != 2 {
|
||||
d.c.Fatalf("register write [%#x, %#x] mis-sized write", r, len(buf))
|
||||
}
|
||||
|
||||
_, ok := d.Registers[r]
|
||||
if !ok {
|
||||
d.c.Fatalf("register write [%#x] unknown register", r)
|
||||
}
|
||||
|
||||
d.Registers[r] = uint16(buf[0])<<8 | uint16(buf[1])
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tx implements I2C.Tx.
|
||||
func (bus *I2CDevice16) Tx(w, r []byte) error {
|
||||
// TODO: implement this
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package tester
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestCreate16(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := NewI2CBus(c)
|
||||
d := NewI2CDevice16(c, 8)
|
||||
bus.AddDevice(d)
|
||||
}
|
||||
|
||||
func TestRead16(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := NewI2CBus(c)
|
||||
d := NewI2CDevice16(c, 8)
|
||||
bus.AddDevice(d)
|
||||
|
||||
// Setup a random register
|
||||
d.Registers[3] = 0x1234
|
||||
|
||||
buf := []byte{0, 0}
|
||||
err := bus.ReadRegister(8, 3, buf)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(buf[0], qt.Equals, byte(0x12))
|
||||
c.Assert(buf[1], qt.Equals, byte(0x34))
|
||||
}
|
||||
|
||||
func TestWrite16(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
bus := NewI2CBus(c)
|
||||
d := NewI2CDevice16(c, 8)
|
||||
bus.AddDevice(d)
|
||||
|
||||
d.Registers[9] = 0x0
|
||||
err := bus.WriteRegister(8, 9, []byte{0xbe, 0xad})
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(d.Registers[9], qt.Equals, uint16(0xbead))
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user