Espruino/targets/esp32/jshardware.c
jumjum123 1973a776a9 Added reset function to jshreset for
- SPI
- ADC
- UART
- PULSE
2017-03-03 11:59:35 +01:00

686 lines
18 KiB
C

/*
* This file is part of Espruino, a JavaScript interpreter for Microcontrollers
*
* Copyright (C) 2015 Gordon Williams <gw@pur3.co.uk>
*
* This Source Code Form is subject to the terms of the Mozilla Publici
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* ----------------------------------------------------------------------------
* This file is designed to be parsed during the build process
*
* Contains ESP32 board specific functions.
* ----------------------------------------------------------------------------
*/
/**
* The ESP32 must implement its part of the Espruino contract. This file
* provides implementations for interfaces that are expected to be provided
* by an Espruino board. The signatures of the exposed functions are part
* of the Espruino environment and can not be changed without express
* approval from all the stakeholders. In addition, the semantics of the
* functions should follow the expected conventions.
*/
#include <stdio.h>
#include "jshardware.h"
#include "jshardwareUart.h"
#include "jshardwareAnalog.h"
#include "jshardwareTimer.h"
#include "jshardwarePWM.h"
#include "jshardwarePulse.h"
#include "jsutils.h"
#include "jstimer.h"
#include "jsparse.h"
#include "jsinteractive.h"
#include "jspininfo.h"
#include "jswrap_esp32_network.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
#include "driver/gpio.h"
#include "jshardwareI2c.h"
#include "jshardwareSpi.h"
#define FLASH_MAX (4*1024*1024) //4MB
#define FLASH_PAGE_SHIFT 12 // Shift is much faster than division by 4096 (size of page)
#define FLASH_PAGE ((uint32_t)1<<FLASH_PAGE_SHIFT) //4KB
#define UNUSED(x) (void)(x)
/**
* Convert a pin id to the corresponding Pin Event id.
*/
static IOEventFlags pinToEV_EXTI(
Pin pin // !< The pin to map to the event id.
) {
// Map pin 0 to EV_EXTI0
// Map pin 1 to EV_EXTI1
// ...
// Map pin x to ECEXTIx
return (IOEventFlags)(EV_EXTI0 + pin);
}
static uint8_t g_pinState[JSH_PIN_COUNT];
/**
* interrupt handler for gpio interrupts
*/
void IRAM_ATTR gpio_intr_handler(void* arg){
//GPIO intr process. Mainly copied from esp-idf
UNUSED(arg);
IOEventFlags exti;
Pin gpio_num = 0;
uint32_t gpio_intr_status = READ_PERI_REG(GPIO_STATUS_REG); //read status to get interrupt status for GPIO0-31
uint32_t gpio_intr_status_h = READ_PERI_REG(GPIO_STATUS1_REG);//read status1 to get interrupt status for GPIO32-39
SET_PERI_REG_MASK(GPIO_STATUS_W1TC_REG, gpio_intr_status); //Clear intr for gpio0-gpio31
SET_PERI_REG_MASK(GPIO_STATUS1_W1TC_REG, gpio_intr_status_h); //Clear intr for gpio32-39
do {
g_pinState[gpio_num] = 0;
if(gpio_num < 32) {
if(gpio_intr_status & BIT(gpio_num)) { //gpio0-gpio31
exti = pinToEV_EXTI(gpio_num);
jshPushIOWatchEvent(exti);
}
} else {
if(gpio_intr_status_h & BIT(gpio_num - 32)) {
exti = pinToEV_EXTI(gpio_num);
jshPushIOWatchEvent(exti);
}
}
} while(++gpio_num < GPIO_PIN_COUNT);
}
void jshPinSetStateRange( Pin start, Pin end, JshPinState state ) {
for ( Pin p=start; p<=end; p++ ) {
jshPinSetState(p, state);
}
}
void jshPinDefaultPullup() {
// 6-11 are used by Flash chip
// 32-33 are routed to rtc for xtal
jshPinSetStateRange(0,0,JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetStateRange(12,19,JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetStateRange(21,22,JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetStateRange(25,27,JSHPINSTATE_GPIO_IN_PULLUP);
jshPinSetStateRange(34,39,JSHPINSTATE_GPIO_IN_PULLUP);
}
/**
* Initialize the JavaScript hardware interface.
*/
void jshInit() {
uint32_t freeHeapSize = esp_get_free_heap_size();
jsWarn( "Free heap size: %d", freeHeapSize);
esp32_wifi_init();
//jswrap_ESP32_wifi_soft_init();
jshInitDevices();
if (JSHPINSTATE_I2C != 13 || JSHPINSTATE_GPIO_IN_PULLDOWN != 6 || JSHPINSTATE_MASK != 15) {
jsError("JshPinState #defines have changed, please update pinStateToString()");
}
/*
jsWarn( "JSHPINSTATE_I2C %d\n",JSHPINSTATE_I2C );
jsWarn( "JSHPINSTATE_GPIO_IN_PULLDOWN %d\n",JSHPINSTATE_GPIO_IN_PULLDOWN );
jsWarn( "JSHPINSTATE_MASK %d\n",JSHPINSTATE_MASK );
*/
gpio_isr_register(gpio_intr_handler,NULL,0,NULL); //changed to automatic assign of interrupt
// Initialize something for each of the possible pins.
jshPinDefaultPullup();
} // End of jshInit
/**
* Reset the Espruino environment.
*/
void jshReset() {
jshResetDevices();
jshPinDefaultPullup() ;
UartReset();
RMTReset();
ADCReset();
SPIReset();
jsWarn("jshReset(): To implement - reset of i2c\n");
}
/**
* Re-init the ESP32 after a soft-reset
*/
void jshSoftInit() {
//jsWarn(">> jshSoftInit()\n");
jswrap_ESP32_wifi_soft_init();
}
/**
* Handle whatever needs to be done in the idle loop when there's nothing to do.
*
* Nothing is needed on the ESP32.
*/
void jshIdle() {
}
// ESP32 chips don't have a serial number but they do have a MAC address
int jshGetSerialNumber(unsigned char *data, int maxChars) {
assert(maxChars >= 6); // it's 32
esp_wifi_get_mac(WIFI_IF_STA, data);
return 6;
}
//===== Interrupts and sleeping
//Mux to protect the JshInterrupt status
//static portMUX_TYPE xJshInterrupt = portMUX_INITIALIZER_UNLOCKED;
void jshInterruptOff() {
//xTaskResumeAll();
//taskEXIT_CRITICAL(&xJshInterrupt);
taskDISABLE_INTERRUPTS();
}
void jshInterruptOn() {
//taskENTER_CRITICAL(&xJshInterrupt);
taskENABLE_INTERRUPTS();
}
/// Enter simple sleep mode (can be woken up by interrupts). Returns true on success
bool jshSleep(JsSysTime timeUntilWake) {
UNUSED(timeUntilWake);
return true;
} // End of jshSleep
/**
* Delay (blocking) for the supplied number of microseconds.
*/
void jshDelayMicroseconds(int microsec) {
ets_delay_us(microsec);
} // End of jshDelayMicroseconds
/**
* Set the state of the specific pin.
*
* The possible states are:
*
* JSHPINSTATE_UNDEFINED
* JSHPINSTATE_GPIO_OUT
* JSHPINSTATE_GPIO_OUT_OPENDRAIN
* JSHPINSTATE_GPIO_OUT_OPENDRAIN_PULLUP
* JSHPINSTATE_GPIO_IN
* JSHPINSTATE_GPIO_IN_PULLUP
* JSHPINSTATE_GPIO_IN_PULLDOWN
* JSHPINSTATE_ADC_IN
* JSHPINSTATE_AF_OUT
* JSHPINSTATE_AF_OUT_OPENDRAIN
* JSHPINSTATE_USART_IN
* JSHPINSTATE_USART_OUT
* JSHPINSTATE_DAC_OUT
* JSHPINSTATE_I2C
*
* This function is exposed indirectly through the exposed global function called
* `pinMode()`. For example, `pinMode(pin, "input")` will set the given pin to input.
*/
void jshPinSetState(
Pin pin, //!< The pin to have its state changed.
JshPinState state //!< The new desired state of the pin.
) {
gpio_mode_t mode;
gpio_pull_mode_t pull_mode=GPIO_FLOATING;
switch(state) {
case JSHPINSTATE_GPIO_OUT:
mode = GPIO_MODE_INPUT_OUTPUT;
break;
case JSHPINSTATE_GPIO_IN:
mode = GPIO_MODE_INPUT;
break;
case JSHPINSTATE_GPIO_IN_PULLUP:
mode = GPIO_MODE_INPUT;
pull_mode=GPIO_PULLUP_ONLY;
break;
case JSHPINSTATE_GPIO_IN_PULLDOWN:
mode = GPIO_MODE_INPUT;
pull_mode=GPIO_PULLDOWN_ONLY;
break;
case JSHPINSTATE_GPIO_OUT_OPENDRAIN:
mode = GPIO_MODE_INPUT_OUTPUT_OD;
break;
case JSHPINSTATE_GPIO_OUT_OPENDRAIN_PULLUP:
mode = GPIO_MODE_INPUT_OUTPUT_OD;
pull_mode=GPIO_PULLUP_ONLY;
break;
default:
jsError( "jshPinSetState: Unexpected state: %d", state);
return;
}
gpio_num_t gpioNum = pinToESP32Pin(pin);
gpio_set_direction(gpioNum, mode);
gpio_set_pull_mode(gpioNum, pull_mode);
gpio_pad_select_gpio(gpioNum);
g_pinState[pin] = state; // remember what we set this to...
}
/**
* Return the current state of the selected pin.
* \return The current state of the selected pin.
*/
JshPinState jshPinGetState(Pin pin) {
if ( jshPinGetValue(pin) & 1 )
return g_pinState[pin] | JSHPINSTATE_PIN_IS_ON;
return g_pinState[pin];
}
//===== GPIO and PIN stuff =====
/**
* Set the value of the corresponding pin.
*/
void jshPinSetValue(
Pin pin, //!< The pin to have its value changed.
bool value //!< The new value of the pin.
) {
gpio_num_t gpioNum = pinToESP32Pin(pin);
gpio_set_level(gpioNum, (uint32_t)value);
}
/**
* Get the value of the corresponding pin.
* \return The current value of the pin.
*/
bool CALLED_FROM_INTERRUPT jshPinGetValue( // can be called at interrupt time
Pin pin //!< The pin to have its value read.
) {
gpio_num_t gpioNum = pinToESP32Pin(pin);
bool level = gpio_get_level(gpioNum);
return level;
}
JsVarFloat jshPinAnalog(Pin pin) {
return (JsVarFloat) readADC(pin) / 4096;
}
int jshPinAnalogFast(Pin pin) {
return readADC(pin) << 4;
}
/**
* Set the output PWM value.
*/
JshPinFunction jshPinAnalogOutput(Pin pin,
JsVarFloat value,
JsVarFloat freq,
JshAnalogOutputFlags flags) { // if freq<=0, the default is used
UNUSED(flags);
if(pin == 25 || pin == 26){
value = (value * 256);
uint8_t val8 = value;
writeDAC(pin,val8);
}
else{
value = (value * PWMTimerRange);
uint16_t val16 = value;
writePWM(pin,val16,(int) freq);
}
return 0;
}
/**
*
*/
void jshSetOutputValue(JshPinFunction func, int value) {
int pin;
if (JSH_PINFUNCTION_IS_DAC(func)) {
uint8_t val = (uint8_t)(value >> 8);
switch (func & JSH_MASK_INFO) {
case JSH_DAC_CH1: writeDAC(25,val); break;
case JSH_DAC_CH2: writeDAC(26,val); break;
}
}
else{
pin = ((func >> JSH_SHIFT_INFO) << 4) + ((func >> JSH_SHIFT_TYPE) & 15);
value >> (16 - PWMTimerBit);
setPWM(pin,value);
}
}
/**
*
*/
void jshEnableWatchDog(JsVarFloat timeout) {
UNUSED(timeout);
jsError(">> jshEnableWatchDog Not implemented,using taskwatchdog from RTOS");
}
// Kick the watchdog
void jshKickWatchDog() {
jsError(">> jshKickWatchDog Not implemented,using taskwatchdog from RTOS");
}
/**
* Get the state of the pin associated with the event flag.
*/
bool CALLED_FROM_INTERRUPT jshGetWatchedPinState(IOEventFlags eventFlag) { // can be called at interrupt time
gpio_num_t gpioNum = pinToESP32Pin((Pin)(eventFlag-EV_EXTI0));
bool level = gpio_get_level(gpioNum);
return level;
}
/**
* Set the value of the pin to be the value supplied and then wait for
* a given period and set the pin value again to be the opposite.
*/
void jshPinPulse(
Pin pin, //!< The pin to be pulsed.
bool pulsePolarity, //!< The value to be pulsed into the pin.
JsVarFloat pulseTime //!< The duration in milliseconds to hold the pin.
) {
int duration = (int)pulseTime * 1000; //from millisecs to microsecs
sendPulse(pin, pulsePolarity, duration);
}
/**
* Determine whether the pin can be watchable.
* \return Returns true if the pin is wathchable.
*/
bool jshCanWatch(
Pin pin //!< The pin that we are asking whether or not we can watch it.
) {
UNUSED(pin);
return true; //lets assume all pins will do
}
/**
* Do what ever is necessary to watch a pin.
* \return The event flag for this pin.
*/
IOEventFlags jshPinWatch(
Pin pin, //!< The pin to be watched.
bool shouldWatch //!< True for watching and false for unwatching.
) {
gpio_num_t gpioNum = pinToESP32Pin(pin);
if(shouldWatch){
gpio_set_intr_type(gpioNum,GPIO_INTR_ANYEDGE); //set posedge interrupt
gpio_set_direction(gpioNum,GPIO_MODE_INPUT); //set as input
gpio_set_pull_mode(gpioNum,GPIO_PULLUP_ONLY); //enable pull-up mode
gpio_intr_enable(gpioNum); //enable interrupt
}
else{
if(gpio_intr_disable(gpioNum) == ESP_ERR_INVALID_ARG){ //disable interrupt
jsError("*** jshPinWatch error");
}
}
return pin;
}
/**
*
*/
JshPinFunction jshGetCurrentPinFunction(Pin pin) {
if (jshIsPinValid(pin)) {
int i;
for (i=0;i<JSH_PININFO_FUNCTIONS;i++) {
JshPinFunction func = pinInfo[pin].functions[i];
if (JSH_PINFUNCTION_IS_TIMER(func) ||
JSH_PINFUNCTION_IS_DAC(func))
return func;
}
}
return JSH_NOTHING;
}
/**
* Determine if a given event is associated with a given pin.
* \return True if the event is associated with the pin and false otherwise.
*/
bool jshIsEventForPin(
IOEvent *event, //!< The event that has been detected.
Pin pin //!< The identity of a pin.
) {
return IOEVENTFLAGS_GETTYPE(event->flags) == pinToEV_EXTI(pin);
}
//===== USART and Serial =====
void jshUSARTSetup(IOEventFlags device, JshUSARTInfo *inf) {
initSerial(device,inf);
}
bool jshIsUSBSERIALConnected() {
return false; // "On non-USB boards this just returns false"
}
/**
* Kick a device into action (if required).
*
*/
void jshUSARTKick(
IOEventFlags device //!< The device to be kicked.
) {
int c = jshGetCharToTransmit(device);
while(c >= 0) {
if(device == EV_SERIAL1) uart_tx_one_char((uint8_t)c);
else writeSerial(device,(uint8_t)c);
c = jshGetCharToTransmit(device);
}
}
//===== System time stuff =====
/**
* Given a time in milliseconds as float, get us the value in microsecond
*/
JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms) {
return (JsSysTime) (ms * 1000.0);
}
/**
* Given a time in microseconds, get us the value in milliseconds (float)
*/
JsVarFloat jshGetMillisecondsFromTime(JsSysTime time) {
return (JsVarFloat) time / 1000.0;
}
/**
* Return the current time in microseconds.
*/
JsSysTime CALLED_FROM_INTERRUPT jshGetSystemTime() { // in us -- can be called at interrupt time
struct timeval tm;
gettimeofday(&tm, 0);
return (JsSysTime)(tm.tv_sec)*1000000L + tm.tv_usec;
}
/**
* Set the current time in microseconds.
*/
void jshSetSystemTime(JsSysTime newTime) {
struct timeval tm;
struct timezone tz;
tm.tv_sec=(time_t)(newTime/1000000L);
tm.tv_usec=0;
tz.tz_minuteswest=0;
tz.tz_dsttime=0;
settimeofday(&tm, &tz);
}
void jshUtilTimerDisable() {
disableTimer(0);
}
void jshUtilTimerStart(JsSysTime period) {
startTimer(0,(uint64_t) period);
}
void jshUtilTimerReschedule(JsSysTime period) {
rescheduleTimer(0,(uint64_t) period);
}
//===== Miscellaneous =====
static uint64_t DEVICE_INITIALISED_FLAGS = 0L;
bool jshIsDeviceInitialised(IOEventFlags device) {
uint64_t mask = 1ULL << (int)device;
return (DEVICE_INITIALISED_FLAGS & mask) != 0L;
// UNUSED(device);
// jsError(">> jshIsDeviceInitialised not implemented");
// return 0;
} // End of jshIsDeviceInitialised
void jshSetDeviceInitialised(IOEventFlags device, bool isInit) {
uint64_t mask = 1ULL << (int)device;
if (isInit) {
DEVICE_INITIALISED_FLAGS |= mask;
} else {
DEVICE_INITIALISED_FLAGS &= ~mask;
}
}
// the esp32 temperature sensor - undocumented library function call. Unsure of values returned.
JsVarFloat jshReadTemperature() {
extern uint8_t temprature_sens_read();
return temprature_sens_read();
}
// the esp8266 can read the VRef but then there's no analog input, so we don't support this
JsVarFloat jshReadVRef() {
jsError(">> jshReadVRef Not implemented");
return NAN;
}
unsigned int jshGetRandomNumber() {
return (unsigned int)rand();
}
//===== Read-write flash =====
/**
* Determine available flash depending on EEprom size
*
*/
uint32_t jshFlashMax() {
return (FLASH_MAX-1);
}
/**
* Read data from flash memory into the buffer.
*
* This reads from flash using memory-mapped reads. Only works for the first 1MB and
* requires 4-byte aligned reads.
*
*/
void jshFlashRead(
void *buf, //!< buffer to read into
uint32_t addr, //!< Flash address to read from
uint32_t len //!< Length of data to read
) {
if(len == 1){ // Can't read a single byte using the API, so read 4 and select the byte requested
uint word;
spi_flash_read(addr & 0xfffffffc,&word,4);
*(uint8_t *)buf = (word >> ((addr & 3) << 3 )) & 255;
}
else spi_flash_read(addr, buf, len);
}
/**
* Write data to flash memory from the buffer.
*
* This is called from jswrap_flash_write and ... which guarantee that addr is 4-byte aligned
* and len is a multiple of 4.
*/
void jshFlashWrite(
void *buf, //!< Buffer to write from
uint32_t addr, //!< Flash address to write into
uint32_t len //!< Length of data to write
) {
spi_flash_write(addr, buf, len);
}
/**
* Return start address and size of the flash page the given address resides in.
* Returns false if no page.
*/
bool jshFlashGetPage(
uint32_t addr, //!<
uint32_t *startAddr, //!<
uint32_t *pageSize //!<
) {
if (addr >= FLASH_MAX) return false;
*startAddr = addr & ~(FLASH_PAGE-1);
*pageSize = FLASH_PAGE;
return true;
}
void addFlashArea(JsVar *jsFreeFlash, uint32_t addr, uint32_t length) {
JsVar *jsArea = jsvNewObject();
if (!jsArea) return;
jsvObjectSetChildAndUnLock(jsArea, "addr", jsvNewFromInteger((JsVarInt)addr));
jsvObjectSetChildAndUnLock(jsArea, "length", jsvNewFromInteger((JsVarInt)length));
jsvArrayPushAndUnLock(jsFreeFlash, jsArea);
}
JsVar *jshFlashGetFree() {
JsVar *jsFreeFlash = jsvNewEmptyArray();
if (!jsFreeFlash) return 0;
// Space should be reserved here in the parition table - assume 4Mb EEPROM
// Set just after programme save area
addFlashArea(jsFreeFlash, 0x100000 + FLASH_PAGE * 16, 0x300000-FLASH_PAGE * 16-1);
return jsFreeFlash;
}
/**
* Erase the flash page containing the address.
*/
void jshFlashErasePage(
uint32_t addr //!<
) {
spi_flash_erase_sector(addr >> FLASH_PAGE_SHIFT);
}
unsigned int jshSetSystemClock(JsVar *options) {
UNUSED(options);
jsError(">> jshSetSystemClock Not implemented");
return 0;
}
/**
* Convert an Espruino pin id to a native ESP32 pin id.
*/
gpio_num_t pinToESP32Pin(Pin pin) {
if ( pin < 40 )
return pin + GPIO_NUM_0;
jsError( "pinToESP32Pin: Unknown pin: %d", pin);
return -1;
}