(NOTE: view this file with a monospaced font) eLua platform interface ================================================================================ This document describes the "platform" interface in eLua. Its purpose is to ease the task of porting eLua to a new platform, as well as having a uniform layer for accesing peripherals (such as PIO, UART, SPI ...) on all platforms. The definitions of the functions shown here are in the "inc/platform.h" file. Some of the functions are required, others are optional; see also the "adding a new platform.txt" and "platform modules.txt" files for additional information. Also, for each function or function group, the name of the module(s) that use it (if any) is specified. If other part of the code uses the module, a "ALSO USED BY" line will be present in the module description. ================================================================================ int platform_init(); TYPE: REQUIRED USED BY MODULE: N/A PURPOSE: platform-specific initialization (this is a good place to initialize the platform CPU, as well as the CPU peripherals, like the UART). RETURNS: PLATFORM_OK or PLATFORM_ERR (if PLATFORM_ERR is returned the program blocks in an infinite loop). ================================================================================ void* platform_get_first_free_ram( unsigned id ); void* platform_get_last_free_ram( unsigned id ); TYPE: REQUIRED USED BY MODULE: N/A PURPOSE: returns the first and the last free RAM address; the space between them will be used for the system heap. 'id' is a memory space identifier. This can be used if there is more than one RAM memory available in the system, and their address ranges do not overlap. For example, one can have a CPU with internal RAM (a very common case) but also an external RAM chip. In this case there are two memory spaces, the first one being the internal RAM and the other one the external RAM. While each of them in part is contiguous, they are generally not contigous to each other in the system address space, so they must be treated as two separate address spaces. If the multiple allocator (see building.txt) is used you can define as many memory spaces as you wish in your system, the allocator will make sure to use all of them. If the system RAM exists in a single memory space (for example the internal RAM on the MCU) the CPU's stack pointer should be set at the end of the RAM at startup. Thus, the first free ram will start right after the data/bss sections, and the last free RAM is the last physical address of RAM minus the size of the stack. The heap and the stack will grow on opposite directions (upward/downward) and the heap will stop if asked to grow "over" the stack. If the MCU has both internal RAM and external RAM, a simple arrangement is to place the CPU stack at the end of the internal RAM and the heap in the external memory (which is generally much larger than the MCU's internal memory). Another arrangement is to use the multiple allocator and the memory space id as described above. ================================================================================ int platform_pio_has_port( unsigned port ); const char* platform_pio_get_prefix( unsigned port ); int platform_pio_has_pin( unsigned port, unsigned pin ); pio_type platform_pio_op( unsigned port, pio_type pinmask, int op ); TYPE: OPTIONAL USED BY MODULE: pio PURPOSE: PIO operations. eLua defines a number of "virtual ports", each one 32 bits in size, as shows in "inc/platform.h". But since it somehow needs to map these virtual ports to physical ports, it will ask the platform if a port is physically present (via platform_pio_has_port) and also if a bit (a "pin") in the port is physically present (via platform_pio_has_pin). platform_pio_get_prefix gets a port number and return the "port name" as defined in the device datasheet. Some devices use PA0, PA1, others simply P0, P1. This is what this function is expected to return. The platform_pio_op function is the one that does the actual work with the PIO subsystem. It receives an operation id ("op") as well as a mask ("pinmask") to which the operation applies. The possible operations are shown in the 'enum' below (taken from "inc/platform.h"): (BEGIN inc/platform.h) enum { // Pin operations PLATFORM_IO_PIN_SET, // Set pin(s) to 1 PLATFORM_IO_PIN_CLEAR, // Set pin(s) to 0 PLATFORM_IO_PIN_GET, // Get value of pin PLATFORM_IO_PIN_DIR_INPUT, // Configure pin(s) as input PLATFORM_IO_PIN_DIR_OUTPUT, // Configure pin(s) as output PLATFORM_IO_PIN_PULLUP, // Enable pullups on the pin(s) PLATFORM_IO_PIN_PULLDOWN, // Enable pulldowns on the pin(s) PLATFORM_IO_PIN_NOPULL, // Disable all pulls on the pin(s) // Port operations PLATFORM_IO_PORT_SET_VALUE, // Set port value PLATFORM_IO_PORT_GET_VALUE, // Get port value PLATFORM_IO_PORT_DIR_INPUT, // Configure port as input PLATFORM_IO_PORT_DIR_OUTPUT // Configure port as output }; (END inc/platform.h) ================================================================================ int platform_spi_exists( unsigned id ); u32 platform_spi_setup( unsigned id, int mode, u32 clock, unsigned cpol, unsigned cpha, unsigned databits ); spi_data_type platform_spi_send_recv( unsigned id, spi_data_type data ); void platform_spi_select( unsigned id, int is_select ); TYPE: OPTIONAL USED BY MODULE: spi PURPOSE: SPI operations. eLua defines 4 "virtual" SPI interfaces. The function platform_spi_exists() gets an identifier from 0 to 3 and returns 1 if the SPI interface with the given identifier exists on the target machine, 0 ohterwise. platform_spi_setup() is called to configure the SPI interface with the given parameters, returning the actual clock that was set for the interface. The actual data transfer is done by calling platform_spi_send_recv(), which executes a SPI "cycle" (send one byte, receive one byte). Finally, platform_spi_select() is used to set the state of the SPI SS (slave select) pin, if the target's SPI interface provides this functionality. ================================================================================ int platform_uart_exists( unsigned id ); u32 platform_uart_setup( unsigned id, u32 baud, int databits, int parity, int stopbits ); void platform_uart_send( unsigned id, u8 data ); int platform_uart_recv( unsigned id, unsigned timer_id, int timeout ); TYPE: OPTIONAL USED BY MODULE: uart ALSO USED BY: XMODEM, TERM over UART PURPOSE: UART operations. eLua defines 4 "virtual" UART interfaces. The function platform_uart_exists() gets an identifier from 0 to 3 and returns 1 if the UART interface with the given identifier exists on the target machine, 0 ohterwise. platform_uart_setup() is called to configure the SPI interface with the given parameters, returning the actual baud that was set for the interface. The actual data transfer is done by calling platform_uart_send to send a byte, and platform_uart_recv to receive a byte. The receive function has a timeout than can take different values: - timeout == 0: receive without waiting for data. If a data byte is available return it, otherwise return -1. - timeout == PLATFORM_UART_INFINITE_TIMEOUT: wait until a data byte is available and then return it. This will block indefinetely if no data is available. - timeout > 0: if a data byte is available in the give time (expressed in us) return id, otherwise return -1. ================================================================================ int platform_timer_exists( unsigned id ); void platform_timer_delay( unsigned id, u32 delay_us ); u32 platform_timer_op( unsigned id, int op, u32 data ); u32 platform_timer_get_diff_us( unsigned id, timer_data_type end, timer_data_type start ); TYPE: OPTIONAL USED BY MODULE: tmr, uart (for receive with timeout) ALSO USED BY: XMODEM, TERM over UART PURPOSE: timer operations. eLua defines 16 "virtual" timers. The function platform_timer_exists() gets an identifier from 0 to 15 and returns 1 if the timer with the given identifier exists on the target machine, 0 otherwise. platform_timer_delay() will block the execution for the specified number of microseconds, and platform_timer_get_diff_us() gets two timer values and returns the time difference (in microseconds) between them. platform_timer_op() executes the specified operation on the givem timer. The operations are defined in an enum from inc/platform.h: (BEGIN inc/platform.h) // Timer operations enum { PLATFORM_TIMER_OP_START, // Start the timer PLATFORM_TIMER_OP_READ, // Read the value of timer PLATFORM_TIMER_OP_SET_CLOCK, // Set the clock of the timer PLATFORM_TIMER_OP_GET_CLOCK, // Read the clock of the timer PLATFORM_TIMER_OP_GET_MAX_DELAY, // Get the maximum achievable delay PLATFORM_TIMER_OP_GET_MIN_DELAY // Get the minimum achievable delay }; (END inc/platform.h) ================================================================================ int platform_pwm_exists( unsigned id ); u32 platform_pwm_setup( unsigned id, u32 frequency, unsigned duty ); u32 platform_pwm_op( unsigned id, int op, u32 data ); TYPE: optional USED BY MODULE: pwm PURPOSE: PWM operations. eLua defines 16 "virtual" PWM blocks. The function platform_pwm_exists() gets an identifier from 0 to 15 and returns 1 if the PWM block with the given identifier exists on the target machinem, 0 otherwise. platform_pwm_setup() is called to configure the SPI interface with the given frequency and duty cycle (the duty cycle is a number from 0 to 100 representing the duty cycle in percents). Finally, platform_pwm_op() implements PWM specific operations. They are all defined in an enum from inc/platform.h, shown below: (BEGIN inc/platform.h) // PWM operations enum { PLATFORM_PWM_OP_START, // Start the PWM block PLATFORM_PWM_OP_STOP, // Stop the PWM block PLATFORM_PWM_OP_SET_CLOCK, // Set the base clock of the PWM block PLATFORM_PWM_OP_GET_CLOCK // Get the base clock of the PWM block }; (END inc/platform.h) ================================================================================ void platform_cpu_enable_interrupts(); void platform_cpu_disable_interrupts(); u32 platform_cpu_get_frequency(); TYPE: optional USED BY MODULE: cpu PURPOSE: CPU interfacing. It allows the user to control some of the CPU functions directly from Lua. platform_cpu_enable_interrupts() enables the CPU interrupts (globally), while platform_cpu_disable_interrupts() disables them. platform_cpu_get_frequency() returns the CPU "core" frequency in Hz. ================================================================================ void platform_eth_send_packet( const void* src, u32 size ); u32 platform_eth_get_packet_nb( void* buf, u32 maxlen ); void platform_eth_force_interrupt(); u32 platform_eth_get_elapsed_time(); TYPE: optional USED BY MODULE: net, also used by the generic TCP/IP support PURPOSE: network support. These functions are used by uIP (the TCP/IP stack of eLua) to implement TCP/IP services on top of the Ethernet ones (for platforms that have an integrated Ethernet controller, or are using an external Ethernet controller). platform_eth_send_packet() sends the packet pointed by "src" with size "size" over the network. platform_eth_get_packet_nb() reads an Ethernet packet in "buf", without exceeding "maxlen" of data. If an Ethernet packet is not available when this function is called, it returns 0 immediately (non-blocking receive), otherwise it returns a negative integer if the packet size is too large or the length of the packet if it fits in "maxlen" bytes. platform_eth_force_interrupt() is used to force an Ethernet receive interrupt. This is needed because uIP's processing function is called from this Ethernet interrupt handler alone. platform_eth_get_elapsed_time() will return the approximate time (in ms) that passed since the last call to platform_eth_get_elapsed_time(). It is used by uIP to process periodic events. For an example of how these functions should be implemented, take a look at the LM3S backend (src/platform/lm3s/platform.c)