github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_esp32.go (about) 1 //go:build esp32 2 3 package machine 4 5 import ( 6 "device/esp" 7 "errors" 8 "runtime/volatile" 9 "unsafe" 10 ) 11 12 const deviceName = esp.Device 13 14 const peripheralClock = 80000000 // 80MHz 15 16 // CPUFrequency returns the current CPU frequency of the chip. 17 // Currently it is a fixed frequency but it may allow changing in the future. 18 func CPUFrequency() uint32 { 19 return 160e6 // 160MHz 20 } 21 22 var ( 23 ErrInvalidSPIBus = errors.New("machine: invalid SPI bus") 24 ) 25 26 const ( 27 PinOutput PinMode = iota 28 PinInput 29 PinInputPullup 30 PinInputPulldown 31 ) 32 33 // Hardware pin numbers 34 const ( 35 GPIO0 Pin = 0 36 GPIO1 Pin = 1 37 GPIO2 Pin = 2 38 GPIO3 Pin = 3 39 GPIO4 Pin = 4 40 GPIO5 Pin = 5 41 GPIO6 Pin = 6 42 GPIO7 Pin = 7 43 GPIO8 Pin = 8 44 GPIO9 Pin = 9 45 GPIO10 Pin = 10 46 GPIO11 Pin = 11 47 GPIO12 Pin = 12 48 GPIO13 Pin = 13 49 GPIO14 Pin = 14 50 GPIO15 Pin = 15 51 GPIO16 Pin = 16 52 GPIO17 Pin = 17 53 GPIO18 Pin = 18 54 GPIO19 Pin = 19 55 GPIO21 Pin = 21 56 GPIO22 Pin = 22 57 GPIO23 Pin = 23 58 GPIO25 Pin = 25 59 GPIO26 Pin = 26 60 GPIO27 Pin = 27 61 GPIO32 Pin = 32 62 GPIO33 Pin = 33 63 GPIO34 Pin = 34 64 GPIO35 Pin = 35 65 GPIO36 Pin = 36 66 GPIO37 Pin = 37 67 GPIO38 Pin = 38 68 GPIO39 Pin = 39 69 ) 70 71 // Configure this pin with the given configuration. 72 func (p Pin) Configure(config PinConfig) { 73 // Output function 256 is a special value reserved for use as a regular GPIO 74 // pin. Peripherals (SPI etc) can set a custom output function by calling 75 // lowercase configure() instead with a signal name. 76 p.configure(config, 256) 77 } 78 79 // configure is the same as Configure, but allows for setting a specific input 80 // or output signal. 81 // Signals are always routed through the GPIO matrix for simplicity. Output 82 // signals are configured in FUNCx_OUT_SEL_CFG which selects a particular signal 83 // to output on a given pin. Input signals are configured in FUNCy_IN_SEL_CFG, 84 // which sets the pin to use for a particular input signal. 85 func (p Pin) configure(config PinConfig, signal uint32) { 86 if p == NoPin { 87 // This simplifies pin configuration in peripherals such as SPI. 88 return 89 } 90 91 var muxConfig uint32 // The mux configuration. 92 93 // Configure this pin as a GPIO pin. 94 const function = 3 // function 3 is GPIO for every pin 95 muxConfig |= (function - 1) << esp.IO_MUX_GPIO0_MCU_SEL_Pos 96 97 // Make this pin an input pin (always). 98 muxConfig |= esp.IO_MUX_GPIO0_FUN_IE 99 100 // Set drive strength: 0 is lowest, 3 is highest. 101 muxConfig |= 2 << esp.IO_MUX_GPIO0_FUN_DRV_Pos 102 103 // Select pull mode. 104 if config.Mode == PinInputPullup { 105 muxConfig |= esp.IO_MUX_GPIO0_FUN_WPU 106 } else if config.Mode == PinInputPulldown { 107 muxConfig |= esp.IO_MUX_GPIO0_FUN_WPD 108 } 109 110 // Configure the pad with the given IO mux configuration. 111 p.mux().Set(muxConfig) 112 113 switch config.Mode { 114 case PinOutput: 115 // Set the 'output enable' bit. 116 if p < 32 { 117 esp.GPIO.ENABLE_W1TS.Set(1 << p) 118 } else { 119 esp.GPIO.ENABLE1_W1TS.Set(1 << (p - 32)) 120 } 121 // Set the signal to read the output value from. It can be a peripheral 122 // output signal, or the special value 256 which indicates regular GPIO 123 // usage. 124 p.outFunc().Set(signal) 125 case PinInput, PinInputPullup, PinInputPulldown: 126 // Clear the 'output enable' bit. 127 if p < 32 { 128 esp.GPIO.ENABLE_W1TC.Set(1 << p) 129 } else { 130 esp.GPIO.ENABLE1_W1TC.Set(1 << (p - 32)) 131 } 132 if signal != 256 { 133 // Signal is a peripheral function (not a simple GPIO). Connect this 134 // signal to the pin. 135 // Note that outFunc and inFunc work in the opposite direction. 136 // outFunc configures a pin to use a given output signal, while 137 // inFunc specifies a pin to use to read the signal from. 138 inFunc(signal).Set(esp.GPIO_FUNC_IN_SEL_CFG_SEL | uint32(p)<<esp.GPIO_FUNC_IN_SEL_CFG_IN_SEL_Pos) 139 } 140 } 141 } 142 143 // outFunc returns the FUNCx_OUT_SEL_CFG register used for configuring the 144 // output function selection. 145 func (p Pin) outFunc() *volatile.Register32 { 146 return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_OUT_SEL_CFG), uintptr(p)*4)) 147 } 148 149 // inFunc returns the FUNCy_IN_SEL_CFG register used for configuring the input 150 // function selection. 151 func inFunc(signal uint32) *volatile.Register32 { 152 return (*volatile.Register32)(unsafe.Add(unsafe.Pointer(&esp.GPIO.FUNC0_IN_SEL_CFG), uintptr(signal)*4)) 153 } 154 155 // Set the pin to high or low. 156 // Warning: only use this on an output pin! 157 func (p Pin) Set(value bool) { 158 if value { 159 reg, mask := p.portMaskSet() 160 reg.Set(mask) 161 } else { 162 reg, mask := p.portMaskClear() 163 reg.Set(mask) 164 } 165 } 166 167 // Return the register and mask to enable a given GPIO pin. This can be used to 168 // implement bit-banged drivers. 169 // 170 // Warning: only use this on an output pin! 171 func (p Pin) PortMaskSet() (*uint32, uint32) { 172 reg, mask := p.portMaskSet() 173 return ®.Reg, mask 174 } 175 176 // Return the register and mask to disable a given GPIO pin. This can be used to 177 // implement bit-banged drivers. 178 // 179 // Warning: only use this on an output pin! 180 func (p Pin) PortMaskClear() (*uint32, uint32) { 181 reg, mask := p.portMaskClear() 182 return ®.Reg, mask 183 } 184 185 func (p Pin) portMaskSet() (*volatile.Register32, uint32) { 186 if p < 32 { 187 return &esp.GPIO.OUT_W1TS, 1 << p 188 } else { 189 return &esp.GPIO.OUT1_W1TS, 1 << (p - 32) 190 } 191 } 192 193 func (p Pin) portMaskClear() (*volatile.Register32, uint32) { 194 if p < 32 { 195 return &esp.GPIO.OUT_W1TC, 1 << p 196 } else { 197 return &esp.GPIO.OUT1_W1TC, 1 << (p - 32) 198 } 199 } 200 201 // Get returns the current value of a GPIO pin when the pin is configured as an 202 // input or as an output. 203 func (p Pin) Get() bool { 204 if p < 32 { 205 return esp.GPIO.IN.Get()&(1<<p) != 0 206 } else { 207 return esp.GPIO.IN1.Get()&(1<<(p-32)) != 0 208 } 209 } 210 211 // mux returns the I/O mux configuration register corresponding to the given 212 // GPIO pin. 213 func (p Pin) mux() *volatile.Register32 { 214 // I have no idea whether there is any pattern in the GPIO <-> pad mapping. 215 // I couldn't find it. 216 switch p { 217 case 36: 218 return &esp.IO_MUX.GPIO36 219 case 37: 220 return &esp.IO_MUX.GPIO37 221 case 38: 222 return &esp.IO_MUX.GPIO38 223 case 39: 224 return &esp.IO_MUX.GPIO39 225 case 34: 226 return &esp.IO_MUX.GPIO34 227 case 35: 228 return &esp.IO_MUX.GPIO35 229 case 32: 230 return &esp.IO_MUX.GPIO32 231 case 33: 232 return &esp.IO_MUX.GPIO33 233 case 25: 234 return &esp.IO_MUX.GPIO25 235 case 26: 236 return &esp.IO_MUX.GPIO26 237 case 27: 238 return &esp.IO_MUX.GPIO27 239 case 14: 240 return &esp.IO_MUX.GPIO14 241 case 12: 242 return &esp.IO_MUX.GPIO12 243 case 13: 244 return &esp.IO_MUX.GPIO13 245 case 15: 246 return &esp.IO_MUX.GPIO15 247 case 2: 248 return &esp.IO_MUX.GPIO2 249 case 0: 250 return &esp.IO_MUX.GPIO0 251 case 4: 252 return &esp.IO_MUX.GPIO4 253 case 16: 254 return &esp.IO_MUX.GPIO16 255 case 17: 256 return &esp.IO_MUX.GPIO17 257 case 9: 258 return &esp.IO_MUX.GPIO9 259 case 10: 260 return &esp.IO_MUX.GPIO10 261 case 11: 262 return &esp.IO_MUX.GPIO11 263 case 6: 264 return &esp.IO_MUX.GPIO6 265 case 7: 266 return &esp.IO_MUX.GPIO7 267 case 8: 268 return &esp.IO_MUX.GPIO8 269 case 5: 270 return &esp.IO_MUX.GPIO5 271 case 18: 272 return &esp.IO_MUX.GPIO18 273 case 19: 274 return &esp.IO_MUX.GPIO19 275 case 20: 276 return &esp.IO_MUX.GPIO20 277 case 21: 278 return &esp.IO_MUX.GPIO21 279 case 22: 280 return &esp.IO_MUX.GPIO22 281 case 3: 282 return &esp.IO_MUX.GPIO3 283 case 1: 284 return &esp.IO_MUX.GPIO1 285 case 23: 286 return &esp.IO_MUX.GPIO23 287 case 24: 288 return &esp.IO_MUX.GPIO24 289 default: 290 return nil 291 } 292 } 293 294 var DefaultUART = UART0 295 296 var ( 297 UART0 = &_UART0 298 _UART0 = UART{Bus: esp.UART0, Buffer: NewRingBuffer()} 299 UART1 = &_UART1 300 _UART1 = UART{Bus: esp.UART1, Buffer: NewRingBuffer()} 301 UART2 = &_UART2 302 _UART2 = UART{Bus: esp.UART2, Buffer: NewRingBuffer()} 303 ) 304 305 type UART struct { 306 Bus *esp.UART_Type 307 Buffer *RingBuffer 308 } 309 310 func (uart *UART) Configure(config UARTConfig) { 311 if config.BaudRate == 0 { 312 config.BaudRate = 115200 313 } 314 uart.Bus.CLKDIV.Set(peripheralClock / config.BaudRate) 315 } 316 317 func (uart *UART) writeByte(b byte) error { 318 for (uart.Bus.STATUS.Get()>>16)&0xff >= 128 { 319 // Read UART_TXFIFO_CNT from the status register, which indicates how 320 // many bytes there are in the transmit buffer. Wait until there are 321 // less than 128 bytes in this buffer (the default buffer size). 322 } 323 // Write to the TX_FIFO register. 324 (*volatile.Register8)(unsafe.Add(unsafe.Pointer(uart.Bus), 0x200C0000)).Set(b) 325 return nil 326 } 327 328 func (uart *UART) flush() {} 329 330 // Serial Peripheral Interface on the ESP32. 331 type SPI struct { 332 Bus *esp.SPI_Type 333 } 334 335 var ( 336 // SPI0 and SPI1 are reserved for use by the caching system etc. 337 SPI2 = SPI{esp.SPI2} 338 SPI3 = SPI{esp.SPI3} 339 ) 340 341 // SPIConfig configures a SPI peripheral on the ESP32. Make sure to set at least 342 // SCK, SDO and SDI (possibly to NoPin if not in use). The default for LSBFirst 343 // (false) and Mode (0) are good for most applications. The frequency defaults 344 // to 1MHz if not set but can be configured up to 40MHz. Possible values are 345 // 40MHz and integer divisions from 40MHz such as 20MHz, 13.3MHz, 10MHz, 8MHz, 346 // etc. 347 type SPIConfig struct { 348 Frequency uint32 349 SCK Pin 350 SDO Pin 351 SDI Pin 352 LSBFirst bool 353 Mode uint8 354 } 355 356 // Configure and make the SPI peripheral ready to use. 357 func (spi SPI) Configure(config SPIConfig) error { 358 if config.Frequency == 0 { 359 config.Frequency = 4e6 // default to 4MHz 360 } 361 362 // Configure the SPI clock. This assumes a peripheral clock of 80MHz. 363 var clockReg uint32 364 if config.Frequency > 40e6 { 365 // Don't use a prescaler, but directly connect to the APB clock. This 366 // results in a SPI clock frequency of 40MHz. 367 clockReg |= esp.SPI_CLOCK_CLK_EQU_SYSCLK 368 } else { 369 // Use a prescaler for frequencies below 40MHz. They will get rounded 370 // down to the next possible frequency (20MHz, 13.3MHz, 10MHz, 8MHz, 371 // 6.7MHz, 5.7MHz, 5MHz, etc). 372 // This code is much simpler than how ESP-IDF configures the frequency, 373 // but should be just as accurate. The only exception is for frequencies 374 // below 4883Hz, which will need special support. 375 if config.Frequency < 4883 { 376 // The current lower limit is 4883Hz. 377 // The hardware supports lower frequencies by setting the h and n 378 // variables, but that's not yet implemented. 379 config.Frequency = 4883 380 } 381 // The prescaler value is 40e6 / config.Frequency, but rounded up so 382 // that the actual frequency is never higher than the frequency 383 // requested in config.Frequency. 384 var ( 385 pre uint32 = (40e6 + config.Frequency - 1) / config.Frequency 386 n uint32 = 2 // this value seems to equal the number of ticks per SPI clock tick 387 h uint32 = 1 // must be half of n according to the formula in the reference manual 388 l uint32 = n // must equal n according to the reference manual 389 ) 390 clockReg |= (pre - 1) << esp.SPI_CLOCK_CLKDIV_PRE_Pos 391 clockReg |= (n - 1) << esp.SPI_CLOCK_CLKCNT_N_Pos 392 clockReg |= (h - 1) << esp.SPI_CLOCK_CLKCNT_H_Pos 393 clockReg |= (l - 1) << esp.SPI_CLOCK_CLKCNT_L_Pos 394 } 395 spi.Bus.CLOCK.Set(clockReg) 396 397 // SPI_CTRL_REG controls bit order. 398 var ctrlReg uint32 399 if config.LSBFirst { 400 ctrlReg |= esp.SPI_CTRL_WR_BIT_ORDER 401 ctrlReg |= esp.SPI_CTRL_RD_BIT_ORDER 402 } 403 spi.Bus.CTRL.Set(ctrlReg) 404 405 // SPI_CTRL2_REG, SPI_USER_REG and SPI_PIN_REG control SPI clock polarity 406 // (mode), among others. 407 var ctrl2Reg, userReg, pinReg uint32 408 // For mode configuration, see table 29 in the reference manual (page 128). 409 switch config.Mode { 410 case 0: 411 case 1: 412 userReg |= esp.SPI_USER_CK_OUT_EDGE 413 case 2: 414 userReg |= esp.SPI_USER_CK_OUT_EDGE 415 pinReg |= esp.SPI_PIN_CK_IDLE_EDGE 416 case 3: 417 pinReg |= esp.SPI_PIN_CK_IDLE_EDGE 418 } 419 // Enable full-duplex communication. 420 userReg |= esp.SPI_USER_DOUTDIN 421 userReg |= esp.SPI_USER_USR_MOSI 422 // Write values to registers. 423 spi.Bus.CTRL2.Set(ctrl2Reg) 424 spi.Bus.USER.Set(userReg) 425 spi.Bus.PIN.Set(pinReg) 426 427 // Configure pins. 428 // TODO: use direct output if possible, if the configured pins match the 429 // possible direct configurations (e.g. for SPI2, when SCK is pin 14 etc). 430 if spi.Bus == esp.SPI2 { 431 config.SCK.configure(PinConfig{Mode: PinOutput}, 8) // HSPICLK 432 config.SDI.configure(PinConfig{Mode: PinInput}, 9) // HSPIQ 433 config.SDO.configure(PinConfig{Mode: PinOutput}, 10) // HSPID 434 } else if spi.Bus == esp.SPI3 { 435 config.SCK.configure(PinConfig{Mode: PinOutput}, 63) // VSPICLK 436 config.SDI.configure(PinConfig{Mode: PinInput}, 64) // VSPIQ 437 config.SDO.configure(PinConfig{Mode: PinOutput}, 65) // VSPID 438 } else { 439 // Don't know how to configure this bus. 440 return ErrInvalidSPIBus 441 } 442 443 return nil 444 } 445 446 // Transfer writes/reads a single byte using the SPI interface. If you need to 447 // transfer larger amounts of data, Tx will be faster. 448 func (spi SPI) Transfer(w byte) (byte, error) { 449 spi.Bus.MISO_DLEN.Set(7 << esp.SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos) 450 spi.Bus.MOSI_DLEN.Set(7 << esp.SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos) 451 452 spi.Bus.W0.Set(uint32(w)) 453 454 // Send/receive byte. 455 spi.Bus.CMD.Set(esp.SPI_CMD_USR) 456 for spi.Bus.CMD.Get() != 0 { 457 } 458 459 // The received byte is stored in W0. 460 return byte(spi.Bus.W0.Get()), nil 461 } 462 463 // Tx handles read/write operation for SPI interface. Since SPI is a syncronous write/read 464 // interface, there must always be the same number of bytes written as bytes read. 465 // This is accomplished by sending zero bits if r is bigger than w or discarding 466 // the incoming data if w is bigger than r. 467 func (spi SPI) Tx(w, r []byte) error { 468 toTransfer := len(w) 469 if len(r) > toTransfer { 470 toTransfer = len(r) 471 } 472 473 for toTransfer != 0 { 474 // Do only 64 bytes at a time. 475 chunkSize := toTransfer 476 if chunkSize > 64 { 477 chunkSize = 64 478 } 479 480 // Fill tx buffer. 481 transferWords := (*[16]volatile.Register32)(unsafe.Pointer(uintptr(unsafe.Pointer(&spi.Bus.W0)))) 482 if len(w) >= 64 { 483 // We can fill the entire 64-byte transfer buffer with data. 484 // This loop is slightly faster than the loop below. 485 for i := 0; i < 16; i++ { 486 word := uint32(w[i*4])<<0 | uint32(w[i*4+1])<<8 | uint32(w[i*4+2])<<16 | uint32(w[i*4+3])<<24 487 transferWords[i].Set(word) 488 } 489 } else { 490 // We can't fill the entire transfer buffer, so we need to be a bit 491 // more careful. 492 // Note that parts of the transfer buffer that aren't used still 493 // need to be set to zero, otherwise we might be transferring 494 // garbage from a previous transmission if w is smaller than r. 495 for i := 0; i < 16; i++ { 496 var word uint32 497 if i*4+3 < len(w) { 498 word |= uint32(w[i*4+3]) << 24 499 } 500 if i*4+2 < len(w) { 501 word |= uint32(w[i*4+2]) << 16 502 } 503 if i*4+1 < len(w) { 504 word |= uint32(w[i*4+1]) << 8 505 } 506 if i*4+0 < len(w) { 507 word |= uint32(w[i*4+0]) << 0 508 } 509 transferWords[i].Set(word) 510 } 511 } 512 513 // Do the transfer. 514 spi.Bus.MISO_DLEN.Set((uint32(chunkSize)*8 - 1) << esp.SPI_MISO_DLEN_USR_MISO_DBITLEN_Pos) 515 spi.Bus.MOSI_DLEN.Set((uint32(chunkSize)*8 - 1) << esp.SPI_MOSI_DLEN_USR_MOSI_DBITLEN_Pos) 516 spi.Bus.CMD.Set(esp.SPI_CMD_USR) 517 for spi.Bus.CMD.Get() != 0 { 518 } 519 520 // Read rx buffer. 521 rxSize := 64 522 if rxSize > len(r) { 523 rxSize = len(r) 524 } 525 for i := 0; i < rxSize; i++ { 526 r[i] = byte(transferWords[i/4].Get() >> ((i % 4) * 8)) 527 } 528 529 // Cut off some part of the output buffer so the next iteration we will 530 // only send the remaining bytes. 531 if len(w) < chunkSize { 532 w = nil 533 } else { 534 w = w[chunkSize:] 535 } 536 if len(r) < chunkSize { 537 r = nil 538 } else { 539 r = r[chunkSize:] 540 } 541 toTransfer -= chunkSize 542 } 543 544 return nil 545 }