Skip to content

Architecture of GPIO

Gordon Williams edited this page Feb 23, 2017 · 3 revisions

THIS WIKI IS OUT OF DATE. check out the README.md on GitHub for docs on Espruino internals, or see www.espruino.com for searchable, up to date docs on using Espruino.

###Pin State

We should realize that a pin is a physical plug or socket that can be electrically wired. Now we must consider the concept of the state of a pin. Loosely, we might consider this as whether the pin is input (we will read a signal) or output (we will write a signal) ... however it is not as black and white as that. See the following list:

  • analog - Analog input
  • input - Digital input
  • input_pullup - Digital input with internal ~40k pull-up resistor
  • input_pulldown - Digital input with internal ~40k pull-down resistor
  • output - Digital output
  • opendrain - Digital output that only ever pulls down to 0v. Sending a logical 1 leaves the pin open circuit
  • af_output - Digital output from built-in peripheral
  • af_opendrain - Digital output from built-in peripheral that only ever pulls down to 0v.

There are instances where the state of a pin is automatically defined. For example, if we perform a JS digitalWrite then the state of the pin is set to output automatically. However, the programmer can also manually define the state of a pin by calling pinMode to set an explicit state. Espruino tracks whether the current state of a pin has been defined automatically or manually.

  • jshSetPinStateIsManual - Set the pin state of a pin
  • jshGetPinStateIsManual - Get the pin state of a pin

###Interrupt Handling

From JavaScript we can execute a setWatch function which takes as input a callback function and the identity of a GPIO pin. When the signal level on the pin changes, the callback function is invoked. Here we supply a primer on how that works.

Within the Espruino code base, there is an implementation of a global static C function that implements setWatch. That function is called jswrap_interface_setWatch. When invoked, it calls jshPinWatch(). That function is a board specific implementation. Its purpose is to request that the board hardware register an interrupt handler to be called when the signal level on the pin changes. The implementation of that board specific function is, obviously, up to the board ... however, the logic should be that when an interrupt on the pin is detected, that a call be made to jshPushIOWatchEvent() telling it that a pin that is being watched has changed state.

The implementation of jshPushIOWatchEvent creates an IO Event and adds it to the IO Event queue by calling jshPushIOEvent. Having added the event to the queue, the interrupt handler will have completed its work.

Within the jshIdle processing loop, an examination is made of IO events in the IO event queue. If there are events waiting to be processed, they are handled there. When a watch event is detected, the function that was originally defined by the setWatch call is invoked.

There are a myriad of additional details in play but at a high level, this is the core set of concepts.


###Pulse Width Modulation, PWM and Analog Output

Pulse Width Modulation (PWM) also known as "Analog Output" is the notion that we can create a PWM signal from an Espruino environment. The JavaScript wrapper for this is contained in jswrap_io.c which parses JavaScript level data and then invokes a board specific handler. That board specific handler is called jshPinAnalogOutput and implemented in a board specific manner. The signature of the implementation is:

JshPinFunction jshPinAnalogOutput(
   Pin pin,
   JsVarFloat value,
   JsVarFloat freq,
   JshAnalogOutputFlags flags); 

The parameters need some explanation.

  • pin - The pin to be used for PWM output.
  • value - The value to output between 0.0 and 1.0.
  • freq - The period/frequency of the width of the pulse. If the value of freq is <= 0, then the default frequency is used.
  • flags - One of the following:
    • JSAOF_NONE - No special flags.
    • JSAOF_ALLOW_SOFTWARE - May use software PWM.
    • JSAOF_FORCE_SOFTWARE - Must use software PWM.

###Previous

GPIO is the ability to drive IO through commands. The functions that relate to GPIO are:

  • digitalRead
  • digitalWrite
  • digitalPulse
  • getPinMode
  • pinMode

From an Espruino implementation perspective, the contract is implemented in the ESP8266 specific jshardware.c source file. We are responsible for implementing the following functions

void jshPinSetState(Pin pin, JshPinState state)
JshPinState jshPinGetSate(Pin pin)
void jshPinSetValue(Pin pin, bool value)
bool jshPinGetValue(Pin pin)
JsVarFloar jshPinAnalog(Pin pin)
int jshPinAnalogFast(Pin pin)
JshPinFunction jshPinAnalogOutput(Pin pin,
   JsVarFloat value,
   JsVarFloat freq,
   JshAnalogOutputFlags flags)
void jshPinPulse(Pin pin, bool value, JsVarFloat time)
bool jshCanWatch(Pin pin)
IOEventFlags jshPinWatch(Pin pin, bool shouldWatch)
JshPinFunction jshGetCurrentPinFunction(Pin pin)
bool jshIsEventForPin(IOEvent *event, Pin pin)
IOEventFlags pinToEVEXTI(Pin pin)

The Pin data type is simply an unsigned char.

There is also a Pin Class. This has methods for:

  • constructor
  • getMode
  • mode
  • read
  • reset - Set the pin to a 0
  • set - Set the pin to a 1
  • write
  • writeAtTime

For example, to create a definition for a Pin we might code:

var myGPIO0 = D0;

To write a value we might code:

myGPIO0.write(true);

or

digitalWrite(myGPIO0, true);

In the build environment in the boards folder, there is a Python script for each of the board types. Within this script we define a function which describes the pins available for each board. For the ESP8266, we have a script called ESP8266_BOARD.py which defines the available pins.

#####devices

This is a list of built-in stuff on the board that is made accessible to Espruino. It's parsed and turned into defines in gen/platform_config.h.

Stuff you can use is LED1-8, BTN1-4, USB, LCD (for boards with FSMC LCDs built in), SD (SD card), JTAG (when JTAG pins are defined, but we need to make sure we leave them alone when the board resets).

A helper script is supplied by the Espruino build system called pinutils.py. It contains utility functions:


######generate_pins Return a set of pins from min_pin to max_pin inclusive.

generate_pins(min_pin, max_pin)

This is achieved by calling findpin for each of the pins in the range with a name of PD<Num>.

The format returned is an array of Pin objects where each of these contains:

  • name is the pin name - due to random historical reasons (from ST datasheets) it needs prefixing with P.
  • sortingname is the name, but padded so that when it's sorted everything appears in the right order port is the actual port - on ESP8266 this might not be needed and could just default to D.
  • num is the pin number - this doesn't have to match D - it's what is needed internally to access the hardware. For instance Olimexino has 'logical' pins that actually map all over the place.
  • function is a map of pin functions to their 'alternate functions' (an STM32 chip thing - STM32F4 chips can have different peripherals on each pin, so the alternate function is a number that you shove in that pin's register in order to connect it to that peripheral). The format, for instance I2C1_SDA is important as it's parsed later and is used to build gen/jspininfo.c.
  • csv isn't needed afaik, but when using data grabbed from csv files from ST's datasheets like this it contains the raw data for debugging)

######findpin Find and add a new pin to the list of pins. Returns the new pin.

findpin(pins, pinname, force)

Find the pin definition with the name pinname and add it to the list of pins supplied by pins. If force is true and the named pin is not known, then an error is generated otherwise a new default pin is created.

Clone this wiki locally