Compare commits

...

10 Commits

Author SHA1 Message Date
sago35 4c59a3ff90 85 fps @ feather-m4-can 2021-06-17 14:48:04 +09:00
sago35 915ca320de 96 fps @ feather-m4-can 2021-06-17 14:46:08 +09:00
sago35 5645eb3f91 ws2812: add support for qtpy and atsame5x 2021-06-01 21:09:08 +02:00
sago35 6b914f7af7 sdcard: add support for fatfs 2021-05-31 17:26:21 +02:00
Ayke van Laethem 5a02fe068b apa102: use 4-byte buffer to improve speed
Instead of sending the APA102 data byte by byte, it's more efficient to
send multiple bytes at once (especially when the SPI peripheral uses
DMA). This requires the apa102.Device object to be a pointer receiver to
avoid excessive heap allocations.

This commit also just happens to work around a hardware bug on the
nrf52832:
https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52832_Rev2%2FERR%2FnRF52832%2FRev2%2Flatest%2Fanomaly_832_58.html&anchor=anomaly_832_58
2021-05-30 22:39:50 +02:00
Ayke van Laethem 5ecefde991 bmi160: avoid heap allocations
The nrf52 series chips use DMA for writing and reading SPI data, which
means that the transmit and receive buffer slices escape. This can
easily be solved by pre-allocating the buffer in the BMI160 driver and
reusing this buffer every time.
2021-05-30 22:11:27 +02:00
Yurii Soldak 9adbde99a2 wifinina: avoid fmt package 2021-05-28 18:09:29 +02:00
Yurii Soldak a09ec31e7a Fix RSSI command for WiFiNINA
+ Print current SSID
+ Wait for correct time before printing it out
+ Cleanup
2021-05-27 08:00:13 +02:00
sago35 d07ad40373 sdcard: add support for spi sdcard driver 2021-05-23 11:22:42 +02:00
sago35 aa17ffd1cb uart: use machine.Serial as the default output 2021-05-14 13:26:59 +02:00
52 changed files with 3801 additions and 1556 deletions
+5 -1
View File
@@ -193,12 +193,16 @@ endif
@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 keypad4x4 max72xx p1am tone tm1637 \
pcf8563 mcp2515 servo
pcf8563 mcp2515 servo sdcard
TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS))
unit-test:
+2 -1
View File
@@ -52,7 +52,7 @@ func main() {
## Currently supported devices
The following 65 devices are supported.
The following 66 devices are supported.
| Device Name | Interface Type |
|----------|-------------|
@@ -106,6 +106,7 @@ The following 65 devices are supported.
| [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 |
+19 -17
View File
@@ -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
View File
@@ -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()
}
+1 -1
View File
@@ -13,7 +13,7 @@ import (
)
var (
apa apa102.Device
apa *apa102.Device
pwm = machine.TCC0
leds = make([]color.RGBA, 1)
+1 -1
View File
@@ -30,7 +30,7 @@ var (
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
console = machine.Serial
adaptor *espat.Device
)
+1 -1
View File
@@ -36,7 +36,7 @@ var (
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
console = machine.Serial
adaptor *espat.Device
topic = "tinygo"
+1 -1
View File
@@ -37,7 +37,7 @@ var (
tx = machine.PA22
rx = machine.PA23
console = machine.UART0
console = machine.Serial
adaptor *espat.Device
cl mqtt.Client
+1 -1
View File
@@ -20,7 +20,7 @@ var (
input [consoleBufLen]byte
store [storageBufLen]byte
console = machine.UART0
console = machine.Serial
dev *flash.Device
+1 -1
View File
@@ -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
+128 -69
View File
@@ -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)
}
}
+363
View File
@@ -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("==> ")
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+51
View File
@@ -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)
}
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+519
View File
@@ -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("==> ")
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+49
View File
@@ -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)
}
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+17
View File
@@ -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
}
+43 -27
View File
@@ -2,9 +2,8 @@
package main
import (
"encoding/binary"
"fmt"
"machine"
"strconv"
"time"
"tinygo.org/x/drivers/wifinina"
@@ -52,15 +51,37 @@ func main() {
connectToAP()
for {
println("---------------------------------")
println("----------------------------------------")
printSSID()
printRSSI()
printMac()
printIPs()
printTime()
printMacAddress()
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 {
@@ -69,39 +90,38 @@ func printIPs() {
}
println("IP: ", ip.String())
println("Subnet: ", subnet.String())
println("Gateway IP: ", gateway.String())
println("Gateway: ", gateway.String())
}
func printTime() {
print("Time: ")
t, err := adaptor.GetTime()
if err != nil {
println("Unknown (error: ", err.Error(), ")")
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 printMacAddress() {
print("MAC Address: ")
b := make([]byte, 8)
func printMac() {
print("MAC: ")
mac, err := adaptor.GetMACAddress()
if err != nil {
println("Unknown (", err.Error(), ")")
}
binary.LittleEndian.PutUint64(b, uint64(mac))
macAddress := ""
for i := 5; i >= 0; i-- {
macAddress += fmt.Sprintf("%0X", b[i])
if i != 0 {
macAddress += ":"
}
}
println(macAddress)
println(mac.String())
}
// Wait for user to open serial console
func waitSerial() {
for !machine.UART0.DTR() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
@@ -115,16 +135,12 @@ func connectToAP() {
}
}
time.Sleep(2 * time.Second)
message("Connecting to " + ssid)
println("Connecting to " + ssid)
adaptor.SetPassphrase(ssid, pass)
for st, _ := adaptor.GetConnectionStatus(); st != wifinina.StatusConnected; {
message("Connection status: " + st.String())
println("Connection status: " + st.String())
time.Sleep(1 * time.Second)
st, _ = adaptor.GetConnectionStatus()
}
message("Connected.")
}
func message(msg string) {
println(msg, "\r")
println("Connected.")
}
+1 -1
View File
@@ -92,7 +92,7 @@ func main() {
// Wait for user to open serial console
func waitSerial() {
for !machine.UART0.DTR() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+1 -1
View File
@@ -77,7 +77,7 @@ func main() {
// Wait for user to open serial console
func waitSerial() {
for !machine.UART0.DTR() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+1 -1
View File
@@ -74,7 +74,7 @@ func main() {
// Wait for user to open serial console
func waitSerial() {
for !machine.UART0.DTR() {
for !machine.Serial.DTR() {
time.Sleep(100 * time.Millisecond)
}
}
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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
-1
View File
@@ -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})
+3 -2
View File
@@ -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
+16
View File
@@ -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()
}
+6
View File
@@ -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
)
+7
View File
@@ -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=
+55 -36
View File
@@ -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
View File
@@ -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)
}
}
+20
View File
@@ -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
}
```
+49
View File
@@ -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,
}
}
+52
View File
@@ -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
View File
@@ -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
}
+641
View File
@@ -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
}
+22
View File
@@ -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)
}
+5 -5
View File
@@ -1,7 +1,7 @@
package wifinina
import (
"fmt"
"errors"
"strconv"
"time"
@@ -104,7 +104,7 @@ func (drv *Driver) connectSocket(addr, portStr string, mode uint8) error {
func convertPort(portStr string) (uint16, error) {
p64, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return 0, fmt.Errorf("could not convert port to uint16: %w", err)
return 0, errors.New("could not convert port to uint16: " + err.Error())
}
return uint16(p64), nil
}
@@ -175,13 +175,13 @@ func (drv *Driver) Write(b []byte) (n int, err error) {
}
if drv.proto == ProtoModeUDP {
if err := drv.dev.StartClient("", drv.ip, drv.port, drv.sock, drv.proto); err != nil {
return 0, fmt.Errorf("error in startClient: %w", err)
return 0, errors.New("error in startClient: " + err.Error())
}
if _, err := drv.dev.InsertDataBuf(b, drv.sock); err != nil {
return 0, fmt.Errorf("error in insertDataBuf: %w", err)
return 0, errors.New("error in insertDataBuf: " + err.Error())
}
if _, err := drv.dev.SendUDPData(drv.sock); err != nil {
return 0, fmt.Errorf("error in sendUDPData: %w", err)
return 0, errors.New("error in sendUDPData: " + err.Error())
}
return len(b), nil
} else {
+24 -10
View File
@@ -8,7 +8,10 @@ package wifinina // import "tinygo.org/x/drivers/wifinina"
import (
"encoding/binary"
"fmt"
"encoding/hex"
"fmt" // used only in debug printouts and is optimized out when debugging is disabled
"strconv"
"strings"
"time"
"machine"
@@ -213,15 +216,16 @@ func (addr IPAddress) String() string {
if len(addr) < 4 {
return ""
}
return fmt.Sprintf("%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3])
return strconv.Itoa(int(addr[0])) + "." + strconv.Itoa(int(addr[1])) + "." + strconv.Itoa(int(addr[2])) + "." + strconv.Itoa(int(addr[3]))
}
func ParseIPv4(s string) (IPAddress, error) {
var v0, v1, v2, v3 uint8
if _, err := fmt.Sscanf(s, "%d.%d.%d.%d", &v0, &v1, &v2, &v3); err != nil {
return "", err
}
return IPAddress([]byte{v0, v1, v2, v3}), nil
v := strings.Split(s, ".")
v0, _ := strconv.Atoi(v[0])
v1, _ := strconv.Atoi(v[1])
v2, _ := strconv.Atoi(v[2])
v3, _ := strconv.Atoi(v[3])
return IPAddress([]byte{byte(v0), byte(v1), byte(v2), byte(v3)}), nil
}
func (addr IPAddress) AsUint32() uint32 {
@@ -235,13 +239,23 @@ func (addr IPAddress) AsUint32() uint32 {
type MACAddress uint64
func (addr MACAddress) String() string {
return fmt.Sprintf("%016X", uint64(addr))
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(addr))
encoded := hex.EncodeToString(b)
result := ""
for i := 2; i < 8; i++ {
result += encoded[i*2 : i*2+2]
if i < 7 {
result += ":"
}
}
return result
}
type Error uint8
func (err Error) Error() string {
return fmt.Sprintf("wifinina error: 0x%02X", uint8(err))
return "wifinina error: 0x" + hex.EncodeToString([]byte{uint8(err)})
}
// Cmd Struct Message */
@@ -484,7 +498,7 @@ func (d *Device) GetCurrentBSSID() (MACAddress, error) {
}
func (d *Device) GetCurrentRSSI() (int32, error) {
return d.getInt32(d.req1(CmdGetCurrBSSID))
return d.getInt32(d.req1(CmdGetCurrRSSI))
}
func (d *Device) GetCurrentSSID() (string, error) {
+1 -1
View File
@@ -1,4 +1,4 @@
// +build atsamd51
// +build atsamd51 atsame5x
package ws2812