Skip to content

Commit 2ce17a1

Browse files
aykevldeadprogram
authored andcommitted
esp8266: add support for this chip
Many thanks to cnlohr for the nosdk8266 project: https://github.com/cnlohr/nosdk8266
1 parent 0b9b293 commit 2ce17a1

File tree

11 files changed

+489
-31
lines changed

11 files changed

+489
-31
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,9 @@ ifneq ($(AVR), 0)
356356
endif
357357
ifneq ($(XTENSA), 0)
358358
$(TINYGO) build -size short -o test.bin -target=esp32-wroom-32 examples/blinky1
359+
@$(MD5SUM) test.bin
360+
$(TINYGO) build -size short -o test.bin -target=nodemcu examples/blinky1
361+
@$(MD5SUM) test.bin
359362
endif
360363
$(TINYGO) build -size short -o test.hex -target=hifive1b examples/blinky1
361364
@$(MD5SUM) test.hex

builder/build.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,10 +292,11 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
292292
if err != nil {
293293
return err
294294
}
295-
case "esp32":
296-
// Special format for the ESP32 chip (parsed by the ROM bootloader).
295+
case "esp32", "esp8266":
296+
// Special format for the ESP family of chips (parsed by the ROM
297+
// bootloader).
297298
tmppath = filepath.Join(dir, "main"+outext)
298-
err := makeESP32FirmareImage(executable, tmppath)
299+
err := makeESPFirmareImage(executable, tmppath, outputBinaryFormat)
299300
if err != nil {
300301
return err
301302
}

builder/esp.go

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ type espImageSegment struct {
2222
data []byte
2323
}
2424

25-
// makeESP32Firmare converts an input ELF file to an image file for the ESP32
26-
// chip. This is a special purpose image format just for the ESP32 chip, and is
27-
// parsed by the on-chip mask ROM bootloader.
25+
// makeESPFirmare converts an input ELF file to an image file for an ESP32 or
26+
// ESP8266 chip. This is a special purpose image format just for the ESP chip
27+
// family, and is parsed by the on-chip mask ROM bootloader.
2828
//
2929
// The following documentation has been used:
3030
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
3131
// https://github.com/espressif/esp-idf/blob/8fbb63c2a701c22ccf4ce249f43aded73e134a34/components/bootloader_support/include/esp_image_format.h#L58
3232
// https://github.com/espressif/esptool/blob/master/esptool.py
33-
func makeESP32FirmareImage(infile, outfile string) error {
33+
func makeESPFirmareImage(infile, outfile, format string) error {
3434
inf, err := elf.Open(infile)
3535
if err != nil {
3636
return err
@@ -79,26 +79,49 @@ func makeESP32FirmareImage(infile, outfile string) error {
7979
outf := &bytes.Buffer{}
8080

8181
// Image header.
82-
// Details: https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
83-
binary.Write(outf, binary.LittleEndian, struct {
84-
magic uint8
85-
segment_count uint8
86-
spi_mode uint8
87-
spi_speed_size uint8
88-
entry_addr uint32
89-
wp_pin uint8
90-
spi_pin_drv [3]uint8
91-
reserved [11]uint8
92-
hash_appended bool
93-
}{
94-
magic: 0xE9,
95-
segment_count: byte(len(segments)),
96-
spi_mode: 0, // irrelevant, replaced by esptool when flashing
97-
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
98-
entry_addr: uint32(inf.Entry),
99-
wp_pin: 0xEE, // disable WP pin
100-
hash_appended: true, // add a SHA256 hash
101-
})
82+
switch format {
83+
case "esp32":
84+
// Header format:
85+
// https://github.com/espressif/esp-idf/blob/8fbb63c2/components/bootloader_support/include/esp_image_format.h#L58
86+
binary.Write(outf, binary.LittleEndian, struct {
87+
magic uint8
88+
segment_count uint8
89+
spi_mode uint8
90+
spi_speed_size uint8
91+
entry_addr uint32
92+
wp_pin uint8
93+
spi_pin_drv [3]uint8
94+
reserved [11]uint8
95+
hash_appended bool
96+
}{
97+
magic: 0xE9,
98+
segment_count: byte(len(segments)),
99+
spi_mode: 0, // irrelevant, replaced by esptool when flashing
100+
spi_speed_size: 0, // spi_speed, spi_size: replaced by esptool when flashing
101+
entry_addr: uint32(inf.Entry),
102+
wp_pin: 0xEE, // disable WP pin
103+
hash_appended: true, // add a SHA256 hash
104+
})
105+
case "esp8266":
106+
// Header format:
107+
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format
108+
// Basically a truncated version of the ESP32 header.
109+
binary.Write(outf, binary.LittleEndian, struct {
110+
magic uint8
111+
segment_count uint8
112+
spi_mode uint8
113+
spi_speed_size uint8
114+
entry_addr uint32
115+
}{
116+
magic: 0xE9,
117+
segment_count: byte(len(segments)),
118+
spi_mode: 0, // irrelevant, replaced by esptool when flashing
119+
spi_speed_size: 0x20, // spi_speed, spi_size: replaced by esptool when flashing
120+
entry_addr: uint32(inf.Entry),
121+
})
122+
default:
123+
return fmt.Errorf("builder: unknown binary format %#v, expected esp32 or esp8266", format)
124+
}
102125

103126
// Write all segments to the image.
104127
// https://github.com/espressif/esptool/wiki/Firmware-Image-Format#segment
@@ -119,9 +142,11 @@ func makeESP32FirmareImage(infile, outfile string) error {
119142
outf.Write(make([]byte, 15-outf.Len()%16))
120143
outf.WriteByte(checksum)
121144

122-
// SHA256 hash (to protect against image corruption, not for security).
123-
hash := sha256.Sum256(outf.Bytes())
124-
outf.Write(hash[:])
145+
if format == "esp32" {
146+
// SHA256 hash (to protect against image corruption, not for security).
147+
hash := sha256.Sum256(outf.Bytes())
148+
outf.Write(hash[:])
149+
}
125150

126151
// Write the image to the output file.
127152
return ioutil.WriteFile(outfile, outf.Bytes(), 0666)

src/device/esp/esp8266.S

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
.section .text.tinygo_scanCurrentStack
3+
.global tinygo_scanCurrentStack
4+
tinygo_scanCurrentStack:
5+
// TODO: save callee saved registers on the stack
6+
j tinygo_scanstack

src/machine/board_nodemcu.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// +build nodemcu
2+
3+
// Pinout for the NodeMCU dev kit.
4+
5+
package machine
6+
7+
// GPIO pins on the NodeMCU board.
8+
const (
9+
D0 Pin = 16
10+
D1 Pin = 5
11+
D2 Pin = 4
12+
D3 Pin = 0
13+
D4 Pin = 2
14+
D5 Pin = 14
15+
D6 Pin = 12
16+
D7 Pin = 13
17+
D8 Pin = 15
18+
)
19+
20+
// Onboard blue LED (on the AI-Thinker module).
21+
const LED = D4

src/machine/machine_esp8266.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// +build esp8266
2+
3+
package machine
4+
5+
import (
6+
"device/esp"
7+
"runtime/volatile"
8+
)
9+
10+
func CPUFrequency() uint32 {
11+
return 80000000 // 80MHz
12+
}
13+
14+
type PinMode uint8
15+
16+
const (
17+
PinOutput PinMode = iota
18+
PinInput
19+
)
20+
21+
// Pins that are fixed by the chip.
22+
const (
23+
UART_TX_PIN Pin = 1
24+
UART_RX_PIN Pin = 3
25+
)
26+
27+
// Pin functions are not trivial. The below array maps a pin number (GPIO
28+
// number) to the pad as used in the IO mux.
29+
// Tables with the mapping:
30+
// https://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations#pin_functions
31+
// https://www.espressif.com/sites/default/files/documentation/ESP8266_Pin_List_0.xls
32+
var pinPadMapping = [...]uint8{
33+
12: 0,
34+
13: 1,
35+
14: 2,
36+
15: 3,
37+
3: 4,
38+
1: 5,
39+
6: 6,
40+
7: 7,
41+
8: 8,
42+
9: 9,
43+
10: 10,
44+
11: 11,
45+
0: 12,
46+
2: 13,
47+
4: 14,
48+
5: 15,
49+
}
50+
51+
// getPad returns the pad number and the register to configure this pad.
52+
func (p Pin) getPad() (uint8, *volatile.Register32) {
53+
pad := pinPadMapping[p]
54+
var reg *volatile.Register32
55+
switch pad {
56+
case 0:
57+
reg = &esp.IO_MUX.IO_MUX_MTDI
58+
case 1:
59+
reg = &esp.IO_MUX.IO_MUX_MTCK
60+
case 2:
61+
reg = &esp.IO_MUX.IO_MUX_MTMS
62+
case 3:
63+
reg = &esp.IO_MUX.IO_MUX_MTDO
64+
case 4:
65+
reg = &esp.IO_MUX.IO_MUX_U0RXD
66+
case 5:
67+
reg = &esp.IO_MUX.IO_MUX_U0TXD
68+
case 6:
69+
reg = &esp.IO_MUX.IO_MUX_SD_CLK
70+
case 7:
71+
reg = &esp.IO_MUX.IO_MUX_SD_DATA0
72+
case 8:
73+
reg = &esp.IO_MUX.IO_MUX_SD_DATA1
74+
case 9:
75+
reg = &esp.IO_MUX.IO_MUX_SD_DATA2
76+
case 10:
77+
reg = &esp.IO_MUX.IO_MUX_SD_DATA3
78+
case 11:
79+
reg = &esp.IO_MUX.IO_MUX_SD_CMD
80+
case 12:
81+
reg = &esp.IO_MUX.IO_MUX_GPIO0
82+
case 13:
83+
reg = &esp.IO_MUX.IO_MUX_GPIO2
84+
case 14:
85+
reg = &esp.IO_MUX.IO_MUX_GPIO4
86+
case 15:
87+
reg = &esp.IO_MUX.IO_MUX_GPIO5
88+
}
89+
return pad, reg
90+
}
91+
92+
// Configure sets the given pin as output or input pin.
93+
func (p Pin) Configure(config PinConfig) {
94+
switch config.Mode {
95+
case PinInput, PinOutput:
96+
pad, reg := p.getPad()
97+
if pad >= 12 { // pin 0, 2, 4, 5
98+
reg.Set(0 << 4) // function 0 at bit position 4
99+
} else {
100+
reg.Set(3 << 4) // function 3 at bit position 4
101+
}
102+
if config.Mode == PinOutput {
103+
esp.GPIO.GPIO_ENABLE_W1TS.Set(1 << p)
104+
} else {
105+
esp.GPIO.GPIO_ENABLE_W1TC.Set(1 << p)
106+
}
107+
}
108+
}
109+
110+
// Set sets the output value of this pin to high (true) or low (false).
111+
func (p Pin) Set(value bool) {
112+
if value {
113+
esp.GPIO.GPIO_OUT_W1TS.Set(1 << p)
114+
} else {
115+
esp.GPIO.GPIO_OUT_W1TC.Set(1 << p)
116+
}
117+
}
118+
119+
// Return the register and mask to enable a given GPIO pin. This can be used to
120+
// implement bit-banged drivers.
121+
//
122+
// Warning: only use this on an output pin!
123+
func (p Pin) PortMaskSet() (*uint32, uint32) {
124+
return &esp.GPIO.GPIO_OUT_W1TS.Reg, 1 << p
125+
}
126+
127+
// Return the register and mask to disable a given GPIO pin. This can be used to
128+
// implement bit-banged drivers.
129+
//
130+
// Warning: only use this on an output pin!
131+
func (p Pin) PortMaskClear() (*uint32, uint32) {
132+
return &esp.GPIO.GPIO_OUT_W1TC.Reg, 1 << p
133+
}
134+
135+
// UART0 is a hardware UART that supports both TX and RX.
136+
var UART0 = UART{Buffer: NewRingBuffer()}
137+
138+
type UART struct {
139+
Buffer *RingBuffer
140+
}
141+
142+
// Configure the UART baud rate. TX and RX pins are fixed by the hardware so
143+
// cannot be modified and will be ignored.
144+
func (uart UART) Configure(config UARTConfig) {
145+
if config.BaudRate == 0 {
146+
config.BaudRate = 115200
147+
}
148+
esp.UART0.UART_CLKDIV.Set(CPUFrequency() / config.BaudRate)
149+
}
150+
151+
// WriteByte writes a single byte to the output buffer. Note that the hardware
152+
// includes a buffer of 128 bytes which will be used first.
153+
func (uart UART) WriteByte(c byte) error {
154+
for (esp.UART0.UART_STATUS.Get()>>16)&0xff >= 128 {
155+
// Wait until the TX buffer has room.
156+
}
157+
esp.UART0.UART_FIFO.Set(uint32(c))
158+
return nil
159+
}

0 commit comments

Comments
 (0)