Skip to content
Open
15 changes: 15 additions & 0 deletions hal/rpi-pico/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.21.0)

set(LT_HAL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/libtropic_port_rpi_pico.c
)

set(LT_HAL_INC_DIRS
${CMAKE_CURRENT_SOURCE_DIR}
)

# export generic names for parent to consume
set(LT_HAL_SRCS ${LT_HAL_SRCS} PARENT_SCOPE)
set(LT_HAL_INC_DIRS ${LT_HAL_INC_DIRS} PARENT_SCOPE)

message(STATUS "HAL for Raspberry Pi Pico added: ${LT_HAL_SRCS}")
171 changes: 171 additions & 0 deletions hal/rpi-pico/libtropic_port_rpi_pico.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/**
* @file lt_port_rpi_pico_.c
* @author Wuard
* @brief Port for Raspberry Pi Pico (RP2040) using native SPI (and GPIO for chip select).
**/

#include "libtropic_port_rpi_pico.h"

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "hardware/gpio.h"
#include "hardware/spi.h"
#include "libtropic_common.h"
#include "libtropic_examples.h"
#include "libtropic_functional_tests.h"
#include "libtropic_logging.h"
#include "libtropic_macros.h"
#include "libtropic_port.h"
#include "pico/rand.h"
#include "pico/stdlib.h"

lt_ret_t lt_port_random_bytes(lt_l2_state_t *s2, void *buff, size_t count)
{
LT_UNUSED(s2);

uint8_t *out = (uint8_t *)buff;

while (count > 0) {
uint32_t r = get_rand_32(); // 32 bits of real entropy

size_t chunk = (count >= 4) ? 4 : count;
memcpy(out, &r, chunk);

out += chunk;
count -= chunk;
}

return LT_OK;
}

lt_ret_t lt_port_spi_csn_low(lt_l2_state_t *s2)
{
lt_dev_rpi_pico_t *device = (lt_dev_rpi_pico_t *)(s2->device);
gpio_put(device->cs_pin, 0);
while (gpio_get(device->cs_pin))
;
return LT_OK;
}

lt_ret_t lt_port_spi_csn_high(lt_l2_state_t *s2)
{
lt_dev_rpi_pico_t *device = (lt_dev_rpi_pico_t *)(s2->device);
gpio_put(device->cs_pin, 1);
while (!gpio_get(device->cs_pin))
;
return LT_OK;
}

lt_ret_t lt_port_init(lt_l2_state_t *s2)
{
lt_dev_rpi_pico_t *device = (lt_dev_rpi_pico_t *)(s2->device);

device->initialized = false;

// Initialize SPI
// return the actual baudrate set by the hardware, which may be different from the requested one
uint32_t baudrate = spi_init(device->spi_instance, device->spi_baudrate);

if (baudrate != device->spi_baudrate) {
LT_LOG_ERROR("Requested SPI baudrate %u Hz, but got %u Hz", device->spi_baudrate, baudrate);
return LT_L1_SPI_ERROR;
}

gpio_set_function(device->pin_miso, GPIO_FUNC_SPI);
gpio_set_function(device->pin_mosi, GPIO_FUNC_SPI);
gpio_set_function(device->pin_sck, GPIO_FUNC_SPI);

gpio_set_function(device->cs_pin, GPIO_FUNC_SIO);

// CS as output
gpio_init(device->cs_pin);
gpio_set_dir(device->cs_pin, GPIO_OUT);
gpio_put(device->cs_pin, 1);

#ifdef LT_USE_INT_PIN
gpio_init(device->int_pin);
gpio_set_dir(device->int_pin, GPIO_IN);
#endif

device->initialized = true;
return LT_OK;
}

lt_ret_t lt_port_deinit(lt_l2_state_t *s2)
{
lt_dev_rpi_pico_t *device = (lt_dev_rpi_pico_t *)(s2->device);

spi_deinit(device->spi_instance);
device->initialized = false;
return LT_OK;
}

lt_ret_t lt_port_spi_transfer(lt_l2_state_t *s2, uint8_t offset, uint16_t tx_data_length,
uint32_t timeout_ms)
{
lt_dev_rpi_pico_t *device = (lt_dev_rpi_pico_t *)(s2->device);

if (offset + tx_data_length > TR01_L1_LEN_MAX) {
LT_LOG_ERROR("Invalid data length!");
return LT_L1_DATA_LEN_ERROR;
}

// returns the number of bytes transferred, which should be equal to tx_data_length
uint16_t dataLen = spi_write_read_blocking(device->spi_instance, s2->buff + offset,
s2->buff + offset, tx_data_length);

if (dataLen != tx_data_length) {
LT_LOG_ERROR("SPI transfer failed! Expected to transfer %u bytes, but transferred %u bytes",
tx_data_length, dataLen);
return LT_L1_SPI_ERROR;
}

return LT_OK;
}

lt_ret_t lt_port_delay(lt_l2_state_t *s2, uint32_t ms)
{
(void)s2;
sleep_ms(ms);
return LT_OK;
}

#if LT_USE_INT_PIN
lt_ret_t lt_port_delay_on_int(lt_l2_state_t *s2, uint32_t ms)
{
lt_dev_rpi_pico_t *device = (lt_dev_rpi_pico_t *)(s2->device);

absolute_time_t start = get_absolute_time();
while (gpio_get(device->int_pin) == 0) {
if (absolute_time_diff_us(start, get_absolute_time()) / 1000 > ms) {
return LT_L1_INT_TIMEOUT;
}
sleep_ms(1);
}
return LT_OK;
}
#endif

int lt_port_log(const char *format, ...)
{
static char log_buff[1024];
va_list args;
int ret;

va_start(args, format);
ret = vsnprintf(log_buff, sizeof(log_buff), format, args);
va_end(args);

if (ret > 0) {
size_t len = strnlen(log_buff, sizeof(log_buff));

// Pico SDK
fwrite(log_buff, 1, len, stdout);
fflush(stdout);
}

return ret;
}
44 changes: 44 additions & 0 deletions hal/rpi-pico/libtropic_port_rpi_pico.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#ifndef LT_PORT_RPI_PICO_H
#define LT_PORT_RPI_PICO_H

/**
* @file lt_port_rpi_pico_.c
* @author Wuard
* @brief Port for Raspberry Pi Pico (RP2040) using native SPI (and GPIO for chip select).
**/

#include "hardware/gpio.h"
#include "hardware/spi.h"
#include "libtropic_port.h"
#include "pico/stdlib.h"

/**
* @brief Device structure for Raspberry Pi Pico port.
*
* The user fills in the parameters before passing the handle to libtropic.
*/
typedef struct lt_dev_rpi_pico_t {
/** @brief @public SPI instance (e.g., spi0 or spi1). */
spi_inst_t *spi_instance;

/** @brief @public SPI frequency in Hz (e.g., 1 MHz, 4 MHz, etc.). */
uint32_t spi_baudrate;

/** @brief @public Pin used for chip select (GPIO). */
uint cs_pin;

/** @brief @public SPI pins */
uint pin_miso;
uint pin_mosi;
uint pin_sck;

#ifdef LT_USE_INT_PIN
/** @brief @public Pin used for interrupts (optional). */
uint16_t int_gpio_pin;
#endif

/** @brief @private Initialization flag */
bool initialized;
} lt_dev_rpi_pico_t;

#endif // LT_PORT_PICO_H