Add RP2040 support (#168)

* Add PICO SDK

* Add RP2040 HAL implementation

* Add target Raspberry Pi Pico and its Upload method

* Modified PICO SDK-RTC because of conflict in name

* Use USB from boot, fix USB linking

* Try removing redundant init call

* Add SWD upload configuration, copy most init code from Pico SDK

* Fix RTC linking, fix ram size constant, fix test warning

* Add upload support for RPi Pico devices using Picotool

* Fix implementation of Tx IRQ for serial ports.  BufferedSerial works now!

* Make PinNames.h pass pin validation

* Fix us ticker not working when debugging

* Fix us ticker double-init and manual fire interrupt function, us ticker tests now pass!

* Fix writing to rtc and rtc double-init, RTC tests now pass (except rtc-reset)

* Fix panic() not working from a critical section or ISR

* Fix compile failure due to extra LED1 definition

* Fix style

* Fix flash_api detection of invalid parameters, fix reset_reason to advertise the correct capabilities and implement them correctly

* Fix watchdog test warnings, fix broken hal_watchdog_get_reload_value(), fix missing frequency field in watchdog features

* Fix watchdog_reset failing to compile on some devices

* Fix us ticker fire_interrupt() not being callable from an ISR

* Fix incorrect license header

---------

Co-authored-by: Johnk1987 <odiin@seznam.cz>
pull/15437/head
Jamie Smith 2023-07-12 21:11:26 -07:00 committed by GitHub
parent 785e3fb87b
commit a9f8e097e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
380 changed files with 123131 additions and 61 deletions

View File

@ -255,8 +255,10 @@ void serial_format(serial_t *obj, int data_bits, SerialParity parity, int stop_b
/** The serial interrupt handler registration
*
* @param obj The serial object
* @param handler The interrupt handler which will be invoked when the interrupt fires
* @param id The SerialBase object
* @param handler The interrupt handler function which will be invoked when the interrupt fires. The handler
* function pointer must be common among all serial instances.
* @param id The SerialBase object. This shall be passed to \c handler when it's
* invoked for this serial instance.
*/
void serial_irq_handler(serial_t *obj, uart_irq_handler handler, uint32_t id);

View File

@ -155,6 +155,9 @@ void us_ticker_irq_handler(void);
* clocking and prescaler registers, along with disabling
* the compare interrupt.
*
* Implementations must tolerate this function being called multiple times, and subsequent calls
* after the first one shall clear any configured interrupt.
*
* @note Initialization properties tested by ticker_init_test
*
* Pseudo Code:
@ -227,6 +230,7 @@ void us_ticker_free(void);
* }
* @endcode
*/
// note: parenthesis around the function name make sure this isn't replaced by the us_ticker_read() macro version.
uint32_t (us_ticker_read)(void);
/** Set interrupt for specified timestamp
@ -282,6 +286,9 @@ void us_ticker_clear_interrupt(void);
*
* The ticker should be initialized prior calling this function.
*
* Note: This function might be called from the ticker ISR, in which case it should
* schedule the ticker ISR to be executed again as soon as it returns.
*
* Pseudo Code:
* @code
* void us_ticker_fire_interrupt(void)

View File

@ -29,6 +29,7 @@
#include "rtc_api.h"
#include <type_traits>
#include <mstd_atomic>
#include <cinttypes>
using namespace utest::v1;
using namespace std::chrono;
@ -135,6 +136,7 @@ void rtc_persist_test()
const bool enabled = RealTimeClock::isenabled();
RealTimeClock::free();
printf("start = %" PRIi64 ", stop = %" PRIi64 "\n", start.time_since_epoch().count(), stop.time_since_epoch().count());
TEST_ASSERT_TRUE(enabled);
TEST_ASSERT_DURATION_WITHIN(WAIT_TOLERANCE, WAIT_TIME, stop - start);
}
@ -199,16 +201,24 @@ void rtc_accuracy_test()
void rtc_write_read_test()
{
RealTimeClock::init();
RealTimeClock::write(RealTimeClock::time_point(1s));
/* NB: IAR compilation issue with "auto init_val = RealTimeClock::time_point(100s)" */
for (auto init_val = RealTimeClock::time_point(seconds(100)); init_val < RealTimeClock::time_point(400s); init_val += 100s) {
core_util_critical_section_enter();
// To prevent the RTC from ticking during the read-write operation, busy wait until it ticks.
// That gives us a second in which to execute the test.
auto initialVal = RealTimeClock::now();
while (RealTimeClock::now() == initialVal) {}
RealTimeClock::write(init_val);
const auto read_val = RealTimeClock::now();
core_util_critical_section_exit();
printf("Wrote: %" PRIi64 ", Read: %" PRIi64 "\n", init_val.time_since_epoch().count(), read_val.time_since_epoch().count());
/* No tolerance is provided since we should have 1 second to
* execute this case after the RTC time is set.
*/

View File

@ -128,7 +128,7 @@ void test_mk_time_boundary()
pTestCases = test_mk_time_arr_partial;
}
for (int i = 0; i < (sizeof(test_mk_time_arr_full) / (sizeof(test_mk_time_struct))); i++) {
for (size_t i = 0; i < (sizeof(test_mk_time_arr_full) / (sizeof(test_mk_time_struct))); i++) {
time_t seconds;
bool result = _rtc_maketime(&pTestCases[i].timeinfo, &seconds, rtc_leap_year_support);
@ -187,7 +187,7 @@ void test_set_time_twice()
TEST_ASSERT_EQUAL(true, (current_time == NEW_TIME));
/* Wait 2 seconds */
ThisThread::sleep_for(2000);
ThisThread::sleep_for(2s);
/* set the time to NEW_TIME again and check it */
set_time(NEW_TIME);

View File

@ -29,12 +29,12 @@
#include <stdlib.h>
/* The shortest timeout value, this test suite is able to handle correctly. */
#define WDG_MIN_TIMEOUT_MS 50UL
#define WDG_MIN_TIMEOUT_MS 50ms
// Do not set watchdog timeout shorter than WDG_MIN_TIMEOUT_MS, as it may
// cause the host-test-runner return 'TIMEOUT' instead of 'FAIL' / 'PASS'
// if watchdog performs reset during test suite teardown.
#define WDG_TIMEOUT_MS 100UL
#define WDG_TIMEOUT_MS 100ms
#define MSG_VALUE_DUMMY "0"
#define MSG_VALUE_LEN 24
@ -57,7 +57,7 @@
* (1 start_bit + 8 data_bits + 1 stop_bit) * 128 * 1000 / 9600 = 133.3 ms.
* To be on the safe side, set the wait time to 150 ms.
*/
#define SERIAL_FLUSH_TIME_MS 150
#define SERIAL_FLUSH_TIME_MS 150ms
int CASE_INDEX_START;
int CASE_INDEX_CURRENT;
@ -67,7 +67,7 @@ using utest::v1::Case;
using utest::v1::Specification;
using utest::v1::Harness;
const watchdog_config_t WDG_CONFIG_DEFAULT = { .timeout_ms = WDG_TIMEOUT_MS };
const watchdog_config_t WDG_CONFIG_DEFAULT = { .timeout_ms = WDG_TIMEOUT_MS.count() };
void test_max_timeout_is_valid()
{
@ -115,12 +115,12 @@ void test_update_config()
}
watchdog_config_t config = WDG_CONFIG_DEFAULT;
uint32_t timeouts[] = {
features.max_timeout / 4,
features.max_timeout / 8,
features.max_timeout / 16
std::chrono::milliseconds timeouts[] = {
std::chrono::milliseconds(features.max_timeout / 4),
std::chrono::milliseconds(features.max_timeout / 8),
std::chrono::milliseconds(features.max_timeout / 16)
};
int num_timeouts = sizeof timeouts / sizeof timeouts[0];
size_t num_timeouts = sizeof timeouts / sizeof timeouts[0];
for (size_t i = 0; i < num_timeouts; i++) {
if (timeouts[i] < WDG_MIN_TIMEOUT_MS) {
@ -129,9 +129,10 @@ void test_update_config()
return;
}
config.timeout_ms = timeouts[i];
config.timeout_ms = timeouts[i].count();
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
uint32_t reload_value = hal_watchdog_get_reload_value();
auto reload_value = std::chrono::milliseconds(hal_watchdog_get_reload_value());
// The watchdog should trigger at, or after the timeout value.
TEST_ASSERT(reload_value >= timeouts[i]);
// The watchdog should trigger before twice the timeout value.
@ -155,7 +156,7 @@ utest::v1::status_t case_teardown_sync_on_reset(const Case *const source, const
// Start kicking the watchdog during teardown.
hal_watchdog_kick();
Ticker wdg_kicking_ticker;
wdg_kicking_ticker.attach_us(mbed::callback(hal_watchdog_kick), 20000);
wdg_kicking_ticker.attach(mbed::callback(hal_watchdog_kick), 20ms);
utest::v1::status_t status = utest::v1::greentea_case_teardown_handler(source, passed, failed, failure);
if (failed) {
/* Return immediately and skip the device reset, if the test case failed.
@ -193,7 +194,7 @@ utest::v1::status_t case_teardown_wdg_stop_or_reset(const Case *const source, co
template<uint32_t timeout_ms>
void test_init()
{
if (timeout_ms < WDG_MIN_TIMEOUT_MS) {
if (std::chrono::milliseconds(timeout_ms) < WDG_MIN_TIMEOUT_MS) {
CASE_IGNORED = true;
TEST_IGNORE_MESSAGE("Requested timeout value is too short -- ignoring test case.");
return;
@ -216,7 +217,7 @@ void test_init_max_timeout()
TEST_ASSERT(hal_watchdog_get_reload_value() >= features.max_timeout);
}
int testsuite_setup_sync_on_reset(const size_t number_of_cases)
utest::v1::status_t testsuite_setup_sync_on_reset(const size_t number_of_cases)
{
GREENTEA_SETUP(45, "sync_on_reset");
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
@ -243,7 +244,7 @@ int testsuite_setup_sync_on_reset(const size_t number_of_cases)
}
utest_printf("Starting with test case index %i of all %i defined test cases.\n", CASE_INDEX_START, number_of_cases);
return CASE_INDEX_START;
return static_cast<utest::v1::status_t>(CASE_INDEX_START);
}
Case cases[] = {
@ -262,7 +263,7 @@ Case cases[] = {
test_init_max_timeout, (utest::v1::case_teardown_handler_t) case_teardown_sync_on_reset),
};
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup_sync_on_reset, cases);
Specification specification(testsuite_setup_sync_on_reset, cases);
int main()
{

View File

@ -25,7 +25,9 @@
#include "watchdog_reset_tests.h"
#include "mbed.h"
#define TIMEOUT_MS 100UL
#include <cinttypes>
#define TIMEOUT_MS 100ms
/* This value is used to calculate the time to kick the watchdog.
* Given the watchdog timeout is set to TIMEOUT_MS, the kick will be performed
@ -40,7 +42,7 @@
* and as short as 66 ms.
* The value of 35 ms is used to cover the worst case scenario (66 ms).
*/
#define KICK_ADVANCE_MS 35UL
#define KICK_ADVANCE_MS 35ms
#define MSG_VALUE_DUMMY "0"
#define CASE_DATA_INVALID 0xffffffffUL
@ -66,11 +68,8 @@
* (1 start_bit + 8 data_bits + 1 stop_bit) * 128 * 1000 / 9600 = 133.3 ms.
* To be on the safe side, set the wait time to 150 ms.
*/
#define SERIAL_FLUSH_TIME_MS 150
#define SERIAL_FLUSH_TIME_MS 150ms
#define TIMEOUT_US (1000 * (TIMEOUT_MS))
#define KICK_ADVANCE_US (1000 * (KICK_ADVANCE_MS))
#define SERIAL_FLUSH_TIME_US (1000 * (SERIAL_FLUSH_TIME_MS))
using utest::v1::Case;
using utest::v1::Specification;
@ -86,10 +85,10 @@ testcase_data current_case;
Ticker wdg_kicking_ticker;
bool send_reset_notification(testcase_data *tcdata, uint32_t delay_ms)
bool send_reset_notification(testcase_data *tcdata, std::chrono::milliseconds delay_ms)
{
char msg_value[12];
int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08lx", tcdata->start_index + tcdata->index, delay_ms);
int str_len = snprintf(msg_value, sizeof msg_value, "%02x,%08" PRIx64, tcdata->start_index + tcdata->index, delay_ms.count());
if (str_len < 0) {
utest_printf("Failed to compose a value string to be sent to host.");
return false;
@ -110,20 +109,20 @@ void test_simple_reset()
// Phase 1. -- run the test code.
// Init the watchdog and wait for a device reset.
watchdog_config_t config = { TIMEOUT_MS };
if (send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS) == false) {
watchdog_config_t config = { TIMEOUT_MS.count() };
if (!send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS)) {
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
return;
}
wait_us(SERIAL_FLUSH_TIME_US); // Wait for the serial buffers to flush.
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(SERIAL_FLUSH_TIME_MS).count()); // Wait for the serial buffers to flush.
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
// Watchdog should fire before twice the timeout value.
wait_us(2 * TIMEOUT_US); // Device reset expected.
wait_us(2 * std::chrono::duration_cast<std::chrono::microseconds>(TIMEOUT_MS).count()); // Device reset expected.
// Watchdog reset should have occurred during a wait above.
hal_watchdog_kick();
wdg_kicking_ticker.attach_us(mbed::callback(hal_watchdog_kick), 20000); // For testsuite failure handling.
wdg_kicking_ticker.attach(mbed::callback(hal_watchdog_kick), 20ms); // For testsuite failure handling.
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
}
@ -138,12 +137,12 @@ void test_sleep_reset()
}
// Phase 1. -- run the test code.
watchdog_config_t config = { TIMEOUT_MS };
if (send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS) == false) {
watchdog_config_t config = { TIMEOUT_MS.count() };
if (!send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS)) {
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
return;
}
wait_us(SERIAL_FLUSH_TIME_US); // Wait for the serial buffers to flush.
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(SERIAL_FLUSH_TIME_MS).count()); // Wait for the serial buffers to flush.
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
sleep_manager_lock_deep_sleep();
if (sleep_manager_can_deep_sleep()) {
@ -157,7 +156,7 @@ void test_sleep_reset()
// Watchdog reset should have occurred during the sleep above.
hal_watchdog_kick();
wdg_kicking_ticker.attach_us(mbed::callback(hal_watchdog_kick), 20000); // For testsuite failure handling.
wdg_kicking_ticker.attach(mbed::callback(hal_watchdog_kick), 20ms); // For testsuite failure handling.
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
}
@ -172,13 +171,13 @@ void test_deepsleep_reset()
}
// Phase 1. -- run the test code.
watchdog_config_t config = { TIMEOUT_MS };
if (send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS) == false) {
watchdog_config_t config = { TIMEOUT_MS.count() };
if (!send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS)) {
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
return;
}
wait_us(SERIAL_FLUSH_TIME_US); // Wait for the serial buffers to flush.
wait_us(SERIAL_FLUSH_TIME_US); // Wait for the serial buffers to flush.
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(SERIAL_FLUSH_TIME_MS).count()); // Wait for the serial buffers to flush.
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(SERIAL_FLUSH_TIME_MS).count()); // Wait for the serial buffers to flush.
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
if (!sleep_manager_can_deep_sleep()) {
TEST_ASSERT_MESSAGE(0, "Deepsleep should be allowed.");
@ -193,7 +192,7 @@ void test_deepsleep_reset()
// Watchdog reset should have occurred during the deepsleep above.
hal_watchdog_kick();
wdg_kicking_ticker.attach_us(mbed::callback(hal_watchdog_kick), 20000); // For testsuite failure handling.
wdg_kicking_ticker.attach(mbed::callback(hal_watchdog_kick), 20ms); // For testsuite failure handling.
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
}
#endif
@ -215,28 +214,29 @@ void test_restart_reset()
}
// Phase 1. -- run the test code.
watchdog_config_t config = { TIMEOUT_MS };
watchdog_config_t config = { TIMEOUT_MS.count() };
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
wait_us(TIMEOUT_US / 2);
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(TIMEOUT_MS / 2).count());
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_stop());
// Check that stopping the Watchdog prevents a device reset.
// The watchdog should trigger at, or after the timeout value.
// The watchdog should trigger before twice the timeout value.
wait_us(TIMEOUT_US / 2 + TIMEOUT_US);
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(TIMEOUT_MS + TIMEOUT_MS / 2).count());
if (send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS) == false) {
if (!send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS)) {
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
return;
}
wait_us(SERIAL_FLUSH_TIME_US); // Wait for the serial buffers to flush.
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(SERIAL_FLUSH_TIME_MS).count()); // Wait for the serial buffers to flush.
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
// Watchdog should fire before twice the timeout value.
wait_us(2 * TIMEOUT_US); // Device reset expected.
wait_us(2 * std::chrono::duration_cast<std::chrono::microseconds>(TIMEOUT_MS).count()); // Device reset expected.
// Watchdog reset should have occurred during a wait above.
hal_watchdog_kick();
wdg_kicking_ticker.attach_us(mbed::callback(hal_watchdog_kick), 20000); // For testsuite failure handling.
wdg_kicking_ticker.attach(mbed::callback(hal_watchdog_kick), 20ms); // For testsuite failure handling.
TEST_ASSERT_MESSAGE(0, "Watchdog did not reset the device as expected.");
}
@ -250,21 +250,21 @@ void test_kick_reset()
}
// Phase 1. -- run the test code.
watchdog_config_t config = { TIMEOUT_MS };
watchdog_config_t config = { TIMEOUT_MS.count() };
TEST_ASSERT_EQUAL(WATCHDOG_STATUS_OK, hal_watchdog_init(&config));
for (int i = 3; i; i--) {
// The reset is prevented as long as the watchdog is kicked
// anytime before the timeout.
wait_us(TIMEOUT_US - KICK_ADVANCE_US);
wait_us(2 * std::chrono::duration_cast<std::chrono::microseconds>(TIMEOUT_MS - KICK_ADVANCE_MS).count());
hal_watchdog_kick();
}
if (send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS) == false) {
if (!send_reset_notification(&current_case, 2 * TIMEOUT_MS + SERIAL_FLUSH_TIME_MS)) {
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
return;
}
wait_us(SERIAL_FLUSH_TIME_US); // Wait for the serial buffers to flush.
wait_us(std::chrono::duration_cast<std::chrono::microseconds>(SERIAL_FLUSH_TIME_MS).count()); // Wait for the serial buffers to flush.
// Watchdog should fire before twice the timeout value.
wait_us(2 * TIMEOUT_US); // Device reset expected.
wait_us(2 * std::chrono::duration_cast<std::chrono::microseconds>(TIMEOUT_MS).count()); // Device reset expected.
// Watchdog reset should have occurred during a wait above.
@ -279,7 +279,7 @@ utest::v1::status_t case_setup(const Case *const source, const size_t index_of_c
return utest::v1::greentea_case_setup_handler(source, index_of_case);
}
int testsuite_setup(const size_t number_of_cases)
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(90, "watchdog_reset");
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
@ -306,7 +306,7 @@ int testsuite_setup(const size_t number_of_cases)
utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases,
current_case.start_index);
return current_case.start_index;
return static_cast<utest::v1::status_t>(current_case.start_index);
}
Case cases[] = {
@ -321,7 +321,7 @@ Case cases[] = {
Case("Kicking the Watchdog prevents reset", case_setup, test_kick_reset),
};
Specification specification((utest::v1::test_setup_handler_t) testsuite_setup, cases);
Specification specification(testsuite_setup, cases);
int main()
{

View File

@ -157,7 +157,7 @@ void test_timeout_lower_limit()
wait_us(sleep_time_ms * 1000);
hal_watchdog_kick();
if (send_reset_notification(&current_case, 2 * TIMEOUT_LOWER_LIMIT_MS) == false) {
if (!send_reset_notification(&current_case, 2 * TIMEOUT_LOWER_LIMIT_MS)) {
TEST_ASSERT_MESSAGE(0, "Dev-host communication error.");
return;
}
@ -178,7 +178,7 @@ utest::v1::status_t case_setup(const Case *const source, const size_t index_of_c
return utest::v1::greentea_case_setup_handler(source, index_of_case);
}
int testsuite_setup(const size_t number_of_cases)
utest::v1::status_t testsuite_setup(const size_t number_of_cases)
{
GREENTEA_SETUP(90, "watchdog_reset");
utest::v1::status_t status = utest::v1::greentea_test_setup_handler(number_of_cases);
@ -205,7 +205,9 @@ int testsuite_setup(const size_t number_of_cases)
utest_printf("This test suite is composed of %i test cases. Starting at index %i.\n", number_of_cases,
current_case.start_index);
return current_case.start_index;
// Return a positive value to ask utest to start from the given case number
return static_cast<utest::v1::status_t>(current_case.start_index);
}
Case cases[] = {

View File

@ -20,4 +20,5 @@ if(${CMAKE_CROSSCOMPILING})
add_subdirectory(TARGET_Silicon_Labs EXCLUDE_FROM_ALL)
add_subdirectory(TARGET_STM EXCLUDE_FROM_ALL)
add_subdirectory(TARGET_TOSHIBA EXCLUDE_FROM_ALL)
add_subdirectory(TARGET_RASPBERRYPI EXCLUDE_FROM_ALL)
endif()

View File

@ -0,0 +1,18 @@
# Copyright (c) 2020-2021 ARM Limited. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
add_subdirectory(TARGET_RP2040 EXCLUDE_FROM_ALL)
add_library(mbed-raspberrypi INTERFACE)
target_include_directories(mbed-raspberrypi
INTERFACE
.
)
target_sources(mbed-raspberrypi
INTERFACE
.
)
target_link_libraries(mbed-raspberrypi INTERFACE mbed-cmsis-cortex-m)

View File

@ -0,0 +1,23 @@
pico-sdk/common/pico_stdlib/include/pico/*
pico-sdk/common/pico_time/include/pico/*
pico-sdk/rp2_common/pico_stdio*
pico-sdk/rp2_common/pico_printf*
pico-sdk/boards/include/boards/*
pico-sdk/common/pico_base/include/pico/*
pico-sdk/rp2_common/boot_stage2/*
pico-sdk/rp2_common/pico_malloc/*
pico-sdk/rp2_common/pico_stdlib/
pico-sdk/rp2_common/pico_mem_ops/*
pico-sdk/rp2_common/pico_double/double_aeabi.S
pico-sdk/rp2_common/pico_double/double_none.S
pico-sdk/rp2_common/pico_float/float_aeabi.S
pico-sdk/rp2_common/pico_float/float_none.S
pico-sdk/rp2_common/pico_float/include/pico/*
pico-sdk/rp2_common/pico_standard_link/new_delete.cpp
pico-sdk/rp2_common/pico_standard_link/*.ld
pico-sdk/rp2_common/pico_unique_id/*
pico-sdk/rp2_common/hardware_divider/*
pico-sdk/rp2_common/hardware_spi/include/hardware/*
pico-sdk/rp2040/hardware_structs/include/hardware/structs/*
pico-sdk/rp2040/hardware_regs/include/hardware/regs/*
pico-sdk/host/*

View File

@ -0,0 +1,87 @@
# Copyright (c) 2020 ARM Limited. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
add_subdirectory(TARGET_RASPBERRY_PI_PICO EXCLUDE_FROM_ALL)
set(LINKER_FILE TOOLCHAIN_GCC_ARM//memmap_default.ld)
add_library(mbed-rp2040 INTERFACE)
target_include_directories(mbed-rp2040
INTERFACE
pico-sdk/rp2_common/hardware_adc/include
pico-sdk/rp2_common/hardware_gpio/include
pico-sdk/rp2_common/hardware_resets/include
pico-sdk/rp2_common/hardware_pwm/include
pico-sdk/rp2_common/hardware_base/include
pico-sdk/rp2_common/hardware_uart/include
pico-sdk/rp2_common/hardware_spi/include
pico-sdk/rp2_common/hardware_i2c/include
pico-sdk/rp2_common/hardware_irq/include
pico-sdk/rp2_common/hardware_flash/include
pico-sdk/rp2_common/hardware_clocks/include
pico-sdk/rp2_common/hardware_rtc/include
pico-sdk/rp2_common/hardware_watchdog/include
pico-sdk/rp2_common/hardware_timer/include
pico-sdk/rp2_common/hardware_pll/include
pico-sdk/rp2_common/hardware_sync/include
pico-sdk/rp2_common/hardware_xosc/include
pico-sdk/rp2_common/pico_platform/include
pico-sdk/rp2_common/pico_fix/rp2040_usb_device_enumeration/include/
pico-sdk/rp2_common/pico_bootrom/include
pico-sdk/rp2_common/hardware_claim/include
pico-sdk/rp2040/hardware_structs/include
pico-sdk/rp2040/hardware_regs/include
pico-sdk/common/pico_sync/include
pico-sdk/common/pico_time/include
pico-sdk/common/pico_base/include
pico-sdk/common/pico_binary_info/include
pico-sdk/common/pico_util/include
pico-sdk/boards/include
pico-sdk/generated
.
)
target_sources(mbed-rp2040
INTERFACE
analogin_api.c
flash_api.c
gpio_api.c
i2c_api.c
lp_ticker.c
mbed_overrides.c
PeripheralPins.c
pwmout_api.c
reset_reason.c
serial_api.c
spi_api.c
rtc_api.c
us_ticker.c
USBPhy_RP2040.cpp
watchdog_api.c
pico-sdk/rp2_common/pico_standard_link/crt0.S
pico-sdk/rp2_common/hardware_flash/flash.c
pico-sdk/rp2_common/hardware_uart/uart.c
pico-sdk/rp2_common/hardware_spi/spi.c
pico-sdk/rp2_common/hardware_i2c/i2c.c
pico-sdk/rp2_common/hardware_gpio/gpio.c
pico-sdk/rp2_common/hardware_xosc/xosc.c
pico-sdk/rp2_common/hardware_irq/irq.c
pico-sdk/rp2_common/hardware_irq/irq_handler_chain.S
pico-sdk/rp2_common/hardware_pll/pll.c
pico-sdk/rp2_common/hardware_watchdog/watchdog.c
pico-sdk/rp2_common/hardware_clocks/clocks.c
pico-sdk/rp2_common/hardware_claim/claim.c
pico-sdk/rp2_common/hardware_timer/timer.c
pico-sdk/rp2_common/hardware_sync/sync.c
pico-sdk/rp2_common/hardware_rtc/rtc.c
pico-sdk/rp2_common/pico_bootrom/bootrom.c
pico-sdk/rp2_common/pico_platform/platform.c
pico-sdk/common/pico_time/time.c
pico-sdk/common/pico_sync/lock_core.c
pico-sdk/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c
)
mbed_set_linker_script(mbed-rp2040 ${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_FILE})
target_link_libraries(mbed-rp2040 INTERFACE mbed-raspberrypi)

View File

@ -0,0 +1,101 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_PERIPHERALNAMES_H
#define MBED_PERIPHERALNAMES_H
#include "cmsis.h"
#include "PinNames.h"
typedef enum {
UART_0 = 0,
UART_1,
} UARTName;
typedef enum {
ADC0 = 0,
} ADCName;
typedef enum {
SPI_0 = 0,
SPI_1
} SPIName;
typedef enum {
I2C_0 = 0,
I2C_1
} I2CName;
typedef enum {
PWM_0 = 0,
PWM_1,
PWM_2,
PWM_3,
PWM_4,
PWM_5,
PWM_6,
PWM_7
} PWMName;
/* Defines to be used by application */
typedef enum {
PIN_INPUT = 0,
PIN_OUTPUT
} PinDirection;
typedef enum {
PullNone = 0,
PullUp = 1,
PullDown = 2,
OpenDrainPullUp = 3,
OpenDrainNoPull = 4,
OpenDrainPullDown = 5,
PushPullNoPull = PullNone,
PushPullPullUp = PullUp,
PushPullPullDown = PullDown,
OpenDrain = OpenDrainPullUp,
PullDefault = PullNone
} PinMode;
#define STDIO_UART_TX CONSOLE_TX
#define STDIO_UART_RX CONSOLE_RX
#define STDIO_UART uart0
// Default peripherals
#define MBED_SPI0 p5, p6, p7, p8
#define MBED_SPI1 p11, p12, p13, p14
#define MBED_UART0 p9, p10
#define MBED_UART1 p13, p14
#define MBED_UART2 p28, p27
#define MBED_UARTUSB CONSOLE_TX, CONSOLE_RX
#define MBED_I2C0 p28, p27
#define MBED_I2C1 p9, p10
#define MBED_ANALOGIN0 p15
#define MBED_ANALOGIN1 p16
#define MBED_ANALOGIN2 p17
#define MBED_ANALOGIN3 p18
#define MBED_PWMOUT0 p26
#define MBED_PWMOUT1 p25
#define MBED_PWMOUT2 p24
#define MBED_PWMOUT3 p23
#define MBED_PWMOUT4 p22
#define MBED_PWMOUT5 p21
#endif

View File

@ -0,0 +1,220 @@
#include "pinmap.h"
#include "objects.h"
#include "PeripheralPins.h"
const PinMap PinMap_FULL[] = {
{p0, 0, 0},
{p1, 0, 0},
{p2, 0, 0},
{p3, 0, 0},
{p4, 0, 0},
{p5, 0, 0},
{p6, 0, 0},
{p7, 0, 0},
{p8, 0, 0},
{p9, 0, 0},
{p10, 0, 0},
{p11, 0, 0},
{p12, 0, 0},
{p13, 0, 0},
{p14, 0, 0},
{p15, 0, 0},
{p16, 0, 0},
{p17, 0, 0},
{p18, 0, 0},
{p19, 0, 0},
{p20, 0, 0},
{p21, 0, 0},
{p22, 0, 0},
{p23, 0, 0},
{p24, 0, 0},
{p25, 0, 0},
{p26, 0, 0},
{p27, 0, 0},
{p28, 0, 0},
{p29, 0, 0},
{NC, NC, 0}
};
/************UART***************/
const PinMap PinMap_UART_TX[] = {
{p0, UART_0, (uint32_t) uart0},
{p4, UART_1, (uint32_t) uart1},
{p8, UART_1, (uint32_t) uart1},
{p12, UART_0, (uint32_t) uart0},
{p16, UART_0, (uint32_t) uart0},
{p20, UART_1, (uint32_t) uart1},
{p24, UART_1, (uint32_t) uart1},
{p28, UART_0, (uint32_t) uart0},
{NC, NC, 0}
};
const PinMap PinMap_UART_RX[] = {
{p1, UART_0, (uint32_t) uart0},
{p5, UART_1, (uint32_t) uart1},
{p9, UART_1, (uint32_t) uart1},
{p13, UART_0, (uint32_t) uart0},
{p17, UART_0, (uint32_t) uart0},
{p21, UART_1, (uint32_t) uart1},
{p25, UART_1, (uint32_t) uart1},
{p29, UART_0, (uint32_t) uart0},
{NC, NC, 0}
};
const PinMap PinMap_UART_CTS[] = {
{p2, UART_0, (uint32_t) uart0},
{p6, UART_1, (uint32_t) uart1},
{p10, UART_1, (uint32_t) uart1},
{p14, UART_0, (uint32_t) uart0},
{p18, UART_0, (uint32_t) uart0},
{p22, UART_1, (uint32_t) uart1},
{p26, UART_1, (uint32_t) uart1},
{NC, NC, 0}
};
const PinMap PinMap_UART_RTS[] = {
{p3, UART_0, (uint32_t) uart0},
{p7, UART_1, (uint32_t) uart1},
{p11, UART_1, (uint32_t) uart1},
{p15, UART_0, (uint32_t) uart0},
{p19, UART_0, (uint32_t) uart0},
{p23, UART_1, (uint32_t) uart1},
{p27, UART_1, (uint32_t) uart1},
{NC, NC, 0}
};
/************PWM***************/
const PinMap PinMap_PWM_OUT[] = {
{p0, PWM_0, 0},
{p1, PWM_0, 0},
{p2, PWM_1, 0},
{p3, PWM_1, 0},
{p4, PWM_2, 0},
{p5, PWM_2, 0},
{p6, PWM_3, 0},
{p7, PWM_3, 0},
{p8, PWM_4, 0},
{p9, PWM_4, 0},
{p10, PWM_5, 0},
{p11, PWM_5, 0},
{p12, PWM_6, 0},
{p13, PWM_6, 0},
{p14, PWM_7, 0},
{p15, PWM_7, 0},
{p16, PWM_0, 0},
{p17, PWM_0, 0},
{p18, PWM_1, 0},
{p19, PWM_1, 0},
{p20, PWM_2, 0},
{p21, PWM_2, 0},
{p22, PWM_3, 0},
{p23, PWM_3, 0},
{p24, PWM_4, 0},
{p25, PWM_4, 0},
{p26, PWM_5, 0},
{p27, PWM_5, 0},
{p28, PWM_6, 0},
{p29, PWM_6, 0},
{NC, NC, 0}
};
/************SPI***************/
const PinMap PinMap_SPI_MISO[] = {
{p0, SPI_0, (uint32_t) spi0},
{p4, SPI_0, (uint32_t) spi0},
{p8, SPI_1, (uint32_t) spi1},
{p12, SPI_1, (uint32_t) spi1},
{p16, SPI_0, (uint32_t) spi0},
{p20, SPI_0, (uint32_t) spi0},
{p24, SPI_1, (uint32_t) spi1},
{p28, SPI_1, (uint32_t) spi1},
{NC, NC, 0}
};
const PinMap PinMap_SPI_SSEL[] = {
{p1, SPI_0, (uint32_t) spi0},
{p5, SPI_0, (uint32_t) spi0},
{p9, SPI_1, (uint32_t) spi1},
{p13, SPI_1, (uint32_t) spi1},
{p17, SPI_0, (uint32_t) spi0},
{p21, SPI_0, (uint32_t) spi0},
{p25, SPI_1, (uint32_t) spi1},
{p29, SPI_1, (uint32_t) spi1},
{NC, NC, 0}
};
const PinMap PinMap_SPI_SCLK[] = {
{p2, SPI_0, (uint32_t) spi0},
{p6, SPI_0, (uint32_t) spi0},
{p10, SPI_1, (uint32_t) spi1},
{p14, SPI_1, (uint32_t) spi1},
{p18, SPI_0, (uint32_t) spi0},
{p22, SPI_0, (uint32_t) spi0},
{p26, SPI_1, (uint32_t) spi1},
{NC, NC, 0}
};
const PinMap PinMap_SPI_MOSI[] = {
{p3, SPI_0, (uint32_t) spi0},
{p7, SPI_0, (uint32_t) spi0},
{p11, SPI_1, (uint32_t) spi1},
{p15, SPI_1, (uint32_t) spi1},
{p19, SPI_0, (uint32_t) spi0},
{p23, SPI_0, (uint32_t) spi0},
{p27, SPI_1, (uint32_t) spi1},
{NC, NC, 0}
};
/************I2C***************/
const PinMap PinMap_I2C_SDA[] = {
{p0, I2C_0, (uint32_t) i2c0},
{p2, I2C_1, (uint32_t) i2c1},
{p4, I2C_0, (uint32_t) i2c0},
{p6, I2C_1, (uint32_t) i2c1},
{p8, I2C_0, (uint32_t) i2c0},
{p10, I2C_1, (uint32_t) i2c1},
{p12, I2C_0, (uint32_t) i2c0},
{p14, I2C_1, (uint32_t) i2c1},
{p16, I2C_0, (uint32_t) i2c0},
{p18, I2C_1, (uint32_t) i2c1},
{p20, I2C_0, (uint32_t) i2c0},
{p22, I2C_1, (uint32_t) i2c1},
{p24, I2C_0, (uint32_t) i2c0},
{p26, I2C_1, (uint32_t) i2c1},
{p28, I2C_0, (uint32_t) i2c0},
{NC, NC, 0}
};
const PinMap PinMap_I2C_SCL[] = {
{p1, I2C_0, (uint32_t) i2c0},
{p3, I2C_1, (uint32_t) i2c1},
{p5, I2C_0, (uint32_t) i2c0},
{p7, I2C_1, (uint32_t) i2c1},
{p9, I2C_0, (uint32_t) i2c0},
{p11, I2C_1, (uint32_t) i2c1},
{p13, I2C_0, (uint32_t) i2c0},
{p15, I2C_1, (uint32_t) i2c1},
{p17, I2C_0, (uint32_t) i2c0},
{p19, I2C_1, (uint32_t) i2c1},
{p21, I2C_0, (uint32_t) i2c0},
{p23, I2C_1, (uint32_t) i2c1},
{p25, I2C_0, (uint32_t) i2c0},
{p27, I2C_1, (uint32_t) i2c1},
{p29, I2C_0, (uint32_t) i2c0},
{NC, NC, 0}
};
/************ADC***************/
/* ADC inputs 0-3 are GPIOs 26-29, ADC input 4
* is the onboard temperature sensor.
*/
const PinMap PinMap_ADC[] = {
{ A0, ADC0, 0},
{ A1, ADC0, 1},
{ A2, ADC0, 2},
{ A3, ADC0, 3},
{ ADC_TEMP, ADC0, 4},
{ NC, NC, 0}
};

View File

@ -0,0 +1,72 @@
/*
* mbed Microcontroller Library
* Copyright (c) 2017-2018 Future Electronics
* Copyright (c) 2018-2019 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0
#ifndef MBED_PERIPHERALPINS_H
#define MBED_PERIPHERALPINS_H
#include "PeripheralNames.h"
//*** I2C ***
#if DEVICE_I2C
extern const PinMap PinMap_I2C_SDA[];
extern const PinMap PinMap_I2C_SCL[];
#endif
//*** PWM ***
#if DEVICE_PWMOUT
extern const PinMap PinMap_PWM_OUT[];
#endif
//*** SERIAL ***
#if DEVICE_SERIAL
extern const PinMap PinMap_UART_TX[];
extern const PinMap PinMap_UART_RX[];
extern const PinMap PinMap_UART_RTS[];
extern const PinMap PinMap_UART_CTS[];
#endif
//*** SPI ***
#if DEVICE_SPI
extern const PinMap PinMap_SPI_MISO[];
extern const PinMap PinMap_SPI_SSEL[];
extern const PinMap PinMap_SPI_SCLK[];
extern const PinMap PinMap_SPI_MOSI[];
#endif
//*** ADC ***
#if DEVICE_ANALOGIN
extern const PinMap PinMap_ADC[];
#endif
//*** DAC ***
#if DEVICE_ANALOGOUT
extern const PinMap PinMap_DAC[];
#endif
//*** QSPI ***
#if DEVICE_QSPI
extern const PinMap PinMap_QSPI_SCLK[];
extern const PinMap PinMap_QSPI_SSEL[];
extern const PinMap PinMap_QSPI_DATA0[];
extern const PinMap PinMap_QSPI_DATA1[];
extern const PinMap PinMap_QSPI_DATA2[];
extern const PinMap PinMap_QSPI_DATA3[];
#endif
#endif

View File

@ -0,0 +1,31 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_PORTNAMES_H
#define MBED_PORTNAMES_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
Port0 = 0,
} PortName;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,20 @@
# Copyright (c) 2020 ARM Limited. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
add_library(mbed-raspberry-pi-pico INTERFACE)
target_include_directories(mbed-raspberry-pi-pico
INTERFACE
.
)
target_sources(mbed-raspberry-pi-pico
INTERFACE
board.c
bs2_default_padded_checksummed.S
)
target_link_libraries(mbed-raspberry-pi-pico INTERFACE mbed-rp2040)
# swd target is an alias of raspberry-pi-pico, just with the USB serial port disabled
add_library(mbed-raspberry-pi-pico-swd ALIAS mbed-raspberry-pi-pico)

View File

@ -0,0 +1,70 @@
/* MBED TARGET LIST: RASPBERRY_PI_PICO */
#ifndef MBED_PINNAMES_H
#define MBED_PINNAMES_H
#include "PeripheralNames.h"
#include "boards/pico.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
p0 = 0,
p1 = 1,
p2 = 2,
p3 = 3,
p4 = 4,
p5 = 5,
p6 = 6,
p7 = 7,
p8 = 8,
p9 = 9,
p10 = 10,
p11 = 11,
p12 = 12,
p13 = 13,
p14 = 14,
p15 = 15,
p16 = 16,
p17 = 17,
p18 = 18,
p19 = 19,
p20 = 20,
p21 = 21,
p22 = 22,
p23 = 23,
p24 = 24,
p25 = 25,
p26 = 26,
p27 = 27,
p28 = 28,
p29 = 29,
// ADC internal channels
ADC_TEMP = 0xF0,
ADC_VREF = 0xF1,
#ifndef ARDUINO_ARCH_MBED
A0 = 26,
A1 = 27,
A2 = 28,
A3 = 29,
#endif
CONSOLE_TX = p0,
CONSOLE_RX = p1,
// Not connected
NC = (int)0xFFFFFFFF
} PinName;
#define LED1 p25
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,3 @@
#include "PinNames.h"
uint32_t SystemCoreClock = 125000000;

View File

@ -0,0 +1,23 @@
// Padded and checksummed version of: /ssd/rp2040/pico-examples/build/pico-sdk/src/rp2_common/boot_stage2/bs2_default.bin
.cpu cortex-m0plus
.thumb
.section .boot2, "ax"
.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60
.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61
.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20
.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0
.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0
.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21
.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60
.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21
.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60
.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49
.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20
.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66
.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40
.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00
.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a

View File

@ -0,0 +1,256 @@
/* Based on GCC ARM embedded samples.
Defines the following symbols for use by code:
__exidx_start
__exidx_end
__etext
__data_start__
__preinit_array_start
__preinit_array_end
__init_array_start
__init_array_end
__fini_array_start
__fini_array_end
__data_end__
__bss_start__
__bss_end__
__end__
end
__HeapLimit
__StackLimit
__StackTop
__stack (== StackTop)
*/
#if !defined(MBED_CONF_TARGET_BOOT_STACK_SIZE)
/* This value is normally defined by the tools
to 0x1000 for bare metal and 0x400 for RTOS */
#define MBED_CONF_TARGET_BOOT_STACK_SIZE 0x400
#endif
#if !defined(PICO_FLASH_SIZE_BYTES)
/* This value is normally defined by the tools
to 0x1000 for bare metal and 0x400 for RTOS */
#define PICO_FLASH_SIZE_BYTES 2048k
#endif
MEMORY
{
FLASH(rx) : ORIGIN = 0x10000000, LENGTH = PICO_FLASH_SIZE_BYTES
RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}
ENTRY(_entry_point)
SECTIONS
{
/* Second stage bootloader is prepended to the image. It must be 256 bytes big
and checksummed. It is usually built by the boot_stage2 target
in the Raspberry Pi Pico SDK
*/
.flash_begin : {
__flash_binary_start = .;
} > FLASH
.boot2 : {
__boot2_start__ = .;
KEEP (*(.boot2))
__boot2_end__ = .;
} > FLASH
ASSERT(__boot2_end__ - __boot2_start__ == 256,
"ERROR: Pico second stage bootloader must be 256 bytes in size")
/* The second stage will always enter the image at the start of .text.
The debugger will use the ELF entry point, which is the _entry_point
symbol if present, otherwise defaults to start of .text.
This can be used to transfer control back to the bootrom on debugger
launches only, to perform proper flash setup.
*/
.text : {
__logical_binary_start = .;
KEEP (*(.vectors))
KEEP (*(.binary_info_header))
__binary_info_header_end = .;
KEEP (*(.reset))
KEEP (*(.init))
*(.fini)
/* Pull all c'tors into .text */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* Followed by destructors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.eh_frame*)
. = ALIGN(4);
} > FLASH
.rodata : {
. = ALIGN(4);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
. = ALIGN(4);
} > FLASH
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > FLASH
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > FLASH
__exidx_end = .;
/* Machine inspectable binary information */
. = ALIGN(4);
__binary_info_start = .;
.binary_info :
{
KEEP(*(.binary_info.keep.*))
*(.binary_info.*)
} > FLASH
__binary_info_end = .;
. = ALIGN(4);
/* End of .text-like segments */
__etext = .;
.ram_vector_table (COPY): {
*(.ram_vector_table)
} > RAM
.data : {
__data_start__ = .;
*(vtable)
*(.time_critical*)
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
*(.after_data.*)
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__mutex_array_start = .);
KEEP(*(SORT(.mutex_array.*)))
KEEP(*(.mutex_array))
PROVIDE_HIDDEN (__mutex_array_end = .);
. = ALIGN(4);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(SORT(.preinit_array.*)))
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
*(SORT(.fini_array.*))
*(.fini_array)
PROVIDE_HIDDEN (__fini_array_end = .);
*(.jcr)
. = ALIGN(4);
/* All data end */
__data_end__ = .;
} > RAM AT> FLASH
.uninitialized_data (COPY): {
. = ALIGN(4);
*(.uninitialized_data*)
} > RAM
/* Start and end symbols must be word-aligned */
.scratch_x : {
__scratch_x_start__ = .;
*(.scratch_x.*)
. = ALIGN(4);
__scratch_x_end__ = .;
} > SCRATCH_X AT > FLASH
__scratch_x_source__ = LOADADDR(.scratch_x);
.scratch_y : {
__scratch_y_start__ = .;
*(.scratch_y.*)
. = ALIGN(4);
__scratch_y_end__ = .;
} > SCRATCH_Y AT > FLASH
__scratch_y_source__ = LOADADDR(.scratch_y);
.bss : {
. = ALIGN(4);
__bss_start__ = .;
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
*(COMMON)
. = ALIGN(4);
__bss_end__ = .;
} > RAM
.heap (COPY):
{
__end__ = .;
PROVIDE(end = .);
*(.heap*)
. = ORIGIN(RAM) + LENGTH(RAM) - MBED_CONF_TARGET_BOOT_STACK_SIZE;
__HeapLimit = .;
} > RAM
/* .stack*_dummy section doesn't contains any symbols. It is only
* used for linker to calculate size of stack sections, and assign
* values to stack symbols later
*
* stack1 section may be empty/missing if platform_launch_core1 is not used */
/* by default we put core 0 stack at the end of scratch Y, so that if core 1
* stack is not used then all of SCRATCH_X is free.
*/
.stack1_dummy (COPY):
{
*(.stack1*)
} > SCRATCH_X
.stack_dummy (COPY):
{
*(.stack*)
} > RAM
.flash_end : {
__flash_binary_end = .;
} > FLASH
/* stack limit is poorly named, but historically is maximum heap ptr */
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
__StackLimit = __StackTop - MBED_CONF_TARGET_BOOT_STACK_SIZE;
__StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
__StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
__StackBottom = __StackTop - SIZEOF(.stack_dummy);
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")
ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
/* todo assert on extra code */
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef USBPHYHW_H
#define USBPHYHW_H
#include "mbed.h"
#include "USBPhy.h"
#include "hardware/structs/usb.h"
typedef struct endpoint_info_ {
uint8_t *data; // pointer to user memory to store data to
uint8_t next_pid; // Next DATA0/DATA1 PID to use on this endpoint
uint8_t *dpram; // Pointer into the dpram for endpoint data
} endpoint_info_t;
class USBPhyHw : public USBPhy {
public:
USBPhyHw();
virtual ~USBPhyHw();
virtual void init(USBPhyEvents *events);
virtual void deinit();
virtual bool powered();
virtual void connect();
virtual void disconnect();
virtual void configure();
virtual void unconfigure();
virtual void sof_enable();
virtual void sof_disable();
virtual void set_address(uint8_t address);
virtual void remote_wakeup();
virtual const usb_ep_table_t *endpoint_table();
virtual uint32_t ep0_set_max_packet(uint32_t max_packet);
virtual void ep0_setup_read_result(uint8_t *buffer, uint32_t size);
virtual void ep0_read(uint8_t *data, uint32_t size);
virtual uint32_t ep0_read_result();
virtual void ep0_write(uint8_t *buffer, uint32_t size);
virtual void ep0_stall();
virtual bool endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type);
virtual void endpoint_remove(usb_ep_t endpoint);
virtual void endpoint_stall(usb_ep_t endpoint);
virtual void endpoint_unstall(usb_ep_t endpoint);
virtual bool endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual uint32_t endpoint_read_result(usb_ep_t endpoint);
virtual bool endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size);
virtual void endpoint_abort(usb_ep_t endpoint);
virtual void process();
private:
USBPhyEvents *events;
int new_addr; // Indicates a new device address has been chosen
uint32_t dpram_buffer_free_ptr;
endpoint_info_t ep_info_in [USB_NUM_ENDPOINTS];
endpoint_info_t ep_info_out[USB_NUM_ENDPOINTS];
static void _usbisr(void);
};
#endif

View File

@ -0,0 +1,522 @@
/*
* Copyright (c) 2018-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "USBPhyHw.h"
#ifdef __cplusplus
extern "C" {
#endif
// USB register definitions from pico-sdk
#include "hardware/regs/usb.h"
// USB hardware struct definitions from pico-sdk
#include "hardware/structs/usb.h"
// For interrupt enable and numbers
#include "hardware/irq.h"
// For resetting the USB controller
#include "hardware/resets.h"
#ifdef PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
#include "pico/fix/rp2040_usb_device_enumeration.h"
#endif
#ifdef __cplusplus
}
#endif
// These accessor functions are used to implement bit clear / bit sets through
// an atomic alias (this handles the state where both cores can access a register
// and cause a bit loss through a read-modify-write access)
#define usb_hw_set hw_set_alias(usb_hw)
#define usb_hw_clear hw_clear_alias(usb_hw)
static USBPhyHw *instance;
USBPhy *get_usb_phy()
{
static USBPhyHw usbphy;
return &usbphy;
}
USBPhyHw::USBPhyHw(): events(NULL)
{
}
USBPhyHw::~USBPhyHw()
{
}
void USBPhyHw::init(USBPhyEvents *events)
{
this->events = events;
this->new_addr = 0;
instance = this;
// Disable IRQ
NVIC_DisableIRQ(USBCTRL_IRQn);
// Reset usb controller
reset_block(RESETS_RESET_USBCTRL_BITS);
unreset_block_wait(RESETS_RESET_USBCTRL_BITS);
// Clear any previous state in dpram just in case
memset(usb_dpram, 0, sizeof(*usb_dpram));
// Mux the controller to the onboard usb phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
// Force VBUS detect so the device thinks it is plugged into a host
usb_hw->pwr = USB_USB_PWR_VBUS_DETECT_BITS | USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS;
// Enable the USB controller in device mode.
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
// Enable IRQ
NVIC_SetVector(USBCTRL_IRQn, (uint32_t)&_usbisr);
NVIC_EnableIRQ(USBCTRL_IRQn);
}
void USBPhyHw::deinit()
{
// Disconnect and disable interrupt
disconnect();
NVIC_DisableIRQ(USBCTRL_IRQn);
}
bool USBPhyHw::powered()
{
return true;
}
void USBPhyHw::connect()
{
// Enable interrupts for when a buffer is done, when the bus is reset,
// and when a setup packet is received
usb_hw->inte = USB_INTS_BUFF_STATUS_BITS |
USB_INTS_BUS_RESET_BITS |
USB_INTS_SETUP_REQ_BITS |
USB_INTS_HOST_CONN_DIS_BITS |
USB_INTS_HOST_RESUME_BITS |
USB_INTS_ERROR_RX_OVERFLOW_BITS |
USB_INTS_STALL_BITS;
// Present full speed device by enabling pull up on DP
usb_hw_set->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
// Enable interrupts on EP0, single buffered
usb_hw_set->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
}
void USBPhyHw::disconnect()
{
// Clear all endpoint interrupts and disable interrupts
memset(&usb_dpram->ep_ctrl[0], 0, sizeof(*usb_dpram) - sizeof(usb_dpram->setup_packet));
// TODO - Disable pullup on D+
usb_hw_clear->sie_ctrl = USB_SIE_CTRL_PULLUP_EN_BITS;
}
void USBPhyHw::configure()
{
// Nothing to be done
}
void USBPhyHw::unconfigure()
{
// Also nothing to do here
}
void USBPhyHw::sof_enable()
{
usb_hw_set->inte = USB_INTE_DEV_SOF_BITS;
}
void USBPhyHw::sof_disable()
{
usb_hw_clear->inte = USB_INTE_DEV_SOF_BITS;
}
void USBPhyHw::set_address(uint8_t address)
{
// We can't set the device address here, because we're only half way
// through the control transfer (the OUT bit), we need to wait until the
// IN bit is completed (the status phase) before setting it...
this->new_addr = address;
}
void USBPhyHw::remote_wakeup()
{
// Send remote wakeup over USB lines
// TODO - confirm that the resume doesn't require resetting
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
}
const usb_ep_table_t *USBPhyHw::endpoint_table()
{
static const usb_ep_table_t template_table = {
sizeof(usb_dpram->epx_data),
{
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
{USB_EP_ATTR_ALLOW_ALL | USB_EP_ATTR_DIR_IN_AND_OUT, 1, 0},
}
};
return &template_table;
}
uint32_t USBPhyHw::ep0_set_max_packet(uint32_t max_packet)
{
// Our max packet size is 64 bytes
return 64;
}
// read setup packet
void USBPhyHw::ep0_setup_read_result(uint8_t *buffer, uint32_t size)
{
memcpy(buffer, (void *) usb_dpram->setup_packet, size < 8 ? size : 8);
}
void USBPhyHw::ep0_read(uint8_t *data, uint32_t size)
{
endpoint_info_t * ep = &this->ep_info_out[0];
ep->data = data;
usb_dpram->ep_buf_ctrl[0].out = (size & USB_BUF_CTRL_LEN_MASK) |
USB_BUF_CTRL_AVAIL |
(ep->next_pid ? USB_BUF_CTRL_DATA1_PID : 0);
ep->next_pid = !ep->next_pid;
}
uint32_t USBPhyHw::ep0_read_result()
{
endpoint_info_t * ep = &this->ep_info_out[0];
int buf_ctrl = usb_dpram->ep_buf_ctrl[0].out;
int sz = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
if(buf_ctrl & USB_BUF_CTRL_FULL)
{
if(ep->data != NULL)
{
memcpy(ep->data, ep->dpram, sz);
}
}
else
sz = 0;
return sz;
}
void USBPhyHw::ep0_write(uint8_t *buffer, uint32_t size)
{
endpoint_info_t * ep = &this->ep_info_in[0];
if(buffer != NULL)
memcpy((void *) ep->dpram, buffer, size);
__asm volatile (
"b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1:\n"
: : : "memory");
usb_dpram->ep_buf_ctrl[0].in = size |
USB_BUF_CTRL_FULL |
USB_BUF_CTRL_AVAIL |
(ep->next_pid ? USB_BUF_CTRL_DATA1_PID : 0) ;
ep->next_pid = !ep->next_pid;
}
void USBPhyHw::ep0_stall()
{
usb_hw->ep_stall_arm = 3;
usb_dpram->ep_buf_ctrl[0].in = USB_BUF_CTRL_STALL;
usb_dpram->ep_buf_ctrl[0].out = USB_BUF_CTRL_STALL;
}
bool USBPhyHw::endpoint_add(usb_ep_t endpoint, uint32_t max_packet, usb_ep_type_t type)
{
int ep_num = endpoint & 0x7f;
int in = endpoint >> 7;
io_rw_32 * ep_ctrl = in ? &usb_dpram->ep_ctrl[ep_num - 1].in :
&usb_dpram->ep_ctrl[ep_num - 1].out;
if(this->dpram_buffer_free_ptr + max_packet < sizeof(usb_dpram->epx_data) )
{
endpoint_info_t * ep = in ? &this->ep_info_in[ep_num] : &this->ep_info_out[ep_num];
ep->next_pid = 0;
ep->dpram = &usb_dpram->epx_data[this->dpram_buffer_free_ptr];
*ep_ctrl =
EP_CTRL_ENABLE_BITS |
EP_CTRL_INTERRUPT_PER_BUFFER |
type << EP_CTRL_BUFFER_TYPE_LSB |
EP_CTRL_INTERRUPT_ON_STALL |
(this->dpram_buffer_free_ptr + 0x180);
this->dpram_buffer_free_ptr += (max_packet + 63) & ~63;
}
else
{
return false;
}
return true;
}
void USBPhyHw::endpoint_remove(usb_ep_t endpoint)
{
}
void USBPhyHw::endpoint_stall(usb_ep_t endpoint)
{
int ep_num = endpoint & 0x7f;
usb_dpram->ep_buf_ctrl[ep_num].out |= USB_BUF_CTRL_STALL;
}
void USBPhyHw::endpoint_unstall(usb_ep_t endpoint)
{
int ep_num = endpoint & 0x7f;
usb_dpram->ep_buf_ctrl[ep_num].out &= ~USB_BUF_CTRL_STALL;
}
bool USBPhyHw::endpoint_read(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
int ep_num = endpoint & 0x7f;
endpoint_info_t * ep = &this->ep_info_out[ep_num];
ep->data = data;
usb_dpram->ep_buf_ctrl[ep_num].out =
(size & USB_BUF_CTRL_LEN_MASK) |
USB_BUF_CTRL_AVAIL |
(ep->next_pid ? USB_BUF_CTRL_DATA1_PID : 0);
ep->next_pid = !ep->next_pid;
return true;
}
uint32_t USBPhyHw::endpoint_read_result(usb_ep_t endpoint)
{
int ep_num = endpoint & 0x7f;
endpoint_info_t * ep = &this->ep_info_out[ep_num];
int buf_ctrl = usb_dpram->ep_buf_ctrl[ep_num].out;
int sz = buf_ctrl & USB_BUF_CTRL_LEN_MASK;
if(buf_ctrl & USB_BUF_CTRL_FULL)
{
if(ep->data != NULL)
{
memcpy(ep->data, ep->dpram, sz);
}
}
else
sz = 0;
return sz;
}
bool USBPhyHw::endpoint_write(usb_ep_t endpoint, uint8_t *data, uint32_t size)
{
int ep_num = endpoint & 0x7f;
endpoint_info_t * ep = &this->ep_info_in[ep_num];
if(data != NULL)
memcpy(ep->dpram, data, size);
__asm volatile (
"b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1: b 1f\n"
"1:\n"
: : : "memory");
usb_dpram->ep_buf_ctrl[ep_num].in = size |
USB_BUF_CTRL_FULL |
USB_BUF_CTRL_AVAIL |
(ep->next_pid ? USB_BUF_CTRL_DATA1_PID : 0);
ep->next_pid = !ep->next_pid;
return true;
}
void USBPhyHw::endpoint_abort(usb_ep_t endpoint)
{
}
void USBPhyHw::process()
{
again:
// reset interrupt
if (usb_hw->ints & USB_INTS_BUS_RESET_BITS) {
// Clear the device address
usb_hw->dev_addr_ctrl = 0;
// Reset all endpoint buffers and controls (leave SETUP packet)
memset(&usb_dpram->ep_ctrl[0], 0, sizeof(*usb_dpram) - sizeof(usb_dpram->setup_packet));
this->dpram_buffer_free_ptr = 0;
// Clear the bus reset
usb_hw->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
// This is required, but having trouble working out how to link in
#ifdef PICO_RP2040_USB_DEVICE_ENUMERATION_FIX
rp2040_usb_device_enumeration_fix();
#endif
// reset bus for USBDevice layer
events->reset();
// Re-enable interrupt
NVIC_ClearPendingIRQ(USBCTRL_IRQn);
NVIC_EnableIRQ(USBCTRL_IRQn);
return;
}
// Received SETUP packet
if(usb_hw->ints & USB_INTS_SETUP_REQ_BITS)
{
this->ep_info_in [0].next_pid = 1;
this->ep_info_in [0].dpram = usb_dpram->ep0_buf_a;
this->ep_info_out[0].next_pid = 1;
this->ep_info_out[0].dpram = usb_dpram->ep0_buf_a;
events->ep0_setup();
// Clear interrupt
usb_hw->sie_status = USB_SIE_STATUS_SETUP_REC_BITS;
}
// BUFF_STATUS bits have changed
if(usb_hw->ints & USB_INTS_BUFF_STATUS_BITS)
{
uint32_t buff_status = usb_hw->buf_status;
int i;
// EP0 IN
if(buff_status & 1)
{
// Clear this bit
usb_hw->buf_status = 1;
events->ep0_in();
// If we got a new address in the (OUT) DATA stage of a control
// transfer, then set it here after having received the (IN) STATUS
// stage
if(this->new_addr != 0)
{
usb_hw->dev_addr_ctrl = this->new_addr;
this->new_addr = 0;
}
}
// EP0 OUT
if(buff_status & 2)
{
// Clear this bit
usb_hw->buf_status = 2;
events->ep0_out();
}
// Go through the rest of the bits
for(i = 2; i < 32; i++)
{
if(buff_status & (1 << i))
{
// Clear the bit
usb_hw->buf_status = 1 << i;
if(i & 1)
{
events->out(i / 2);
}
else
{
events->in(0x80 | (i / 2));
}
}
}
}
// sof interrupt
if (usb_hw->ints & USB_INTR_DEV_SOF_BITS) {
// SOF event, read frame number
events->sof(usb_hw->sof_rd);
}
// Suspend / Resume not tested, not found a way of triggering suspend
if(usb_hw->ints & USB_INTS_DEV_RESUME_FROM_HOST_BITS)
{
// Will clear the interrupt
usb_hw_set->sie_ctrl = USB_SIE_CTRL_RESUME_BITS;
events->suspend(false);
}
if(usb_hw->ints & USB_INTS_DEV_SUSPEND_BITS)
{
usb_hw->sie_status = USB_SIE_STATUS_SUSPENDED_BITS;
events->suspend(true);
}
// If any interrupts are still set (except BUFSTATUS), then hang for analysis
// This is only for debug while developing the driver
if(usb_hw->ints & ~16)
{
volatile int ints = usb_hw->ints;
volatile int going = 1;
goto again;
//while(ints && going);
}
// Re-enable interrupt
NVIC_ClearPendingIRQ(USBCTRL_IRQn);
NVIC_EnableIRQ(USBCTRL_IRQn);
}
void USBPhyHw::_usbisr(void)
{
NVIC_DisableIRQ(USBCTRL_IRQn);
instance->events->start_process();
}

View File

@ -0,0 +1,57 @@
#include "mbed_assert.h"
#include "analogin_api.h"
#include "hardware/adc.h"
#include "pinmap.h"
#include "PeripheralPins.h"
static float const ADC_VREF_VOLTAGE = 3.3f; /* 3.3V */
static uint16_t const ADC_RESOLUTION_BITS = 12;
static float const ADC_CONVERSION_FACTOR = ADC_VREF_VOLTAGE / (1 << 16);
void analogin_init(analogin_t *obj, PinName pin)
{
/* Make sure pin is an analog pin we can use for ADC */
MBED_ASSERT((ADCName)pinmap_peripheral(pin, PinMap_ADC) != (ADCName)NC);
static bool is_adc_initialized = false;
/* Initialize the ADC the first time it is being used,
* but don't reinitialize it again afterwards.
*/
if (!is_adc_initialized)
{
adc_init();
is_adc_initialized = true;
}
/* Lookup the corresponding ADC channel for a given pin. */
obj->channel = pinmap_find_function(pin, PinMap_ADC);
/* Make sure GPIO is high-impedance, no pullups etc. */
adc_gpio_init(pin);
/* Check if the ADC channel we just configured belongs to the
* temperature sensor. If that's the case, enable the temperature
* sensor.
*/
if (pin == ADC_TEMP)
adc_set_temp_sensor_enabled(true);
}
float analogin_read(analogin_t *obj)
{
/* Read the raw 12-Bit value from the ADC. */
float analog_in_raw = (float)analogin_read_u16(obj);
/* Convert it to a voltage value. */
return (analog_in_raw * ADC_CONVERSION_FACTOR);
}
uint16_t analogin_read_u16(analogin_t *obj)
{
/* Select the desired ADC input channel. */
adc_select_input(obj->channel);
/* Read the 16-Bit ADC value. */
return adc_read() << (16 - ADC_RESOLUTION_BITS);
}
const PinMap *analogin_pinmap()
{
return PinMap_ADC;
}

View File

@ -0,0 +1,60 @@
#ifndef __RP2040_H__
#define __RP2040_H__
typedef enum IRQn
{
/* ToDo: use this Cortex interrupt numbers if your device is a CORTEX-M0 device */
NonMaskableInt_IRQn = -14, /*!< 2 Cortex-M0 Non Maskable Interrupt */
HardFault_IRQn = -13, /*!< 3 Cortex-M0 Hard Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M0 SV Call Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M0 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M0 System Tick Interrupt */
/* ---------------------- RP2040 Specific Interrupt Numbers ------------------ */
TIMER_IRQ_0n = 0, /*!< */
TIMER_IRQ_1n = 1, /*!< */
TIMER_IRQ_2n = 2, /*!< */
TIMER_IRQ_3n = 3, /*!< */
PWM_IRQ_WRAPn = 4, /*!< */
USBCTRL_IRQn = 5, /*!< */
XIP_IRQn = 6, /*!< */
PIO0_IRQ_0n = 7, /*!< */
PIO0_IRQ_1n = 8, /*!< */
PIO1_IRQ_0n = 9, /*!< */
PIO1_IRQ_1n = 10, /*!< */
DMA_IRQ_0n = 11, /*!< */
DMA_IRQ_1n = 12, /*!< */
IO_IRQ_BANK0n = 13, /*!< */
IO_IRQ_QSPIn = 14, /*!< */
SIO_IRQ_PROC0n = 15, /*!< */
SIO_IRQ_PROC1n = 16, /*!< */
CLOCKS_IRQn = 17, /*!< */
SPI0_IRQn = 18, /*!< */
SPI1_IRQn = 19, /*!< */
UART0_IRQn = 20, /*!< */
UART1_IRQn = 21, /*!< */
ADC_IRQ_FIFOn = 22, /*!< */
I2C0_IRQn = 23, /*!< */
I2C1_IRQn = 24, /*!< */
RTC_IRQn = 25, /*!< */
} IRQn_Type;
/*
* ==========================================================================
* ----------- Processor and Core Peripheral Section ------------------------
* ==========================================================================
*/
/* Configuration of the Cortex-M0+ Processor and Core Peripherals */
#define __CM0PLUS_REV 0x0000 /* Core revision r0p0 */
#define __MPU_PRESENT 0 /* MPU present or not */
#define __VTOR_PRESENT 1 /* VTOR present or not */
#define __NVIC_PRIO_BITS 2 /* Number of Bits used for Priority Levels */
#define __Vendor_SysTickConfig 0 /* Set to 1 if different SysTick Config is used */
#include "core_cm0plus.h"
#include "cmsis_nvic.h"
extern uint32_t SystemCoreClock; // System Clock Frequency (Core Clock)
#endif

View File

@ -0,0 +1,40 @@
/* mbed Microcontroller Library
* SPDX-License-Identifier: BSD-3-Clause
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2016-2020 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
#ifndef MBED_CMSIS_NVIC_H
#define MBED_CMSIS_NVIC_H
#if !defined(MBED_ROM_START)
#define MBED_ROM_START 0x10000000
#endif
#if !defined(MBED_ROM_SIZE)
#if defined(PICO_FLASH_SIZE_BYTES)
#define MBED_ROM_SIZE PICO_FLASH_SIZE_BYTES
#else
#define MBED_ROM_SIZE (2048*1024)
#endif
#endif
#if !defined(MBED_RAM_START)
#define MBED_RAM_START 0x20000000
#endif
#if !defined(MBED_RAM_SIZE)
#define MBED_RAM_SIZE (256*1024)
#endif
#endif

View File

@ -0,0 +1,38 @@
/* mbed Microcontroller Library
*******************************************************************************
* Copyright (c) 2019, STMicroelectronics
* SPDX-License-Identifier: Apache-2.0
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************
*/
#ifndef MBED_DEVICE_H
#define MBED_DEVICE_H
#include "objects.h"
#include "us_ticker_defines.h"
#endif

View File

@ -0,0 +1,125 @@
/******************************************************************************
* INCLUDE
******************************************************************************/
#if DEVICE_FLASH
#include "mbed_assert.h"
#include "mbed_critical.h"
#include "hal/flash_api.h"
#include "hardware/flash.h"
/******************************************************************************
* CONSTANT
******************************************************************************/
/******************************************************************************
* FUNCTION DEFINITION
******************************************************************************/
int32_t flash_init(flash_t *obj)
{
(void)(obj);
return 0;
}
int32_t flash_free(flash_t *obj)
{
(void)(obj);
return 0;
}
int32_t flash_erase_sector(flash_t *obj, uint32_t address)
{
(void)(obj);
address = address - XIP_BASE;
if ((address % FLASH_SECTOR_SIZE) != 0) {
return -1;
}
core_util_critical_section_enter();
flash_range_erase(address, FLASH_SECTOR_SIZE);
core_util_critical_section_exit();
return 0;
}
int32_t flash_read(flash_t *obj, uint32_t address, uint8_t *data, uint32_t size)
{
(void)(obj);
const uint8_t *flash_target_contents = (const uint8_t *) (address);
for (uint32_t i = 0; i < size; i++) {
data[i] = flash_target_contents[i];
}
return 0;
}
int32_t flash_program_page(flash_t *obj, uint32_t address, const uint8_t *data, uint32_t size)
{
(void)(obj);
address = address - XIP_BASE;
if ((address % FLASH_PAGE_SIZE) != 0) {
return -1;
}
size_t pages = size/FLASH_PAGE_SIZE;
if (size%FLASH_PAGE_SIZE != 0) {
pages += 1;
}
core_util_critical_section_enter();
flash_range_program(address, data, FLASH_PAGE_SIZE * pages);
core_util_critical_section_exit();
return 0;
}
uint32_t flash_get_size(const flash_t *obj)
{
(void)(obj);
return PICO_FLASH_SIZE_BYTES;
}
uint32_t flash_get_sector_size(const flash_t *obj, uint32_t address)
{
if (address < flash_get_start_address(obj) || address >= flash_get_start_address(obj) + flash_get_size(obj)) {
return MBED_FLASH_INVALID_SIZE;
}
return FLASH_SECTOR_SIZE;
}
uint32_t flash_get_page_size(const flash_t *obj)
{
(void)(obj);
return FLASH_PAGE_SIZE;
}
uint32_t flash_get_start_address(const flash_t *obj)
{
(void)obj;
return XIP_BASE;
}
uint8_t flash_get_erase_value(const flash_t *obj)
{
(void)obj;
return 0xFF;
}
#endif

View File

@ -0,0 +1,143 @@
#include "mbed_assert.h"
#include "hal/gpio_api.h"
#include "gpio_irq_api.h"
#include "pinmap.h"
#include "mbed_error.h"
#define GPIO_PIN_COUNT 30
static gpio_irq_handler m_irq_handler;
static uint32_t m_channel_ids[GPIO_PIN_COUNT] = {0};
static uint32_t m_pico_events[GPIO_PIN_COUNT] = {0};
void gpio_write(gpio_t *obj, int value)
{
gpio_put(obj->pin, value);
}
int gpio_read(gpio_t *obj)
{
return gpio_get(obj->pin);
}
void gpio_init(gpio_t *obj, PinName pin)
{
obj->pin = pin;
if (pin == (PinName)NC) {
return;
}
_gpio_init(obj->pin);
}
static uint32_t gpio_convert_event(gpio_irq_event event)
{
uint32_t irq_event = 0;
if (event == IRQ_RISE) {
irq_event = GPIO_IRQ_EDGE_RISE;
} else if (event == IRQ_FALL) {
irq_event = GPIO_IRQ_EDGE_FALL;
}
return irq_event;
}
void gpio_mode(gpio_t *obj, PinMode mode)
{
MBED_ASSERT(obj->pin != (PinName)NC);
gpio_set_pulls(obj->pin, mode == PullUp, mode == PullDown);
}
void gpio_dir(gpio_t *obj, PinDirection direction)
{
MBED_ASSERT(obj->pin != (PinName)NC);
obj->direction = direction;
if (direction == PIN_OUTPUT) {
gpio_set_dir(obj->pin, GPIO_OUT);
}
if (direction == PIN_INPUT) {
gpio_set_dir(obj->pin, GPIO_IN);
}
}
int gpio_is_connected(const gpio_t *obj) {
return (obj->pin == NC ? 0 : 1);
}
/***********
GPIO IRQ
***********/
#if DEVICE_INTERRUPTIN
static void _gpio_irq(uint gpio, uint32_t events)
{
gpio_irq_event ev;
if (events == GPIO_IRQ_EDGE_RISE) {
ev = IRQ_RISE;
} else if (events == GPIO_IRQ_EDGE_FALL) {
ev = IRQ_FALL;
} else {
ev = IRQ_NONE;
}
m_irq_handler(m_channel_ids[gpio], ev);
}
int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uintptr_t context)
{
if (pin == NC) {
return -1;
}
MBED_ASSERT((uint32_t)pin < GPIO_PIN_COUNT);
m_channel_ids[pin] = context;
m_irq_handler = handler;
obj->irq_n = IO_IRQ_BANK0;
obj->pin = pin;
obj->irq_index = context;
return 0;
}
void gpio_irq_free(gpio_irq_t *obj)
{
gpio_irq_disable(obj);
obj->irq_n = 0;
obj->pin = 0;
obj->irq_index = 0;
}
void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
uint32_t irq_event = gpio_convert_event(event);
if (enable) {
m_pico_events[obj->pin] |= irq_event;
obj->event = irq_event;
gpio_irq_enable(obj);
}
}
void gpio_irq_enable(gpio_irq_t *obj)
{
gpio_set_irq_enabled_with_callback(obj->pin, obj->event, true, _gpio_irq);
}
void gpio_irq_disable(gpio_irq_t *obj)
{
gpio_set_irq_enabled(obj->pin, m_pico_events[obj->pin], false);
obj->event = 0;
m_pico_events[obj->pin] = 0;
}
#endif

View File

@ -0,0 +1,238 @@
/******************************************************************************
* INCLUDE
******************************************************************************/
#include "mbed_assert.h"
#include "i2c_api.h"
#include "pinmap.h"
#include "PeripheralPins.h"
#include "objects.h"
#include "stdio.h"
/******************************************************************************
* DEFINE
******************************************************************************/
#if 0
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
#else
#define DEBUG_PRINTF(...)
#endif
#define NoData 0 // the slave has not been addressed
#define ReadAddressed 1 // the master has requested a read from this slave (slave = transmitter)
#define WriteGeneral 2 // the master is writing to all slave
#define WriteAddressed 3 // the master is writing to this slave (slave = receiver)
/******************************************************************************
* CONST
******************************************************************************/
static unsigned int const DEFAULT_I2C_BAUDRATE = 100 * 1000; /* 100 kHz */
/******************************************************************************
* FUNCTION DEFINITION
******************************************************************************/
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
/* Verify if both pins belong to the same I2C peripheral. */
I2CName const i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA);
I2CName const i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL);
MBED_ASSERT(i2c_sda == i2c_scl);
#if DEVICE_I2CSLAVE
/** was_slave is used to decide which driver call we need
* to use when uninitializing a given instance
*/
obj->was_slave = false;
obj->is_slave = false;
obj->slave_addr = 0;
#endif
/* Obtain the pointer to the I2C hardware instance. */
obj->dev = (i2c_inst_t *)pinmap_function(sda, PinMap_I2C_SDA);
//obj->baudrate = DEFAULT_I2C_BAUDRATE;
//Call this function because if we are configuring a slave, we don't have to set the frequency
//i2c_frequency(obj->dev, DEFAULT_I2C_BAUDRATE);
/* Initialize the I2C module. */
_i2c_init(obj->dev, DEFAULT_I2C_BAUDRATE);
/* Configure GPIO for I2C as alternate function. */
gpio_set_function(sda, GPIO_FUNC_I2C);
gpio_set_function(scl, GPIO_FUNC_I2C);
/* Enable pull-ups for I2C pins. */
gpio_pull_up(sda);
gpio_pull_up(scl);
}
void i2c_frequency(i2c_t *obj, int hz)
{
DEBUG_PRINTF("obj->is_slave: %d\r\n", obj->is_slave);
#if DEVICE_I2CSLAVE
/* Slaves automatically get frequency from master */
if(obj->is_slave) {
return;
}
#endif
obj->baudrate = i2c_set_baudrate(obj->dev, hz);
}
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
{
int const bytes_read = i2c_read_blocking(obj->dev,
(uint8_t)(address >> 1),
(uint8_t *)data,
(size_t)length,
/* nostop = */(stop == 0));
if (bytes_read < 0)
return I2C_ERROR_NO_SLAVE;
else
return bytes_read;
}
int i2c_write(i2c_t *obj, int address, const char *data, int length, int stop)
{
if (length == 0) {
// From pico-sdk:
// static int i2c_write_blocking_internal(i2c_inst_t *i2c, uint8_t addr, const uint8_t *src, size_t len, bool nostop,
// Synopsys hw accepts start/stop flags alongside data items in the same
// FIFO word, so no 0 byte transfers.
// invalid_params_if(I2C, len == 0);
length = 1;
}
int const bytes_written = i2c_write_blocking(obj->dev,
address >> 1,
(const uint8_t *)data,
(size_t)length,
/* nostop = */(stop == 0));
if (bytes_written < 0)
return I2C_ERROR_NO_SLAVE;
else
return bytes_written;
}
void i2c_reset(i2c_t *obj)
{
i2c_deinit(obj->dev);
_i2c_init(obj->dev, obj->baudrate);
}
const PinMap *i2c_master_sda_pinmap()
{
return PinMap_I2C_SDA;
}
const PinMap *i2c_master_scl_pinmap()
{
return PinMap_I2C_SCL;
}
const PinMap *i2c_slave_sda_pinmap()
{
return PinMap_I2C_SDA;
}
const PinMap *i2c_slave_scl_pinmap()
{
return PinMap_I2C_SCL;
}
int i2c_stop(i2c_t *obj)
{
}
#if DEVICE_I2CSLAVE
/** Configure I2C as slave or master.
* @param obj The I2C object
* @param enable_slave Enable i2c hardware so you can receive events with ::i2c_slave_receive
* @return non-zero if a value is available
*/
void i2c_slave_mode(i2c_t *obj, int enable_slave)
{
DEBUG_PRINTF("i2c_slave_mode: %p, %d\r\n", obj, enable_slave);
obj->is_slave = enable_slave;
}
/** Check to see if the I2C slave has been addressed.
* @param obj The I2C object
* @return The status - 1 - read addressed, 2 - write to all slaves,
* 3 write addressed, 0 - the slave has not been addressed
*/
int i2c_slave_receive(i2c_t *obj)
{
int retValue = NoData;
int rd_req = (obj->dev->hw->raw_intr_stat & I2C_IC_RAW_INTR_STAT_RD_REQ_BITS) >> 5;
if (rd_req == I2C_IC_RAW_INTR_STAT_RD_REQ_VALUE_ACTIVE) {
DEBUG_PRINTF("Read addressed\r\n");
return ReadAddressed;
}
int wr_req = (obj->dev->hw->status & I2C_IC_STATUS_RFNE_BITS) >> 3;
if (wr_req == I2C_IC_STATUS_RFNE_VALUE_NOT_EMPTY) {
DEBUG_PRINTF("Write addressed\r\n");
return WriteAddressed;
}
return (retValue);
}
/** Configure I2C as slave or master.
* @param obj The I2C objecti2c_get_read_availableread
* @return non-zero if a value is available
*/
int i2c_slave_read(i2c_t *obj, char *data, int length)
{
size_t read_len = i2c_read_raw_blocking(obj->dev, (uint8_t *)data, length);
DEBUG_PRINTF("i2c_slave read %d bytes\r\n", read_len);
return read_len;
}
/** Configure I2C as slave or master.
* @param obj The I2C object
* @param data The buffer for sending
* @param length Number of bytes to write
* @return non-zero if a value is available
*/
int i2c_slave_write(i2c_t *obj, const char *data, int length)
{
DEBUG_PRINTF("i2c_slave_write\r\n");
i2c_write_raw_blocking(obj->dev, (const uint8_t *)data, (size_t)length);
//Clear interrupt
int clear_read_req = i2c_get_hw(obj->dev)->clr_rd_req;
DEBUG_PRINTF("clear_read_req: %d\n", clear_read_req);
return length;
}
/** Configure I2C address.
* @param obj The I2C object
* @param idx Currently not used
* @param address The address to be set
* @param mask Currently not used
*/
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
{
if (obj->is_slave) {
DEBUG_PRINTF("i2c_slave_address: %p, %d, %d, %d\r\n", obj, idx, address, mask);
obj->slave_addr = (uint8_t)(address >> 1);
i2c_set_slave_mode(obj->dev, true, obj->slave_addr);
}
}
#endif // DEVICE_I2CSLAVE

View File

@ -0,0 +1,114 @@
/* mbed Microcontroller Library
* Copyright (c) 2015 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lp_ticker_api.h"
#include "hardware/rtc.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
#include "hardware/clocks.h"
#include "time.h"
#include "platform/mbed_critical.h"
#define RTC_COUNTER_BITS 32u
#define RTC_FREQ 1u
#if DEVICE_LPTICKER
/* LP ticker counts with 1Hz resolution */
const ticker_info_t* lp_ticker_get_info()
{
static const ticker_info_t info = {
RTC_FREQ,
RTC_COUNTER_BITS
};
return &info;
}
static datetime_t now = {
.year = 0,
.month = 1,
.day = 1,
.dotw = 0,
.hour = 0,
.min = 0,
.sec = 0
};
static datetime_t* epoch_to_datetime(timestamp_t epoch, datetime_t* date) {
return NULL;
}
static timestamp_t datetime_to_epoch(datetime_t* date) {
struct tm t;
time_t t_of_day;
t.tm_year = date->year; // Year
t.tm_mon = date->month + 1; // Month, where 0 = jan
t.tm_mday = date->day; // Day of the month
t.tm_hour = date->hour;
t.tm_min = date->min;
t.tm_sec = date->sec;
t_of_day = mktime(&t);
return (long)t_of_day;
}
void lp_ticker_init(void)
{
if (rtc_running()) {
// Populates now struct with existing data
lp_ticker_read();
return;
}
rtc_init();
rtc_set_datetime(&now);
}
void lp_ticker_free(void)
{
}
uint32_t lp_ticker_read()
{
rtc_get_datetime(&now);
return 0; //datetime_to_epoch(&now);
}
void lp_ticker_set_interrupt(timestamp_t timestamp)
{
timestamp_t target_ts = lp_ticker_read() + timestamp;
datetime_t target;
epoch_to_datetime(target_ts, &target);
rtc_set_alarm(&target, NULL);
}
void lp_ticker_fire_interrupt(void)
{
rtc_get_datetime(&now);
rtc_set_alarm(&now, NULL);
}
void lp_ticker_disable_interrupt(void)
{
rtc_disable_alarm();
}
void lp_ticker_clear_interrupt(void)
{
}
#endif // DEVICE_LPTICKER

View File

@ -0,0 +1,108 @@
#include "cmsis.h"
#include "objects.h"
#include "platform/mbed_error.h"
#include "mbed_interface.h"
#include "hardware/resets.h"
#include "hardware/clocks.h"
#include "hardware/sync.h"
#include "pico/binary_info.h"
#include <stdarg.h>
int mbed_sdk_inited = 0;
uint32_t __attribute__((section(".ram_vector_table"))) ram_vector_table[48];
void mbed_sdk_init()
{
// This function is adapted from the Pico SDK's runtime_init()
// Reset all peripherals to put system into a known state,
// - except for QSPI pads and the XIP IO bank, as this is fatal if running from flash
// - and the PLLs, as this is fatal if clock muxing has not been reset on this boot
reset_block(~(
RESETS_RESET_IO_QSPI_BITS |
RESETS_RESET_PADS_QSPI_BITS |
RESETS_RESET_PLL_USB_BITS |
RESETS_RESET_PLL_SYS_BITS
));
// Remove reset from peripherals which are clocked only by clk_sys and
// clk_ref. Other peripherals stay in reset until we've configured clocks.
unreset_block_wait(RESETS_RESET_BITS & ~(
RESETS_RESET_ADC_BITS |
RESETS_RESET_RTC_BITS |
RESETS_RESET_SPI0_BITS |
RESETS_RESET_SPI1_BITS |
RESETS_RESET_UART0_BITS |
RESETS_RESET_UART1_BITS |
RESETS_RESET_USBCTRL_BITS
));
// After calling preinit we have enough runtime to do the exciting maths
// in clocks_init
clocks_init();
// Peripheral clocks should now all be running
unreset_block_wait(RESETS_RESET_BITS);
#if !PICO_IE_26_29_UNCHANGED_ON_RESET
// after resetting BANK0 we should disable IE on 26-29
hw_clear_alias(padsbank0_hw)->io[26] = hw_clear_alias(padsbank0_hw)->io[27] =
hw_clear_alias(padsbank0_hw)->io[28] = hw_clear_alias(padsbank0_hw)->io[29] = PADS_BANK0_GPIO0_IE_BITS;
#endif
#if !(PICO_NO_RAM_VECTOR_TABLE || PICO_NO_FLASH)
__builtin_memcpy(ram_vector_table, (uint32_t *) SCB->VTOR, sizeof(ram_vector_table));
SCB->VTOR = (intptr_t) ram_vector_table;
#endif
#ifndef NDEBUG
uint32_t xpsr;
__asm volatile ("mrs %0, XPSR" : "=r" (xpsr)::);
if (xpsr & 0xffu) {
// crap; started in exception handler
__asm ("bkpt #0");
}
#endif
spin_locks_reset();
mbed_sdk_inited = 1;
}
// Note we put at most 4 pieces of binary info in the reset section because that's how much spare space we had
// (picked the most common ones)... if there is a link failure because of .reset section overflow then move
// more out.
#define reset_section_attr __attribute__((section(".reset")))
bi_decl_with_attr(bi_program_name("Mbed OS CE Program"), reset_section_attr)
// Pico SDK panic handlers
void __attribute__((noreturn)) panic_unsupported() {
panic("not supported");
}
void hard_assertion_failure(void) {
panic("Hard assert");
}
void __attribute__((noreturn)) __printflike(1, 0) panic(const char *fmt, ...) {
mbed_error_printf("\n*** PANIC ***\n");
// Try and format the message to the stack
const size_t MSG_BUF_LEN = 256;
char msgBuf[MSG_BUF_LEN];
va_list args;
va_start(args, fmt);
snprintf(msgBuf, MSG_BUF_LEN, fmt, args);
va_end(args);
MBED_ERROR(
MBED_MAKE_ERROR(
MBED_MODULE_HAL,
MBED_ERROR_CODE_UNKNOWN
),
msgBuf
);
}

View File

@ -0,0 +1,138 @@
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_OBJECTS_H
#define MBED_OBJECTS_H
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wtype-limits"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#pragma GCC diagnostic ignored "-Wunused-value"
#ifdef __cplusplus
#include <stdint.h>
#include <cstddef>
extern "C" {
#endif
#include "pico.h"
#include "pico/platform.h"
#include "pico/types.h"
#include "pico/assert.h"
#include "pico/time.h"
#include "pico/types.h"
#include "hardware/pwm.h"
#include "hardware/adc.h"
#include "hardware/resets.h"
#include "hardware/timer.h"
#include "hardware/uart.h"
#include "hardware/spi.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"
#include "hardware/regs/addressmap.h"
#ifdef __cplusplus
}
#endif
#pragma GCC diagnostic pop
#include "cmsis.h"
#include <stdbool.h>
#include "PortNames.h"
#include "PeripheralNames.h"
#include "PinNames.h"
#ifdef __cplusplus
extern "C" {
#endif
struct gpio_s {
PinName pin;
PinDirection direction;
};
struct gpio_irq_s {
IRQn_Type irq_n;
uint32_t irq_index;
uint32_t event;
PinName pin;
};
struct port_s {
PortName port;
uint32_t mask;
PinDirection direction;
__IO uint32_t *reg_in;
__IO uint32_t *reg_out;
};
/* common objects */
struct analogin_s {
uint8_t channel;
};
struct serial_s {
uart_inst_t * dev;
PinName pin_rx;
PinName pin_tx;
PinName pin_rts;
PinName pin_cts;
uint32_t baud;
// Current states of the IRQ enables for Tx and Rx
bool txIrqEnabled;
bool rxIrqEnabled;
};
struct i2c_s {
i2c_inst_t * dev;
unsigned int baudrate;
#if DEVICE_I2CSLAVE
bool was_slave;
bool is_slave;
uint8_t slave_addr;
#endif
};
struct spi_s {
spi_inst_t * dev;
};
struct pwmout_s {
PinName pin;
uint8_t slice;
uint8_t channel;
uint16_t period;
float percent;
pwm_config cfg;
};
struct flash_s {
/* nothing to be stored for now */
uint32_t dummy;
};
typedef struct gpio_s gpio_t;
typedef struct serial_s serial_t;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,89 @@
cmake_policy(SET CMP0079 NEW) # allow inserting of dependencies into our INTERFACE libraries
set(PICO_PLATFORM_CMAKE_FILE "" CACHE INTERNAL "")
set(PICO_DOXYGEN_PATHS "" CACHE INTERNAL "") # generated each time
if (NOT PICO_PLATFORM_CMAKE_FILE)
set(PICO_PLATFORM_CMAKE_FILE ${CMAKE_CURRENT_LIST_DIR}/${PICO_PLATFORM}.cmake CACHE INTERNAL "")
endif ()
if (NOT EXISTS "${PICO_PLATFORM_CMAKE_FILE}")
message(FATAL_ERROR "${PICO_PLATFORM_CMAKE_FILE} does not exist. \
Either specify a valid PICO_PLATFORM (or PICO_PLATFORM_CMAKE_FILE).")
endif ()
include(${CMAKE_CURRENT_LIST_DIR}/board_setup.cmake)
# todo add option to disable skip flag
function(pico_add_subdirectory subdir)
string(TOUPPER ${subdir} subdir_upper)
set(replace_flag SKIP_${subdir_upper})
if (NOT ${replace_flag})
add_subdirectory(${subdir})
else ()
message("Not including ${subdir} because ${replace_flag} defined.")
endif ()
endfunction()
function(pico_wrap_function TARGET FUNCNAME)
target_link_options(${TARGET} INTERFACE "LINKER:--wrap=${FUNCNAME}")
endfunction()
function(pico_add_map_output TARGET)
get_target_property(target_type ${TARGET} TYPE)
if ("EXECUTABLE" STREQUAL "${target_type}")
target_link_options(${TARGET} PRIVATE "LINKER:-Map=$<TARGET_PROPERTY:NAME>${CMAKE_EXECUTABLE_SUFFIX}.map")
else ()
target_link_options(${TARGET} INTERFACE "LINKER:-Map=$<TARGET_PROPERTY:NAME>${CMAKE_EXECUTABLE_SUFFIX}.map")
endif ()
endfunction()
macro(pico_simple_hardware_target NAME)
pico_simple_hardware_headers_target(${NAME})
pico_simple_hardware_impl_target(${NAME})
endmacro()
macro(pico_simple_hardware_headers_target NAME)
if (NOT TARGET hardware_${NAME}_headers)
add_library(hardware_${NAME}_headers INTERFACE)
target_include_directories(hardware_${NAME}_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(hardware_${NAME}_headers INTERFACE pico_base_headers)
if (NOT PICO_NO_HARDWARE)
target_link_libraries(hardware_${NAME}_headers INTERFACE hardware_structs hardware_claim)
endif()
endif()
endmacro()
macro(pico_simple_hardware_headers_only_target NAME)
if (NOT TARGET hardware_${NAME})
add_library(hardware_${NAME} INTERFACE)
target_include_directories(hardware_${NAME} INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(hardware_${NAME} INTERFACE pico_base_headers)
if (NOT PICO_NO_HARDWARE)
target_link_libraries(hardware_${NAME} INTERFACE hardware_structs)
endif()
endif()
endmacro()
macro(pico_simple_hardware_impl_target NAME)
if (NOT TARGET hardware_${NAME})
add_library(hardware_${NAME} INTERFACE)
target_sources(hardware_${NAME} INTERFACE
${CMAKE_CURRENT_LIST_DIR}/${NAME}.c
)
target_link_libraries(hardware_${NAME} INTERFACE hardware_${NAME}_headers pico_platform)
endif()
endmacro()
function(pico_add_doxygen SOURCE_DIR)
set(PICO_DOXYGEN_PATHS "${PICO_DOXYGEN_PATHS} ${SOURCE_DIR}" CACHE INTERNAL "")
endfunction()
function(pico_add_doxygen_exclude SOURCE_DIR)
set(PICO_DOXYGEN_EXCLUDE_PATHS "${PICO_DOXYGEN_EXCLUDE_PATHS} ${SOURCE_DIR}" CACHE INTERNAL "")
endfunction()
include(${PICO_PLATFORM_CMAKE_FILE})

View File

@ -0,0 +1,31 @@
# PICO_CMAKE_CONFIG: PICO_BOARD, The board name being built for. This is overridable from the user environment, type=string, default=rp2040, group=build
if (DEFINED ENV{PICO_BOARD})
set(PICO_BOARD $ENV{PICO_BOARD})
message("Using PICO_BOARD from environment ('${PICO_BOARD}')")
else()
if (NOT PICO_BOARD)
set(PICO_BOARD "pico")
pico_message("Defaulting PICO target board to ${PICO_BOARD} since not specified.")
else()
message("PICO target board is ${PICO_BOARD}.")
endif()
endif()
set(PICO_BOARD ${PICO_BOARD} CACHE STRING "PICO target board (e.g. pico)")
# PICO_CMAKE_CONFIG: PICO_BOARD_CMAKE_DIRS, Directories to look for <PICO_BOARD>.cmake in. This is overridable from the user environment, type=list, default="", group=build
if (DEFINED ENV{PICO_BOARD_CMAKE_DIRS})
set(PICO_BOARD_CMAKE_DIRS $ENV{PICO_BOARD_CMAKE_DIRS})
message("Using PICO_BOARD_CMAKE_DIRS from environment ('${PICO_BOARD_CMAKE_DIRS}')")
endif()
list(APPEND PICO_BOARD_CMAKE_DIRS ${CMAKE_CURRENT_LIST_DIR}/boards)
pico_find_in_paths(PICO_BOARD_CMAKE_FILE PICO_BOARD_CMAKE_DIRS ${PICO_BOARD}.cmake)
if (EXISTS "${PICO_BOARD_CMAKE_FILE}")
message("Using CMake board configuration from ${PICO_BOARD_CMAKE_FILE}")
include(${PICO_BOARD_CMAKE_FILE} board_config)
else()
include(boards/generic_board.cmake)
endif()
list(APPEND PICO_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/boards/include) # so boards/foo.h can be explicitly included

View File

@ -0,0 +1,23 @@
# For boards without their own cmake file, simply include a header
# PICO_CMAKE_CONFIG: PICO_BOARD_HEADER_DIRS, Directories to look for <PICO_BOARD>.h in. This is overridable from the user environment, type=list, default="", group=build
if (DEFINED ENV{PICO_BOARD_HEADER_DIRS})
set(PICO_BOARD_HEADER_DIRS $ENV{PICO_BOARD_HEADER_DIRS})
message("Using PICO_BOARD_HEADER_DIRS from environment ('${PICO_BOARD_HEADER_DIRS}')")
endif()
set(PICO_BOARD_HEADER_DIRS ${PICO_BOARD_HEADER_DIRS} CACHE STRING "PICO board header directories")
list(APPEND PICO_BOARD_HEADER_DIRS ${CMAKE_CURRENT_LIST_DIR}/include/boards)
pico_find_in_paths(PICO_BOARD_HEADER_FILE PICO_BOARD_HEADER_DIRS ${PICO_BOARD}.h)
if (EXISTS ${PICO_BOARD_HEADER_FILE})
message("Using board configuration from ${PICO_BOARD_HEADER_FILE}")
list(APPEND PICO_CONFIG_HEADER_FILES ${PICO_BOARD_HEADER_FILE})
else()
set(msg "Unable to find definition of board '${PICO_BOARD}' (specified by PICO_BOARD):\n")
list(JOIN PICO_BOARD_HEADER_DIRS ", " DIRS)
string(CONCAT msg ${msg} " Looked for ${PICO_BOARD}.h in ${DIRS} (additional paths specified by PICO_BOARD_HEADER_DIRS)\n")
list(JOIN PICO_BOARD_CMAKE_DIRS ", " DIRS)
string(CONCAT msg ${msg} " Looked for ${PICO_BOARD}.cmake in ${DIRS} (additional paths specified by PICO_BOARD_CMAKE_DIRS)")
message(FATAL_ERROR ${msg})
endif()

View File

@ -0,0 +1,15 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// -----------------------------------------------------
// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
// -----------------------------------------------------
#ifndef _BOARDS_NONE_H
#define _BOARDS_NONE_H
#endif

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// -----------------------------------------------------
// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
// -----------------------------------------------------
// This header may be included by other board headers as "boards/pico.h"
#ifndef _BOARDS_PICO_H
#define _BOARDS_PICO_H
#ifndef PICO_DEFAULT_UART
#define PICO_DEFAULT_UART 0
#endif
#ifndef PICO_DEFAULT_UART_TX_PIN
#define PICO_DEFAULT_UART_TX_PIN 0
#endif
#ifndef PICO_DEFAULT_UART_RX_PIN
#define PICO_DEFAULT_UART_RX_PIN 1
#endif
#ifndef PICO_DEFAULT_LED_PIN
#define PICO_DEFAULT_LED_PIN 25
#endif
#ifndef PICO_FLASH_SPI_CLKDIV
#define PICO_FLASH_SPI_CLKDIV 2
#endif
#ifndef PICO_FLASH_SIZE_BYTES
#define PICO_FLASH_SIZE_BYTES (2 * 1024 * 1024)
#endif
// Drive high to force power supply into PWM mode (lower ripple on 3V3 at light loads)
#define PICO_SMPS_MODE_PIN 23
#ifndef PICO_FLOAT_SUPPORT_ROM_V1
#define PICO_FLOAT_SUPPORT_ROM_V1 1
#endif
#ifndef PICO_DOUBLE_SUPPORT_ROM_V1
#define PICO_DOUBLE_SUPPORT_ROM_V1 1
#endif
#endif

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// -----------------------------------------------------
// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
// -----------------------------------------------------
#ifndef _BOARDS_VGABOARD_H
#define _BOARDS_VGABOARD_H
#ifndef PICO_DEFAULT_UART
#define PICO_DEFAULT_UART 1
#endif
#ifndef PICO_DEFAULT_UART_TX_PIN
#define PICO_DEFAULT_UART_TX_PIN 20
#endif
#ifndef PICO_DEFAULT_UART_RX_PIN
#define PICO_DEFAULT_UART_RX_PIN 21
#endif
#ifndef PICO_DEFAULT_LED_PIN
#define PICO_DEFAULT_LED_PIN 25 // same as Pico
#endif
// Audio pins. I2S BCK, LRCK are on the same pins as PWM L/R.
// - When outputting I2S, PWM sees BCK and LRCK, which should sound silent as
// they are constant duty cycle, and above the filter cutoff
// - When outputting PWM, I2S DIN should be low, so I2S should remain silent.
#define VGABOARD_I2S_DIN_PIN 26
#define VGABOARD_I2S_BCK_PIN 27
#define VGABOARD_I2S_LRCK_PIN 28
#define VGABOARD_PWM_L_PIN 28
#define VGABOARD_PWM_R_PIN 27
#define VGABOARD_VGA_COLOR_PIN_BASE 0
#define VGABOARD_VGA_SYNC_PIN_BASE 16
// Note DAT2/3 are shared with UART TX/RX (pull jumpers off header to access
// UART pins and disconnect SD DAT2/3)
#define VGABOARD_SD_CLK_PIN 5
#define VGABOARD_SD_CMD_PIN 18
#define VGABOARD_SD_DAT0_PIN 19
// Note buttons are shared with VGA colour LSBs -- if using VGA, you can float
// the pin on VSYNC assertion and sample on VSYNC deassertion
#define VGABOARD_BUTTON_A_PIN 0
#define VGABOARD_BUTTON_B_PIN 6
#define VGABOARD_BUTTON_C_PIN 11
#ifndef PICO_SCANVIDEO_COLOR_PIN_BASE
#define PICO_SCANVIDEO_COLOR_PIN_BASE VGABOARD_VGA_COLOR_PIN_BASE
#endif
#ifndef PICO_SCANVIDEO_SYMC_PIN_BASE
#define PICO_SCANVIDEO_SYNC_PIN_BASE VGABOARD_VGA_SYNC_PIN_BASE
#endif
#ifndef PICO_SD_CLK_PIN
#define PICO_SD_CLK_PIN VGABOARD_SD_CLK_PIN
#endif
#ifndef PICO_SD_CMD_PIN
#define PICO_SD_CMD_PIN VGABOARD_SD_CMD_PIN
#endif
#ifndef PICO_SD_DAT0_PIN
#define PICO_SD_DAT0_PIN VGABOARD_SD_DAT0_PIN
#endif
#define PICO_AUDIO_I2S_DATA_PIN VGABOARD_I2S_DIN_PIN
#define PICO_AUDIO_I2S_CLOCK_PIN_BASE VGABOARD_I2S_BCK_PIN
#define PICO_AUDIO_PWM_L_PIN VGABOARD_PWM_L_PIN
#define PICO_AUDIO_PWM_R_PIN VGABOARD_PWM_R_PIN
#ifndef PICO_FLASH_SPI_CLKDIV
#define PICO_FLASH_SPI_CLKDIV 2
#endif
#ifndef PICO_FLASH_SIZE_BYTES
#define PICO_FLASH_SIZE_BYTES (2 * 1024 * 1024)
#endif
// Drive high to force power supply into PWM mode (lower ripple on 3V3 at light loads)
#define PICO_SMPS_MODE_PIN 23
#ifndef PICO_FLOAT_SUPPORT_ROM_V1
#define PICO_FLOAT_SUPPORT_ROM_V1 1
#endif
#ifndef PICO_DOUBLE_SUPPORT_ROM_V1
#define PICO_DOUBLE_SUPPORT_ROM_V1 1
#endif
#define PICO_VGA_BOARD
#endif

View File

@ -0,0 +1,16 @@
pico_add_subdirectory(boot_picoboot)
pico_add_subdirectory(boot_uf2)
pico_add_subdirectory(pico_base)
# PICO_CMAKE_CONFIG: PICO_BARE_METAL, Flag to exclude anything except base headers from the build, type=bool, default=0, group=build
if (NOT PICO_BARE_METAL)
pico_add_subdirectory(pico_bit_ops)
pico_add_subdirectory(pico_binary_info)
pico_add_subdirectory(pico_divider)
pico_add_subdirectory(pico_sync)
pico_add_subdirectory(pico_time)
pico_add_subdirectory(pico_util)
pico_add_subdirectory(pico_stdlib)
endif()
pico_add_doxygen(${CMAKE_CURRENT_LIST_DIR})

View File

@ -0,0 +1,3 @@
This directory code that is common to all builds regardless of `PICO_PLATFORM`. It is a mix
of common header files, or high level functionality built entirely using `hardware_` or `pico_` libraries provided
by the actual target `PICO_PLATFORM``

View File

@ -0,0 +1,2 @@
add_library(boot_picoboot_headers INTERFACE)
target_include_directories(boot_picoboot_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _BOOT_PICOBOOT_H
#define _BOOT_PICOBOOT_H
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#ifndef NO_PICO_PLATFORM
#include "pico/platform.h"
#endif
/** \file picoboot.h
* \defgroup boot_picoboot boot_picoboot
*
* Header file for the PICOBOOT USB interface exposed by an RP2040 in BOOTSEL mode.
*/
#define PICOBOOT_MAGIC 0x431fd10bu
// --------------------------------------------
// CONTROL REQUESTS FOR THE PICOBOOT INTERFACE
// --------------------------------------------
// size 0 OUT - unstall EPs and reset
#define PICOBOOT_IF_RESET 0x41
// size 16 IN - return the status of the last command
#define PICOBOOT_IF_CMD_STATUS 0x42
// --------------------------------------------------
// COMMAND REQUESTS SENT TO THE PICOBOOT OUT ENDPOINT
// --------------------------------------------------
//
// picoboot_cmd structure of size 32 is sent to OUT endpoint
// transfer_length bytes are transferred via IN/OUT
// device responds on success with 0 length ACK packet set via OUT/IN
// device may stall the transferring endpoint in case of error
enum picoboot_cmd_id {
PC_EXCLUSIVE_ACCESS = 0x1,
PC_REBOOT = 0x2,
PC_FLASH_ERASE = 0x3,
PC_READ = 0x84, // either RAM or FLASH
PC_WRITE = 5, // either RAM or FLASH (does no erase)
PC_EXIT_XIP = 0x6,
PC_ENTER_CMD_XIP = 0x7,
PC_EXEC = 0x8,
PC_VECTORIZE_FLASH = 0x9
};
enum picoboot_status {
PICOBOOT_OK = 0,
PICOBOOT_UNKNOWN_CMD = 1,
PICOBOOT_INVALID_CMD_LENGTH = 2,
PICOBOOT_INVALID_TRANSFER_LENGTH = 3,
PICOBOOT_INVALID_ADDRESS = 4,
PICOBOOT_BAD_ALIGNMENT = 5,
PICOBOOT_INTERLEAVED_WRITE = 6,
PICOBOOT_REBOOTING = 7,
PICOBOOT_UNKNOWN_ERROR = 8,
};
struct __packed picoboot_reboot_cmd {
uint32_t dPC; // 0 means reset into bootrom
uint32_t dSP;
uint32_t dDelayMS;
};
// used for EXEC, VECTORIZE_FLASH
struct __packed picoboot_address_only_cmd {
uint32_t dAddr;
};
// used for READ, WRITE, FLASH_ERASE
struct __packed picoboot_range_cmd {
uint32_t dAddr;
uint32_t dSize;
};
enum picoboot_exclusive_type {
NOT_EXCLUSIVE = 0,
EXCLUSIVE,
EXCLUSIVE_AND_EJECT
};
struct __packed picoboot_exclusive_cmd {
uint8_t bExclusive;
};
// little endian
struct __packed __aligned(4) picoboot_cmd {
uint32_t dMagic;
uint32_t dToken; // an identifier for this token to correlate with a status response
uint8_t bCmdId; // top bit set for IN
uint8_t bCmdSize; // bytes of actual data in the arg part of this structure
uint16_t _unused;
uint32_t dTransferLength; // length of IN/OUT transfer (or 0) if none
union {
uint8_t args[16];
struct picoboot_reboot_cmd reboot_cmd;
struct picoboot_range_cmd range_cmd;
struct picoboot_address_only_cmd address_only_cmd;
struct picoboot_exclusive_cmd exclusive_cmd;
};
};
static_assert(32 == sizeof(struct picoboot_cmd), "picoboot_cmd must be 32 bytes big");
struct __packed __aligned(4) picoboot_cmd_status {
uint32_t dToken;
uint32_t dStatusCode;
uint8_t bCmdId;
uint8_t bInProgress;
uint8_t _pad[6];
};
static_assert(16 == sizeof(struct picoboot_cmd_status), "picoboot_cmd_status must be 16 bytes big");
#endif

View File

@ -0,0 +1,2 @@
add_library(boot_uf2_headers INTERFACE)
target_include_directories(boot_uf2_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _BOOT_UF2_H
#define _BOOT_UF2_H
#include <stdint.h>
#include <assert.h>
/** \file uf2.h
* \defgroup boot_uf2 boot_uf2
*
* Header file for the UF2 format supported by an RP2040 in BOOTSEL mode.
*/
#define UF2_MAGIC_START0 0x0A324655u
#define UF2_MAGIC_START1 0x9E5D5157u
#define UF2_MAGIC_END 0x0AB16F30u
#define UF2_FLAG_NOT_MAIN_FLASH 0x00000001u
#define UF2_FLAG_FILE_CONTAINER 0x00001000u
#define UF2_FLAG_FAMILY_ID_PRESENT 0x00002000u
#define UF2_FLAG_MD5_PRESENT 0x00004000u
#define RP2040_FAMILY_ID 0xe48bff56
struct uf2_block {
// 32 byte header
uint32_t magic_start0;
uint32_t magic_start1;
uint32_t flags;
uint32_t target_addr;
uint32_t payload_size;
uint32_t block_no;
uint32_t num_blocks;
uint32_t file_size; // or familyID;
uint8_t data[476];
uint32_t magic_end;
};
static_assert(sizeof(struct uf2_block) == 512, "uf2_block not sector sized");
#endif

View File

@ -0,0 +1,40 @@
if (NOT TARGET pico_base_headers)
# build the auto gen config headers
set(header_content "// AUTOGENERATED FROM PICO_CONFIG_HEADER_FILES and then PICO_<PLATFORM>_CONFIG_HEADER_FILES\n// DO NOT EDIT!\n")
string(TOUPPER ${PICO_PLATFORM} PICO_PLATFORM_UPPER)
macro(add_header_content_from_var VAR)
set(header_content "${header_content}\n\n// based on ${VAR}:\n")
foreach(var IN LISTS ${VAR})
set(header_content "${header_content}\n#include \"${var}\"")
endforeach()
endmacro()
# PICO_CMAKE_CONFIG: PICO_CONFIG_HEADER_FILES, List of extra header files to include from pico/config.h for all platforms, type=list, default="", group=pico_base
add_header_content_from_var(PICO_CONFIG_HEADER_FILES)
# PICO_CMAKE_CONFIG: PICO_CONFIG_RP2040_HEADER_FILES, List of extra header files to include from pico/config.h for rp2040 platform, type=list, default="", group=pico_base
# PICO_CMAKE_CONFIG: PICO_CONFIG_HOST_HEADER_FILES, List of extra header files to include from pico/config.h for host platform, type=list, default="", group=pico_base
add_header_content_from_var(PICO_${PICO_PLATFORM_UPPER}_CONFIG_HEADER_FILES)
file(GENERATE
OUTPUT ${CMAKE_BINARY_DIR}/generated/pico_base/pico/config_autogen.h
CONTENT "${header_content}"
)
configure_file( include/pico/version.h.in ${CMAKE_BINARY_DIR}/generated/pico_base/pico/version.h)
add_library(pico_base_headers INTERFACE)
target_include_directories(pico_base_headers INTERFACE include ${CMAKE_BINARY_DIR}/generated/pico_base)
foreach(DIR IN LISTS PICO_INCLUDE_DIRS)
target_include_directories(pico_base_headers INTERFACE ${DIR})
endforeach()
# PICO_BUILD_DEFINE: PICO_BOARD, Name of board, type=string, default=CMake PICO_BOARD variable, group=pico_base
target_compile_definitions(pico_base_headers INTERFACE
PICO_BOARD="${PICO_BOARD}")
target_link_libraries(pico_base_headers INTERFACE pico_platform_headers)
endif()

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef PICO_H_
#define PICO_H_
/** \file pico.h
* \defgroup pico_base pico_base
*
* Core types and macros for the Raspberry Pi Pico SDK. This header is intended to be included by all source code
*/
#include "pico/types.h"
#include "pico/version.h"
#include "pico/config.h"
#include "pico/platform.h"
#include "pico/assert.h"
#include "pico/error.h"
#endif

View File

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_ASSERT_H
#define _PICO_ASSERT_H
#include "pico/types.h"
#ifdef __cplusplus
#include <cassert>
extern "C" {
#else
#include <assert.h>
#endif
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLE_ALL, Global assert enable, type=bool, default=0, group=pico_base
// PICO_CONFIG: PARAM_ASSERTIONS_DISABLE_ALL, Global assert disable, type=bool, default=0, group=pico_base
#ifndef PARAM_ASSERTIONS_ENABLE_ALL
#define PARAM_ASSERTIONS_ENABLE_ALL 0
#endif
#ifndef PARAM_ASSERTIONS_DISABLE_ALL
#define PARAM_ASSERTIONS_DISABLE_ALL 0
#endif
#define PARAM_ASSERTIONS_ENABLED(x) ((PARAM_ASSERTIONS_ENABLED_ ## x || PARAM_ASSERTIONS_ENABLE_ALL) && !PARAM_ASSERTIONS_DISABLE_ALL)
#define invalid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(!(test));})
#define valid_params_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) assert(test);})
#define hard_assert_if(x, test) ({if (PARAM_ASSERTIONS_ENABLED(x)) hard_assert(!(test));})
#ifdef NDEBUG
extern void hard_assertion_failure();
static inline void hard_assert(bool condition, ...) {
if (!condition)
hard_assertion_failure();
}
#else
#define hard_assert assert
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef PICO_CONFIG_H_
#define PICO_CONFIG_H_
// -----------------------------------------------------
// NOTE: THIS HEADER IS ALSO INCLUDED BY ASSEMBLER SO
// SHOULD ONLY CONSIST OF PREPROCESSOR DIRECTIVES
// OR USE #ifndef __ASSEMBLER__ guards
// -------------
// PICO_CONFIG_HEADER_FILES and then PICO_SDK_<PLATFORM>_CONFIG_INCLUDE_FILES
// entries are dumped in order at build time into this generated header
#endif

View File

@ -0,0 +1,21 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_ERROR_H
#define _PICO_ERROR_H
/*!
* Common return codes from pico_sdk methods that return a status
*/
enum {
PICO_OK = 0,
PICO_ERROR_NONE = 0,
PICO_ERROR_TIMEOUT = -1,
PICO_ERROR_GENERIC = -2,
PICO_ERROR_NO_DATA = -3,
};
#endif

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_TYPES_H
#define _PICO_TYPES_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
typedef unsigned int uint;
#ifdef NDEBUG
/*! \typedef absolute_time_t
\brief An opaque 64 bit timestamp in microseconds
The type is used instead of a raw uint64_t to prevent accidentally passing relative times or times in the wrong
time units where an absolute time is required. It is equivalent to uint64_t in release builds.
\see to_us_since_boot
\see update_us_since_boot
*/
typedef uint64_t absolute_time_t;
/*! fn to_us_since_boot
* \brief convert an absolute_time_t into a number of microseconds since boot.
* \param t the number of microseconds since boot
* \return an absolute_time_t value equivalent to t
*/
static inline uint64_t to_us_since_boot(absolute_time_t t) {
return t;
}
/*! fn update_us_since_boot
* \brief update an absolute_time_t value to represent a given number of microseconds since boot
* \param t the absolute time value to update
* \param us_since_boot the number of microseconds since boot to represent
*/
static inline void update_us_since_boot(absolute_time_t *t, uint64_t us_since_boot) {
*t = us_since_boot;
}
#define ABSOLUTE_TIME_INITIALIZED_VAR(name, value) name = value
#else
typedef struct {
uint64_t _private_us_since_boot;
} absolute_time_t;
static inline uint64_t to_us_since_boot(absolute_time_t t) {
return t._private_us_since_boot;
}
static inline void update_us_since_boot(absolute_time_t *t, uint64_t us_since_boot) {
t->_private_us_since_boot = us_since_boot;
}
#define ABSOLUTE_TIME_INITIALIZED_VAR(name, value) name = {value}
#endif
/** \struct datetime_t
* \ingroup util_datetime
* \brief Structure containing date and time information
*
* When setting an RTC alarm, set a field to -1 tells
* the RTC to not match on this field
*/
typedef struct {
int16_t year; ///< 0..4095
int8_t month; ///< 1..12, 1 is January
int8_t day; ///< 1..28,29,30,31 depending on month
int8_t dotw; ///< 0..6, 0 is Sunday
int8_t hour; ///< 0..23
int8_t min; ///< 0..59
int8_t sec; ///< 0..59
} datetime_t;
#endif

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// ---------------------------------------
// THIS FILE IS AUTOGENERATED; DO NOT EDIT
// ---------------------------------------
#ifndef _PICO_VERSION_H
#define _PICO_VERSION_H
#define PICO_SDK_VERSION_MAJOR ${PICO_SDK_VERSION_MAJOR}
#define PICO_SDK_VERSION_MINOR ${PICO_SDK_VERSION_MINOR}
#define PICO_SDK_VERSION_REVISION ${PICO_SDK_VERSION_REVISION}
#define PICO_SDK_VERSION_STRING "${PICO_SDK_VERSION_STRING}"
#endif

View File

@ -0,0 +1,30 @@
add_library(pico_binary_info_headers INTERFACE)
target_include_directories(pico_binary_info_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
add_library(pico_binary_info INTERFACE)
target_link_libraries(pico_binary_info INTERFACE pico_binary_info_headers)
function(pico_set_program_name TARGET name)
# PICO_BUILD_DEFINE: PICO_PROGRAM_NAME, value passed to pico_set_program_name, type=string, default=none, group=pico_binary_info
target_compile_definitions(${TARGET} PRIVATE -DPICO_PROGRAM_NAME="${name}")
endfunction()
function(pico_set_program_description TARGET description)
# since this is the command line, we will remove newlines
string(REPLACE "\n" " " description ${description})
string(REPLACE "\"" "\\\"" description ${description})
# PICO_BUILD_DEFINE: PICO_PROGRAM_DESCRIPTION, value passed to pico_set_program_description, type=string, default=none, group=pico_binary_info
target_compile_definitions(${TARGET} PRIVATE -DPICO_PROGRAM_DESCRIPTION="${description}")
endfunction()
function(pico_set_program_url TARGET url)
# PICO_BUILD_DEFINE: PICO_PROGRAM_URL, value passed to pico_set_program_url, type=string, default=none, group=pico_binary_info
target_compile_definitions(${TARGET} PRIVATE -DPICO_PROGRAM_URL="${url}")
endfunction()
function(pico_set_program_version TARGET version)
# PICO_BUILD_DEFINE: PICO_PROGRAM_VERSION_STRING, value passed to pico_set_program_version, type=string, default=none, group=pico_binary_info
target_compile_definitions(${TARGET} PRIVATE -DPICO_PROGRAM_VERSION_STRING="${version}")
endfunction()

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_BINARY_INFO_H
#define _PICO_BINARY_INFO_H
/**
* Binary info is intended for embedding machine readable information with the binary in FLASH.
*
* Example uses include:
*
* - Program identification / information
* - Pin layouts
* - Included features
* - Identifying flash regions used as block devices/storage
*/
#include "pico/binary_info/defs.h"
#include "pico/binary_info/structure.h"
#if PICO_ON_DEVICE
#include "pico/binary_info/code.h"
#endif
#endif

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_BINARY_INFO_CODE_H
#define _PICO_BINARY_INFO_CODE_H
#include "pico.h"
#include "pico/binary_info/structure.h"
#if !PICO_NO_BINARY_INFO
#define __bi_decl(name, bi, section_prefix, attr) static const attr __attribute__((section(section_prefix __STRING(name)))) struct _binary_info_core *name = bi
#define __bi_lineno_var_name __CONCAT(__bi_, __LINE__)
#define __bi_ptr_lineno_var_name __CONCAT(__bi_ptr, __LINE__)
#define __bi_enclosure_check_lineno_var_name __CONCAT(_error_bi_is_missing_enclosing_decl_,__LINE__)
#define __bi_mark_enclosure static const __unused int __bi_enclosure_check_lineno_var_name=0;
#if !defined(__GNUC__) || __cplusplus || __GNUC__ >= 8
#define __bi_enclosure_check(x) (x + __bi_enclosure_check_lineno_var_name)
#else
// skip the version check on older GCC non C++, as it doesn't compile.. this is only here to catch the
// user accidentally forgetting to enclose the binary item with bi_decl
#define __bi_enclosure_check(x) (x)
#endif
/**
* Declare some binary information that will be included if the contain source file/line is compiled into the binary
*/
#define bi_decl(_decl) __bi_mark_enclosure _decl; __bi_decl(__bi_ptr_lineno_var_name, &__bi_lineno_var_name.core, ".binary_info.keep.", __used);
/**
* Declare some binary information that will be included if the function containing the decl is linked into the binary.
* The SDK uses --gc-sections, so functions that are never called will be removed by the linker, and any associated
* binary information declared this way will also be stripped
*/
#define bi_decl_if_func_used(_decl) ({__bi_mark_enclosure _decl; __bi_decl(__bi_ptr_lineno_var_name, &__bi_lineno_var_name.core, ".binary_info.", ); *(volatile uint8_t *)&__bi_ptr_lineno_var_name;});
#define bi_decl_with_attr(_decl, _attr) __bi_mark_enclosure _attr _decl; __bi_decl(__bi_ptr_lineno_var_name, &__bi_lineno_var_name.core, ".binary_info.keep.", __used);
#define bi_decl_if_func_used_with_attr(_decl, _attr) ({__bi_mark_enclosure _attr _decl; __bi_decl(__bi_ptr_lineno_var_name, &__bi_lineno_var_name.core, ".binary_info.", ); *(volatile uint8_t *)&__bi_ptr_lineno_var_name;});
#else
#define __bi_decl(bi, name, attr)
#define bi_decl_with_attr(_decl, _attr)
#define bi_decl(_decl)
#define bi_decl_if_func_used_with_attr(_decl, _attr) ((void)0);
#define bi_decl_if_func_used(_decl) ((void)0);
#endif
#define bi_int(_tag, _id, _value) \
static const struct _binary_info_id_and_int __bi_lineno_var_name = { \
.core = { \
.type = __bi_enclosure_check(BINARY_INFO_TYPE_ID_AND_INT), \
.tag = _tag, \
},\
.id = _id, \
.value = _value \
};
#define bi_string(_tag, _id, _value) \
static const struct _binary_info_id_and_string __bi_lineno_var_name = { \
.core = { \
.type = __bi_enclosure_check(BINARY_INFO_TYPE_ID_AND_STRING), \
.tag = _tag, \
},\
.id = _id, \
.value = _value, \
}
#define bi_block_device(_tag, _name, _address, _size, _extra, _flags) \
static const struct _binary_info_block_device __bi_lineno_var_name = { \
.core = { \
.type = __bi_enclosure_check(BINARY_INFO_TYPE_BLOCK_DEVICE), \
.tag = _tag, \
},\
.name = _name, \
.address = _address, \
.size = _size, \
.extra = _extra, \
.flags = _flags, \
}
#define __bi_encoded_pins_with_func(_encoding) \
static const struct _binary_info_pins_with_func __bi_lineno_var_name = { \
.core = { \
.type = __bi_enclosure_check(BINARY_INFO_TYPE_PINS_WITH_FUNC), \
.tag = BINARY_INFO_TAG_RASPBERRY_PI, \
},\
.pin_encoding = _encoding \
}
#define __bi_pins_with_name(_mask, _label) \
static const struct _binary_info_pins_with_name __bi_lineno_var_name = { \
.core = { \
.type = __bi_enclosure_check(BINARY_INFO_TYPE_PINS_WITH_NAME), \
.tag = BINARY_INFO_TAG_RASPBERRY_PI, \
},\
.pin_mask = _mask, \
.label = _label \
}
#define __bi_named_group(_parent_tag, _parent_id, _group_tag, _group_id, _label, _flags) \
static const struct _binary_info_named_group __bi_lineno_var_name = { \
.core = { \
.type = __bi_enclosure_check(BINARY_INFO_TYPE_NAMED_GROUP), \
.tag = _parent_tag, \
},\
.parent_id = _parent_id, \
.group_tag = _group_tag, \
.flags = _flags, \
.group_id = _group_id, \
.label = _label \
}
#define bi_binary_end(end) bi_int(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_BINARY_END, end)
#define bi_program_name(name) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_NAME, name)
#define bi_program_description(description) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_DESCRIPTION, description)
#define bi_program_version_string(version_string) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_VERSION_STRING, version_string)
#define bi_program_build_date_string(date_string) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_BUILD_DATE_STRING, date_string)
#define bi_program_url(url) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_URL, url)
// multiple of these may be added
#define bi_program_feature(feature) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_FEATURE, feature)
#define bi_program_build_attribute(attr) bi_string(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_BUILD_ATTRIBUTE, attr)
#define bi_program_feature_group(tag, id, name) __bi_named_group(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_FEATURE, tag, id, name, 0)
#define bi_program_feature_group_with_flags(tag, id, name, flags) __bi_named_group(BINARY_INFO_TAG_RASPBERRY_PI, BINARY_INFO_ID_RP_PROGRAM_FEATURE, tag, id, name, flags)
#define bi_1pin_with_func(p0, func) __bi_encoded_pins_with_func(BI_PINS_ENCODING_MULTI | ((func << 3)) | ((p0) << 7) | ((p0) << 12))
#define bi_2pins_with_func(p0, p1, func) __bi_encoded_pins_with_func(BI_PINS_ENCODING_MULTI | ((func << 3)) | ((p0) << 7) | ((p1) << 12) | ((p1) << 17))
#define bi_3pins_with_func(p0, p1, p2, func) __bi_encoded_pins_with_func(BI_PINS_ENCODING_MULTI | ((func << 3)) | ((p0) << 7) | ((p1) << 12) | ((p2) << 17) | ((p2) << 22))
#define bi_4pins_with_func(p0, p1, p2, p3, func) __bi_encoded_pins_with_func(BI_PINS_ENCODING_MULTI | ((func << 3)) | ((p0) << 7) | ((p1) << 12) | ((p2) << 17) | ((p3) << 22) | ((p3) << 27))
#define bi_5pins_with_func(p0, p1, p2, p3, p4, func) __bi_encoded_pins_with_func(BI_PINS_ENCODING_MULTI | ((func << 3)) | ((p0) << 7) | ((p1) << 12) | ((p2) << 17) | ((p3) << 22) | ((p4) << 27))
#define bi_pin_range_with_func(plo, phi, func) __bi_encoded_pins_with_func(BI_PINS_ENCODING_RANGE | ((func << 3)) | ((plo) << 7) | ((phi) << 12))
#define bi_pin_mask_with_name(pmask, label) __bi_pins_with_name((pmask), (label))
// names are sperated by | ... i.e. "name1|name2|name3"
#define bi_pin_mask_with_names(pmask, label) __bi_pins_with_name((pmask), (label))
#define bi_1pin_with_name(p0, name) bi_pin_mask_with_name(1u << (p0), name)
#define bi_2pins_with_names(p0, name0, p1, name1) bi_pin_mask_with_names((1u << (p0)) | (1u << (p1)), name0 "|" name1)
#define bi_3pins_with_names(p0, name0, p1, name1, p2, name2) bi_pin_mask_with_names((1u << (p0)) | (1u << (p1)) | (1u << (p2)), name0 "|" name1 "|" name2)
#define bi_4pins_with_names(p0, name0, p1, name1, p2, name2, p3, name3) bi_pin_mask_with_names((1u << (p0)) | (1u << (p1)) | (1u << (p2)) | (1u << (p3)), name0 "|" name1 "|" name2 "|" name3)
#endif

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_BINARY_INFO_DEFS_H
#define _PICO_BINARY_INFO_DEFS_H
// this file is for pre-processor definitions only
// should be found within the first 256 bytes of the real binary (i.e. after the flash second stage if a flash binary)
//
// Note the layout is:
//
// addr : BINARY_INFO_MARKER_START
// addr+0x04 : __binary_info_start
// addr+0x08 : __binary_info_end
// addr+0x0c : __address_mapping_table
// addr+0x10 | BINARY_INFO_MARKER_END
//
// __binary_info_start to __binary_info_end are the start, end (non inclusive) of an array
// of pointers to binary_info_t structures
//
// __address_mapping_table is an array of the following items:
//
// uint32_t source_addr_start
// uint32_t dest_addr_start
// uint32_t dest_addr_end
//
// representing a mapping from the stored address in the binary/flash to addresses at runtime.
// The linker will store pointers within the binary using their runtime values, however because of
// "AT" mapping in the link script these addresses actually correspond to a different address in the binary
// image. This mapping (which in the case of crt0.S is simply the data copy table used at initialization
// to copy data into it's runtime location) can be used by picotool or others to reverse the mapping to find data
// within the binary.
//
// Note the above array is terminated with a NULL source_addr_start
#define BINARY_INFO_MARKER_START 0x7188ebf2
#define BINARY_INFO_MARKER_END 0xe71aa390
#endif

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_BINARY_INFO_STRUCTURE_H
#define _PICO_BINARY_INFO_STRUCTURE_H
// NOTE: This file may be included by non SDK code, so does not use SDK includes
// NOTE: ALL CHANGES MUST BE BACKWARDS COMPATIBLE
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#ifndef __packed
#define __packed __attribute__((packed))
#endif
typedef struct _binary_info_core binary_info_t;
#define BINARY_INFO_TYPE_RAW_DATA 1
#define BINARY_INFO_TYPE_SIZED_DATA 2
#define BINARY_INFO_TYPE_BINARY_INFO_LIST_ZERO_TERMINATED 3
#define BINARY_INFO_TYPE_BSON 4
#define BINARY_INFO_TYPE_ID_AND_INT 5
#define BINARY_INFO_TYPE_ID_AND_STRING 6
// traditional block device
#define BINARY_INFO_TYPE_BLOCK_DEVICE 7
#define BINARY_INFO_TYPE_PINS_WITH_FUNC 8
#define BINARY_INFO_TYPE_PINS_WITH_NAME 9
#define BINARY_INFO_TYPE_PINS_WITH_NAMES 9
#define BINARY_INFO_TYPE_NAMED_GROUP 10
// note plan is to reserve c1 = 0->31 for "collision tags"; i.e.
// for which you should always use random IDs with the binary_info,
// giving you 4 + 8 + 32 = 44 bits to avoid collisions
#define BINARY_INFO_MAKE_TAG(c1, c2) ((((uint)c2&0xffu)<<8u)|((uint)c1&0xffu))
// Raspberry Pi defined. do not use
#define BINARY_INFO_TAG_RASPBERRY_PI BINARY_INFO_MAKE_TAG('R','P')
#define BINARY_INFO_ID_RP_PROGRAM_NAME 0x02031c86
#define BINARY_INFO_ID_RP_PROGRAM_VERSION_STRING 0x11a9bc3a
#define BINARY_INFO_ID_RP_PROGRAM_BUILD_DATE_STRING 0x9da22254
#define BINARY_INFO_ID_RP_BINARY_END 0x68f465de
#define BINARY_INFO_ID_RP_PROGRAM_URL 0x1856239a
#define BINARY_INFO_ID_RP_PROGRAM_DESCRIPTION 0xb6a07c19
#define BINARY_INFO_ID_RP_PROGRAM_FEATURE 0xa1f4b453
#define BINARY_INFO_ID_RP_PROGRAM_BUILD_ATTRIBUTE 0x4275f0d3
#define BINARY_INFO_ID_RP_SDK_VERSION 0x5360b3ab
#define BINARY_INFO_ID_RP_PICO_BOARD 0xb63cffbb
#if PICO_ON_DEVICE
#define bi_ptr_of(x) x *
#else
#define bi_ptr_of(x) uint32_t
#endif
typedef struct __packed _binary_info_core {
uint16_t type;
uint16_t tag;
} binary_info_core_t;
typedef struct __packed _binary_info_raw_data {
struct _binary_info_core core;
uint8_t bytes[1];
} binary_info_raw_data_t;
typedef struct __packed _binary_info_sized_data {
struct _binary_info_core core;
uint32_t length;
uint8_t bytes[1];
} binary_info_sized_data_t;
typedef struct __packed _binary_info_list_zero_terminated {
struct _binary_info_core core;
bi_ptr_of(binary_info_t) list;
} binary_info_list_zero_terminated_t;
typedef struct __packed _binary_info_id_and_int {
struct _binary_info_core core;
uint32_t id;
int32_t value;
} binary_info_id_and_int_t;
typedef struct __packed _binary_info_id_and_string {
struct _binary_info_core core;
uint32_t id;
bi_ptr_of(const char) value;
} binary_info_id_and_string_t;
typedef struct __packed _binary_info_block_device {
struct _binary_info_core core;
bi_ptr_of(const char) name; // optional static name (independent of what is formatted)
uint32_t address;
uint32_t size;
bi_ptr_of(binary_info_t) extra; // additional info
uint16_t flags;
} binary_info_block_device_t;
#define BI_PINS_ENCODING_RANGE 1
#define BI_PINS_ENCODING_MULTI 2
typedef struct __packed _binary_info_pins_with_func {
struct _binary_info_core core;
// p4_5 : p3_5 : p2_5 : p1_5 : p0_5 : func_4 : 001_3 //individual pins p0,p1,p2,p3,p4 ... if fewer than 5 then duplicate p
// phi_5 : plo_5 : func_4 : 010_3 // pin range plo-phi inclusive
uint32_t pin_encoding;
} binary_info_pins_with_func_t;
typedef struct __packed _binary_info_pins_with_name {
struct _binary_info_core core;
uint32_t pin_mask;
bi_ptr_of(const char) label;
} binary_info_pins_with_name_t;
#define BI_NAMED_GROUP_SHOW_IF_EMPTY 0x0001 // default is to hide
#define BI_NAMED_GROUP_SEPARATE_COMMAS 0x0002 // default is newlines
#define BI_NAMED_GROUP_SORT_ALPHA 0x0004 // default is no sort
#define BI_NAMED_GROUP_ADVANCED 0x0008 // if set, then only shown in say info -a
typedef struct __packed _binary_info_named_group {
struct _binary_info_core core;
uint32_t parent_id;
uint16_t flags;
uint16_t group_tag;
uint32_t group_id;
bi_ptr_of(const char) label;
} binary_info_named_group_t;
enum {
BINARY_INFO_BLOCK_DEV_FLAG_READ =
1 << 0, // if not readable, then it is basically hidden, but tools may choose to avoid overwriting it
BINARY_INFO_BLOCK_DEV_FLAG_WRITE = 1 << 1,
BINARY_INFO_BLOCK_DEV_FLAG_REFORMAT = 1 << 2, // may be reformatted..
BINARY_INFO_BLOCK_DEV_FLAG_PT_UNKNOWN = 0 << 4, // unknown free to look
BINARY_INFO_BLOCK_DEV_FLAG_PT_MBR = 1 << 4, // expect MBR
BINARY_INFO_BLOCK_DEV_FLAG_PT_GPT = 2 << 4, // expect GPT
BINARY_INFO_BLOCK_DEV_FLAG_PT_NONE = 3 << 4, // no partition table
};
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,5 @@
if (NOT TARGET pico_bit_ops_headers)
add_library(pico_bit_ops_headers INTERFACE)
target_include_directories(pico_bit_ops_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_bit_ops_headers INTERFACE pico_base_headers)
endif()

View File

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_BIT_OPS_H
#define _PICO_BIT_OPS_H
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file bit_ops.h
* \defgroup pico_bit_ops pico_bit_ops
*
* Optimized bit manipulation functions.
* Additionally provides replacement implementations of the compiler built-ins __builtin_popcount, __builtin_clz
* and __bulitin_ctz
*/
/*! \brief Reverse the bits in a 32 bit word
* \ingroup pico_bit_ops
*
* \param bits 32 bit input
* \return the 32 input bits reversed
*/
uint32_t __rev(uint32_t bits);
/*! \brief Reverse the bits in a 64 bit double word
* \ingroup pico_bit_ops
*
* \param bits 64 bit input
* \return the 64 input bits reversed
*/
uint64_t __revll(uint64_t bits);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,5 @@
if (NOT TARGET pico_divider_headers)
add_library(pico_divider_headers INTERFACE)
target_include_directories(pico_divider_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_divider_headers INTERFACE pico_base_headers)
endif()

View File

@ -0,0 +1,322 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_DIVIDER_H_
#define _PICO_DIVIDER_H_
#include "pico.h"
#include "hardware/divider.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup pico_divider pico_divider
* Optimized 32 and 64 bit division functions accelerated by the RP2040 hardware divider.
* Additionally provides integration with the C `/` and `%` operators
*/
/** \file pico/divider.h
* \brief High level APIs including combined quotient and remainder functions for 32 and 64 bit accelerated by the hardware divider
* \ingroup pico_divider
*
* These functions all call __aeabi_idiv0 or __aebi_ldiv0 on division by zero
* passing the largest applicably signed value
*
* Functions with unsafe in their name do not save/restore divider state, so are unsafe to call from interrupts. Unsafe functions are slightly faster.
*/
/**
* \brief Integer divide of two signed 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient
*/
int32_t div_s32s32(int32_t a, int32_t b);
/**
* \brief Integer divide of two signed 32-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*/
static inline int32_t divmod_s32s32_rem(int32_t a, int32_t b, int32_t *rem) {
divmod_result_t r = hw_divider_divmod_s32(a, b);
*rem = to_remainder_s32(r);
return to_quotient_s32(r);
}
/**
* \brief Integer divide of two signed 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in low word/r0, remainder in high word/r1
*/
divmod_result_t divmod_s32s32(int32_t a, int32_t b);
/**
* \brief Integer divide of two unsigned 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return Quotient
*/
uint32_t div_u32u32(uint32_t a, uint32_t b);
/**
* \brief Integer divide of two unsigned 32-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*/
static inline uint32_t divmod_u32u32_rem(uint32_t a, uint32_t b, uint32_t *rem) {
divmod_result_t r = hw_divider_divmod_u32(a, b);
*rem = to_remainder_u32(r);
return to_quotient_u32(r);
}
/**
* \brief Integer divide of two unsigned 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in low word/r0, remainder in high word/r1
*/
divmod_result_t divmod_u32u32(uint32_t a, uint32_t b);
/**
* \brief Integer divide of two signed 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return Quotient
*/
int64_t div_s64s64(int64_t a, int64_t b);
/**
* \brief Integer divide of two signed 64-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*/
int64_t divmod_s64s64_rem(int64_t a, int64_t b, int64_t *rem);
/**
* \brief Integer divide of two signed 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in result (r0,r1), remainder in regs (r2, r3)
*/
int64_t divmod_s64s64(int64_t a, int64_t b);
/**
* \brief Integer divide of two unsigned 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return Quotient
*/
uint64_t div_u64u64(uint64_t a, uint64_t b);
/**
* \brief Integer divide of two unsigned 64-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*/
uint64_t divmod_u64u64_rem(uint64_t a, uint64_t b, uint64_t *rem);
/**
* \brief Integer divide of two signed 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in result (r0,r1), remainder in regs (r2, r3)
*/
uint64_t divmod_u64u64(uint64_t a, uint64_t b);
// -----------------------------------------------------------------------
// these "unsafe" functions are slightly faster, but do not save the divider state,
// so are not generally safe to be called from interrupts
// -----------------------------------------------------------------------
/**
* \brief Unsafe integer divide of two signed 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient
*
* Do not use in interrupts
*/
int32_t div_s32s32_unsafe(int32_t a, int32_t b);
/**
* \brief Unsafe integer divide of two signed 32-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*
* Do not use in interrupts
*/
int32_t divmod_s32s32_rem_unsafe(int32_t a, int32_t b, int32_t *rem);
/**
* \brief Unsafe integer divide of two unsigned 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in low word/r0, remainder in high word/r1
*
* Do not use in interrupts
*/
int64_t divmod_s32s32_unsafe(int32_t a, int32_t b);
/**
* \brief Unsafe integer divide of two unsigned 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return Quotient
*
* Do not use in interrupts
*/
uint32_t div_u32u32_unsafe(uint32_t a, uint32_t b);
/**
* \brief Unsafe integer divide of two unsigned 32-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*
* Do not use in interrupts
*/
uint32_t divmod_u32u32_rem_unsafe(uint32_t a, uint32_t b, uint32_t *rem);
/**
* \brief Unsafe integer divide of two unsigned 32-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in low word/r0, remainder in high word/r1
*
* Do not use in interrupts
*/
uint64_t divmod_u32u32_unsafe(uint32_t a, uint32_t b);
/**
* \brief Unsafe integer divide of two signed 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return Quotient
*
* Do not use in interrupts
*/
int64_t div_s64s64_unsafe(int64_t a, int64_t b);
/**
* \brief Unsafe integer divide of two signed 64-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*
* Do not use in interrupts
*/
int64_t divmod_s64s64_rem_unsafe(int64_t a, int64_t b, int64_t *rem);
/**
* \brief Unsafe integer divide of two signed 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in result (r0,r1), remainder in regs (r2, r3)
*
* Do not use in interrupts
*/
int64_t divmod_s64s64_unsafe(int64_t a, int64_t b);
/**
* \brief Unsafe integer divide of two unsigned 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return Quotient
*
* Do not use in interrupts
*/
uint64_t div_u64u64_unsafe(uint64_t a, uint64_t b);
/**
* \brief Unsafe integer divide of two unsigned 64-bit values, with remainder
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \param [out] rem The remainder of dividend/divisor
* \return Quotient result of dividend/divisor
*
* Do not use in interrupts
*/
uint64_t divmod_u64u64_rem_unsafe(uint64_t a, uint64_t b, uint64_t *rem);
/**
* \brief Unsafe integer divide of two signed 64-bit values
* \ingroup pico_divider
*
* \param a Dividend
* \param b Divisor
* \return quotient in result (r0,r1), remainder in regs (r2, r3)
*
* Do not use in interrupts
*/
uint64_t divmod_u64u64_unsafe(uint64_t a, uint64_t b);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,11 @@
if (NOT TARGET pico_stdlib_headers)
add_library(pico_stdlib_headers INTERFACE)
target_include_directories(pico_stdlib_headers INTERFACE include)
target_link_libraries(pico_stdlib_headers INTERFACE
hardware_gpio
hardware_uart
hardware_divider
pico_time
pico_util
)
endif()

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_STDLIB_H
#define _PICO_STDLIB_H
#include "pico.h"
#include "pico/stdio.h"
#include "pico/time.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file stdlib.h
* \defgroup pico_stdlib pico_stdlib
*
* Aggregation of a core subset of Raspberry Pi Pico SDK libraries used by most executables along with some additional
* utility methods. Including pico_stdlib gives you everything you need to get a basic program running
* which prints to stdout or flashes a LED
*
* This library aggregates:
* - @ref hardware_uart
* - @ref hardware_gpio
* - @ref pico_binary_info
* - @ref pico_runtime
* - @ref pico_platform
* - @ref pico_printf
* - @ref pico_stdio
* - @ref pico_standard_link
* - @ref pico_util
*
* There are some basic default values used by these functions that will default to
* usable values, however, they can be customised in a board definition header via
* config.h or similar
*/
// Note PICO_STDIO_UART, PICO_STDIO_USB, PICO_STDIO_SEMIHOSTING are set by the
// respective INTERFACE libraries, so these defines are set if the library
// is included for the target executable
#if PICO_STDIO_UART
#include "pico/stdio_uart.h"
#endif
#if PICO_STDIO_USB
#include "pico/stdio_usb.h"
#endif
#if PICO_STDIO_SEMIHOSTING
#include "pico/stdio_semihosting.h"
#endif
/*! \brief Set up the default UART and assign it to the default GPIO's
* \ingroup pico_stdlib
*
* By default this will use UART 0, with TX to pin GPIO 0,
* RX to pin GPIO 1, and the baudrate to 115200
*
* Calling this method also initializes stdin/stdout over UART if the
* @ref pico_stdio_uart library is linked.
*
* Defaults can be changed using configuration defines,
* PICO_DEFAULT_UART_INSTANCE,
* PICO_DEFAULT_UART_BAUD_RATE
* PICO_DEFAULT_UART_TX_PIN
* PICO_DEFAULT_UART_RX_PIN
*/
void setup_default_uart();
/*! \brief Initialise the system clock to 48MHz
* \ingroup pico_stdlib
*
* Set the system clock to 48MHz, and set the peripheral clock to match.
*/
void set_sys_clock_48mhz();
/*! \brief Initialise the system clock
* \ingroup pico_stdlib
*
* \param vco_freq The voltage controller oscillator frequency to be used by the SYS PLL
* \param post_div1 The first post divider for the SYS PLL
* \param post_div2 The second post divider for the SYS PLL.
*
* See the PLL documentation in the datasheet for details of driving the PLLs.
*/
void set_sys_clock_pll(uint32_t vco_freq, uint post_div1, uint post_div2);
/*! \brief Check if a given system clock frequency is valid/attainable
* \ingroup pico_stdlib
*
* \param freq_khz Requested frequency
* \param vco_freq_out On success, the voltage controller oscillator frequeucny to be used by the SYS PLL
* \param post_div1_out On success, The first post divider for the SYS PLL
* \param post_div2_out On success, The second post divider for the SYS PLL.
* @return true if the frequency is possible and the output parameters have been written.
*/
bool check_sys_clock_khz(uint32_t freq_khz, uint *vco_freq_out, uint *post_div1_out, uint *post_div2_out);
/*! \brief Attempt to set a system clock frequency in khz
* \ingroup pico_stdlib
*
* Note that not all clock frequencies are possible; it is preferred that you
* use src/rp2_common/hardware_clocks/scripts/vcocalc.py to calculate the parameters
* for use with set_sys_clock_pll
*
* \param freq_khz Requested frequency
* \param required if true then this function will assert if the frequency is not attainable.
* \return true if the clock was configured
*/
static inline bool set_sys_clock_khz(uint32_t freq_khz, bool required) {
uint vco, postdiv1, postdiv2;
if (check_sys_clock_khz(freq_khz, &vco, &postdiv1, &postdiv2)) {
set_sys_clock_pll(vco, postdiv1, postdiv2);
return true;
} else if (required) {
panic("System clock of %u kHz cannot be exactly achieved", freq_khz);
}
return false;
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,44 @@
if (NOT TARGET pico_sync_headers)
add_library(pico_sync_headers INTERFACE)
target_include_directories(pico_sync_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_sync_headers INTERFACE hardware_sync pico_time)
endif()
if (NOT TARGET pico_sync_core)
add_library(pico_sync_core INTERFACE)
target_sources(pico_sync_core INTERFACE
${CMAKE_CURRENT_LIST_DIR}/lock_core.c
)
target_link_libraries(pico_sync_core INTERFACE pico_sync_headers)
endif()
if (NOT TARGET pico_sync_sem)
add_library(pico_sync_sem INTERFACE)
target_sources(pico_sync_sem INTERFACE
${CMAKE_CURRENT_LIST_DIR}/sem.c
)
target_link_libraries(pico_sync_sem INTERFACE pico_sync_core pico_time)
endif()
if (NOT TARGET pico_sync_mutex)
add_library(pico_sync_mutex INTERFACE)
target_sources(pico_sync_mutex INTERFACE
${CMAKE_CURRENT_LIST_DIR}/mutex.c
)
target_link_libraries(pico_sync_mutex INTERFACE pico_sync_core pico_time)
endif()
if (NOT TARGET pico_sync_critical_section)
add_library(pico_sync_critical_section INTERFACE)
target_sources(pico_sync_critical_section INTERFACE
${CMAKE_CURRENT_LIST_DIR}/critical_section.c
)
target_link_libraries(pico_sync_critical_section INTERFACE pico_sync_core pico_time)
endif()
if (NOT TARGET pico_sync)
add_library(pico_sync INTERFACE)
target_link_libraries(pico_sync INTERFACE pico_sync_sem pico_sync_mutex pico_sync_critical_section pico_sync_core)
endif()

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/critical_section.h"
#if !PICO_NO_HARDWARE
static_assert(sizeof(critical_section_t) == 8, "");
#endif
void critical_section_init(critical_section_t *critsec) {
critical_section_init_with_lock_num(critsec, spin_lock_claim_unused(true));
}
void critical_section_init_with_lock_num(critical_section_t *critsec, uint lock_num) {
lock_init(&critsec->core, lock_num);
__mem_fence_release();
}
void critical_section_deinit(critical_section_t *critsec) {
spin_lock_unclaim(spin_lock_get_num(critsec->core.spin_lock));
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PLATFORM_CRITICAL_SECTION_H
#define _PLATFORM_CRITICAL_SECTION_H
#include "pico/lock_core.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file critical_section.h
* \defgroup critical_section critical_section
* \ingroup pico_sync
* \brief Critical Section API for short-lived mutual exclusion safe for IRQ and multi-core
*
* A critical section is non-reentrant, and provides mutual exclusion using a spin-lock to prevent access
* from the other core, and from (higher priority) interrupts on the same core. It does the former
* using a spin lock and the latter by disabling interrupts on the calling core.
*
* Because interrupts are disabled by this function, uses of the critical_section should be as short as possible.
*/
typedef struct __packed_aligned critical_section {
lock_core_t core;
uint32_t save;
} critical_section_t;
/*! \brief Initialise a critical_section structure allowing the system to assign a spin lock number
* \ingroup critical_section
*
* The critical section is initialized ready for use, and will use a (possibly shared) spin lock
* number assigned by the system. Note that in general it is unlikely that you would be nesting
* critical sections, however if you do so you *must* use \ref critical_section_init_with_lock_num
* to ensure that the spin lock's used are different.
*
* \param critsec Pointer to critical_section structure
*/
void critical_section_init(critical_section_t *critsec);
/*! \brief Initialise a critical_section structure assigning a specific spin lock number
* \ingroup critical_section
* \param critsec Pointer to critical_section structure
* \param lock_num the specific spin lock number to use
*/
void critical_section_init_with_lock_num(critical_section_t *critsec, uint lock_num);
/*! \brief Enter a critical_section
* \ingroup critical_section
*
* If the spin lock associated with this critical section is in use, then this
* method will block until it is released.
*
* \param critsec Pointer to critical_section structure
*/
static inline void critical_section_enter_blocking(critical_section_t *critsec) {
critsec->save = spin_lock_blocking(critsec->core.spin_lock);
}
/*! \brief Release a critical_section
* \ingroup critical_section
*
* \param critsec Pointer to critical_section structure
*/
static inline void critical_section_exit(critical_section_t *critsec) {
spin_unlock(critsec->core.spin_lock, critsec->save);
}
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,28 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_LOCK_CORE_H
#define _PICO_LOCK_CORE_H
#include "pico.h"
#include "hardware/sync.h"
/** \file lock_core.h
* \ingroup pico_sync
*
* Base implementation for locking primitives protected by a spin lock
*/
typedef struct lock_core {
// spin lock protecting this lock's state
spin_lock_t *spin_lock;
// note any lock members in containing structures need not be volatile;
// they are protected by memory/compiler barriers when gaining and release spin locks
} lock_core_t;
void lock_init(lock_core_t *core, uint lock_num);
#endif

View File

@ -0,0 +1,135 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PLATFORM_MUTEX_H
#define _PLATFORM_MUTEX_H
#include "pico/lock_core.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file mutex.h
* \defgroup mutex mutex
* \ingroup pico_sync
* \brief Mutex API for non IRQ mutual exclusion between cores
*
* Mutexes are application level locks usually used protecting data structures that might be used by
* multiple cores. Unlike critical sections, the mutex protected code is not necessarily
* required/expected to complete quickly, as no other sytemwide locks are held on account of a locked mutex.
*
* Because they are not re-entrant on the same core, blocking on a mutex should never be done in an IRQ
* handler. It is valid to call \ref mutex_try_enter from within an IRQ handler, if the operation
* that would be conducted under lock can be skipped if the mutex is locked (at least by the same core).
*
* See \ref critical_section.h for protecting access between multiple cores AND IRQ handlers
*/
typedef struct __packed_aligned mutex {
lock_core_t core;
int8_t owner; //! core number or -1 for unowned
} mutex_t;
/*! \brief Initialise a mutex structure
* \ingroup mutex
*
* \param mtx Pointer to mutex structure
*/
void mutex_init(mutex_t *mtx);
/*! \brief Take ownership of a mutex
* \ingroup mutex
*
* This function will block until the calling core can claim ownership of the mutex.
* On return the caller core owns the mutex
*
* \param mtx Pointer to mutex structure
*/
void mutex_enter_blocking(mutex_t *mtx);
/*! \brief Check to see if a mutex is available
* \ingroup mutex
*
* Will return true if the mutex is unowned, false otherwise
*
* \param mtx Pointer to mutex structure
* \param owner_out If mutex is owned, and this pointer is non-zero, it will be filled in with the core number of the current owner of the mutex
*/
bool mutex_try_enter(mutex_t *mtx, uint32_t *owner_out);
/*! \brief Wait for mutex with timeout
* \ingroup mutex
*
* Wait for up to the specific time to take ownership of the mutex. If the calling
* core can take ownership of the mutex before the timeout expires, then true will be returned
* and the calling core will own the mutex, otherwise false will be returned and the calling
* core will *NOT* own the mutex.
*
* \param mtx Pointer to mutex structure
* \param timeout_ms The timeout in milliseconds.
* \return true if mutex now owned, false if timeout occurred before mutex became available
*/
bool mutex_enter_timeout_ms(mutex_t *mtx, uint32_t timeout_ms);
/*! \brief Wait for mutex until a specific time
* \ingroup mutex
*
* Wait until the specific time to take ownership of the mutex. If the calling
* core can take ownership of the mutex before the timeout expires, then true will be returned
* and the calling core will own the mutex, otherwise false will be returned and the calling
* core will *NOT* own the mutex.
*
* \param mtx Pointer to mutex structure
* \param until The time after which to return if the core cannot take owner ship of the mutex
* \return true if mutex now owned, false if timeout occurred before mutex became available
*/
bool mutex_enter_block_until(mutex_t *mtx, absolute_time_t until);
/*! \brief Release ownership of a mutex
* \ingroup mutex
*
* \param mtx Pointer to mutex structure
*/
void mutex_exit(mutex_t *mtx);
/*! \brief Test for mutex initialised state
* \ingroup mutex
*
* \param mtx Pointer to mutex structure
* \return true if the mutex is initialised, false otherwise
*/
static inline bool mutex_is_initialzed(mutex_t *mtx) {
return mtx->core.spin_lock != 0;
}
/*! \brief Helper macro for static definition of mutexes
* \ingroup mutex
*
* A mutex defined as follows:
*
* ```c
* auto_init_mutex(my_mutex);
* ```
*
* Is equivalent to doing
*
* ```c
* static mutex_t my_mutex;
*
* void my_init_function() {
* mutex_init(&my_mutex);
* }
* ```
*
* But the initialization of the mutex is performed automatically during runtime initialization
*/
#define auto_init_mutex(name) static __attribute__((section(".mutex_array"))) mutex_t name
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,101 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PLATFORM_SEM_H
#define _PLATFORM_SEM_H
#include "pico/lock_core.h"
/** \file sem.h
* \defgroup sem sem
* \ingroup pico_sync
* \brief Semaphore API for restricting access to a resource
*
* A semaphore holds a number of available permits. `sem_acquire` methods will acquire a permit if available
* (reducing the available count by 1) or block if the number of available permits is 0.
* \ref sem_release() increases the number of available permits by one potentially unblocking a `sem_acquire` method.
*
* Note that \ref sem_release() may be called an arbitrary number of times, however the number of available
* permits is capped to the max_permit value specified during semaphore initialization.
*
* Although these semaphore related functions can be used from IRQ handlers, it is obviously preferable to only
* release semaphores from within an IRQ handler (i.e. avoid blocking)
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct __packed_aligned semaphore {
struct lock_core core;
int16_t permits;
int16_t max_permits;
} semaphore_t;
/*! \brief Initialise a semaphore structure
* \ingroup sem
*
* \param sem Pointer to semaphore structure
* \param initial_permits How many permits are initially acquired
* \param max_permits Total number of permits allowed for this semaphore
*/
void sem_init(semaphore_t *sem, int16_t initial_permits, int16_t max_permits);
/*! \brief Return number of available permits on the semaphore
* \ingroup sem
*
* \param sem Pointer to semaphore structure
* \return The number of permits available on the semaphore.
*/
int sem_available(semaphore_t *sem);
/*! \brief Release a permit on a semaphore
* \ingroup sem
*
* Increases the number of permits by one (unless the number of permits is already at the maximum).
* A blocked `sem_acquire` will be released if the number of permits is increased.
*
* \param sem Pointer to semaphore structure
* \return true if the number of permits available was increased.
*/
bool sem_release(semaphore_t *sem);
/*! \brief Reset semaphore to a specific number of available permits
* \ingroup sem
*
* Reset value should be from 0 to the max_permits specified in the init function
*
* \param sem Pointer to semaphore structure
* \param permits the new number of available permits
*/
void sem_reset(semaphore_t *sem, int16_t permits);
/*! \brief Acquire a permit from the semaphore
* \ingroup sem
*
* This function will block and wait if no permits are available.
*
* \param sem Pointer to semaphore structure
*/
void sem_acquire_blocking(semaphore_t *sem);
/*! \brief Acquire a permit from a semaphore, with timeout
* \ingroup sem
*
* This function will block and wait if no permits are available, until the
* defined timeout has been reached. If the timeout is reached the function will
* return false, otherwise it will return true.
*
* \param sem Pointer to semaphore structure
* \param timeout_ms Time to wait to acquire the semaphore, in ms.
* \return false if timeout reached, true if permit was acquired.
*/
bool sem_acquire_timeout_ms(semaphore_t *sem, uint32_t timeout_ms);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_SYNC_H
#define _PICO_SYNC_H
/** \file pico/sync.h
* \defgroup pico_sync pico_sync
* Synchronization primitives and mutual exclusion
*/
#include "pico/sem.h"
#include "pico/mutex.h"
#include "pico/critical_section.h"
#endif

View File

@ -0,0 +1,13 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/lock_core.h"
void lock_init(lock_core_t *core, uint lock_num) {
assert(lock_num >= 0 && lock_num < NUM_SPIN_LOCKS);
core->spin_lock = spin_lock_instance(lock_num);
}

View File

@ -0,0 +1,79 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/mutex.h"
#include "pico/time.h"
#if !PICO_NO_HARDWARE
static_assert(sizeof(mutex_t) == 8, "");
#endif
void mutex_init(mutex_t *mtx) {
lock_init(&mtx->core, next_striped_spin_lock_num());
mtx->owner = -1;
__mem_fence_release();
}
void __time_critical_func(mutex_enter_blocking)(mutex_t *mtx) {
assert(mtx->core.spin_lock);
bool block = true;
do {
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
if (mtx->owner < 0) {
mtx->owner = get_core_num();
block = false;
}
spin_unlock(mtx->core.spin_lock, save);
if (block) {
__wfe();
}
} while (block);
}
bool __time_critical_func(mutex_try_enter)(mutex_t *mtx, uint32_t *owner_out) {
bool entered;
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
if (mtx->owner < 0) {
mtx->owner = get_core_num();
entered = true;
} else {
if (owner_out) *owner_out = mtx->owner;
entered = false;
}
spin_unlock(mtx->core.spin_lock, save);
return entered;
}
bool __time_critical_func(mutex_enter_timeout_ms)(mutex_t *mtx, uint32_t timeout_ms) {
return mutex_enter_block_until(mtx, make_timeout_time_ms(timeout_ms));
}
bool __time_critical_func(mutex_enter_block_until)(mutex_t *mtx, absolute_time_t until) {
assert(mtx->core.spin_lock);
bool block = true;
do {
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
if (mtx->owner < 0) {
mtx->owner = get_core_num();
block = false;
}
spin_unlock(mtx->core.spin_lock, save);
if (block) {
if (best_effort_wfe_or_timeout(until)) {
return false;
}
}
} while (block);
return true;
}
void __time_critical_func(mutex_exit)(mutex_t *mtx) {
uint32_t save = spin_lock_blocking(mtx->core.spin_lock);
assert(mtx->owner >= 0);
mtx->owner = -1;
__sev();
spin_unlock(mtx->core.spin_lock, save);
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/sem.h"
#include "pico/time.h"
void sem_init(semaphore_t *sem, int16_t initial_permits, int16_t max_permits) {
lock_init(&sem->core, next_striped_spin_lock_num());
sem->permits = initial_permits;
sem->max_permits = max_permits;
__mem_fence_release();
}
int __time_critical_func(sem_available)(semaphore_t *sem) {
return *(volatile typeof(sem->permits) *) &sem->permits;
}
void __time_critical_func(sem_acquire_blocking)(semaphore_t *sem) {
bool block = true;
do {
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
if (sem->permits > 0) {
sem->permits--;
__sev();
block = false;
}
spin_unlock(sem->core.spin_lock, save);
if (block) {
__wfe();
}
} while (block);
}
bool __time_critical_func(sem_acquire_timeout_ms)(semaphore_t *sem, uint32_t timeout_ms) {
bool block = true;
absolute_time_t target = nil_time;
do {
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
if (sem->permits > 0) {
sem->permits--;
__sev();
block = false;
}
spin_unlock(sem->core.spin_lock, save);
if (block) {
if (is_nil_time(target)) {
target = make_timeout_time_ms(timeout_ms);
}
if (best_effort_wfe_or_timeout(target)) {
return false;
}
}
} while (block);
return true;
}
// todo this should really have a blocking variant for when permits are maxed out
bool __time_critical_func(sem_release)(semaphore_t *sem) {
bool rc;
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
int32_t count = sem->permits;
if (count < sem->max_permits) {
sem->permits = count + 1;
__sev();
rc = true;
} else {
rc = false;
}
spin_unlock(sem->core.spin_lock, save);
return rc;
}
void __time_critical_func(sem_reset)(semaphore_t *sem, int16_t permits) {
assert(permits >= 0 && permits <= sem->max_permits);
uint32_t save = spin_lock_blocking(sem->core.spin_lock);
if (permits > sem->permits) __sev();
sem->permits = permits;
spin_unlock(sem->core.spin_lock, save);
}

View File

@ -0,0 +1,16 @@
if (NOT TARGET pico_time_headers)
add_library(pico_time_headers INTERFACE)
target_include_directories(pico_time_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_time_headers INTERFACE hardware_timer)
endif()
if (NOT TARGET pico_time)
add_library(pico_time INTERFACE)
target_sources(pico_time INTERFACE
${CMAKE_CURRENT_LIST_DIR}/time.c
${CMAKE_CURRENT_LIST_DIR}/timeout_helper.c)
target_link_libraries(pico_time INTERFACE pico_time_headers pico_sync pico_util)
endif()

View File

@ -0,0 +1,696 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_TIME_H
#define _PICO_TIME_H
#include "pico.h"
#include "hardware/timer.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file time.h
* \defgroup pico_time pico_time
*
* API for accurate timestamps, sleeping, and time based callbacks
*
* \note The functions defined here provide a much more powerful and user friendly wrapping around the
* low level hardware timer functionality. For these functions (and any other SDK functionality
* e.g. timeouts, that relies on them) to work correctly, the hardware timer should not be modified. i.e. it is expected
* to be monotonically increasing once per microsecond. Fortunately there is no need to modify the hardware
* timer as any functionality you can think of that isn't already covered here can easily be modelled
* by adding or subtracting a constant value from the unmodified hardware timer.
*
* \sa \ref hardware_timer
*/
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_TIME, Enable/disable assertions in the time module, type=bool, default=0, group=pico_time
#ifndef PARAM_ASSERTIONS_ENABLED_TIME
#define PARAM_ASSERTIONS_ENABLED_TIME 0
#endif
// PICO_CONFIG: PICO_TIME_SLEEP_OVERHEAD_ADJUST_US, How many microseconds to wake up early (and then busy_wait) to account for timer overhead when sleeping in low power mode, type=int, default=6, group=pico_time
#ifndef PICO_TIME_SLEEP_OVERHEAD_ADJUST_US
#define PICO_TIME_SLEEP_OVERHEAD_ADJUST_US 6
#endif
/*!
* \defgroup timestamp timestamp
* \ingroup pico_time
* \brief Timestamp functions relating to points in time (including the current time)
*
* These are functions for dealing with timestamps (i.e. instants in time) represented by the type absolute_time_t. This opaque
* type is provided to help prevent accidental mixing of timestamps and relative time values.
*/
/*! \brief Return a representation of the current time.
* \ingroup timestamp
*
* Returns an opaque high fidelity representation of the current time sampled during the call.
*
* \return the absolute time (now) of the hardware timer
*
* \sa absolute_time_t
* \sa sleep_until()
* \sa time_us_64()
*/
static inline absolute_time_t get_absolute_time() {
absolute_time_t t;
update_us_since_boot(&t, time_us_64());
return t;
}
static inline uint32_t us_to_ms(uint64_t us) {
if (us >> 32u) {
return (uint32_t)(us / 1000u);
} else {
return ((uint32_t)us) / 1000u;
}
}
/*! fn to_ms_since_boot
* \ingroup timestamp
* \brief Convert a timestamp into a number of milliseconds since boot.
* \param t an absolute_time_t value to convert
* \return the number of microseconds since boot represented by t
* \sa to_us_since_boot
*/
static inline uint32_t to_ms_since_boot(absolute_time_t t) {
uint64_t us = to_us_since_boot(t);
return us_to_ms(us);
}
/*! \brief Return a timestamp value obtained by adding a number of microseconds to another timestamp
* \ingroup timestamp
*
* \param t the base timestamp
* \param us the number of microseconds to add
* \return the timestamp representing the resulting time
*/
static inline absolute_time_t delayed_by_us(const absolute_time_t t, uint64_t us) {
absolute_time_t t2;
uint64_t base = to_us_since_boot(t);
uint64_t delayed = base + us;
if (delayed < base) {
delayed = (uint64_t)-1;
}
update_us_since_boot(&t2, delayed);
return t2;
}
/*! \brief Return a timestamp value obtained by adding a number of milliseconds to another timestamp
* \ingroup timestamp
*
* \param t the base timestamp
* \param ms the number of milliseconds to add
* \return the timestamp representing the resulting time
*/
static inline absolute_time_t delayed_by_ms(const absolute_time_t t, uint32_t ms) {
absolute_time_t t2;
uint64_t base = to_us_since_boot(t);
uint64_t delayed = base + ms * 1000ull;
if (delayed < base) {
delayed = (uint64_t)-1;
}
update_us_since_boot(&t2, delayed);
return t2;
}
/*! \brief Convenience method to get the timestamp a number of microseconds from the current time
* \ingroup timestamp
*
* \param us the number of microseconds to add to the current timestamp
* \return the future timestamp
*/
static inline absolute_time_t make_timeout_time_us(uint64_t us) {
return delayed_by_us(get_absolute_time(), us);
}
/*! \brief Convenience method to get the timestamp a number of milliseconds from the current time
* \ingroup timestamp
*
* \param ms the number of milliseconds to add to the current timestamp
* \return the future timestamp
*/
static inline absolute_time_t make_timeout_time_ms(uint32_t ms) {
return delayed_by_ms(get_absolute_time(), ms);
}
/*! \brief Return the difference in microseconds between two timestamps
* \ingroup timestamp
*
* \note be careful when diffing against large timestamps (e.g. \ref at_the_end_of_time)
* as the signed integer may overflow.
*
* \param from the first timestamp
* \param to the second timestamp
* \return the number of microseconds between the two timestamps (positive if `to` is after `from` except
* in case of overflow)
*/
static inline int64_t absolute_time_diff_us(absolute_time_t from, absolute_time_t to) {
return to_us_since_boot(to) - to_us_since_boot(from);
}
/*! \brief The timestamp representing the end of time; no timestamp is after this
* \ingroup timestamp
*/
extern const absolute_time_t at_the_end_of_time;
/*! \brief The timestamp representing a null timestamp
* \ingroup timestamp
*/
extern const absolute_time_t nil_time;
/*! \brief Determine if the given timestamp is nil
* \ingroup timestamp
* \param t the timestamp
* \return true if the timestamp is nil
* \sa nil_time()
*/
static inline bool is_nil_time(absolute_time_t t) {
return !to_us_since_boot(t);
}
/*!
* \defgroup sleep sleep
* \ingroup pico_time
* \brief Sleep functions for delaying execution in a lower power state.
*
* These functions allow the calling core to sleep. This is a lower powered sleep; waking and re-checking time on every processor
* event (WFE)
*
* \note These functions should not be called from an IRQ handler.
*
* \note Lower powered sleep requires use of the \link alarm_pool_get_default default alarm pool\endlink which may
* be disabled by the #PICO_TIME_DEFAULT_ALARM_POOL_DISABLED define or currently full in which case these functions
* become busy waits instead.
*
* \note Whilst \a sleep_ functions are preferable to \a busy_wait functions from a power perspective, the \a busy_wait equivalent function
* may return slightly sooner after the target is reached.
*
* \sa busy_wait_until() \sa busy_wait_us() \sa busy_wait_us_32()
*/
/*! \brief Wait until after the given timestamp to return
* \ingroup sleep
*
* \note This method attempts to perform a lower power (WFE) sleep
*
* \param target the time after which to return
* \sa sleep_us()
* \sa busy_wait_until()
* */
void sleep_until(absolute_time_t target);
/*! \brief Wait for the given number of microseconds before returning
* \ingroup sleep
*
* \note This method attempts to perform a lower power (WFE) sleep
*
* \param us the number of microseconds to sleep
* \sa busy_wait_us()
*/
void sleep_us(uint64_t us);
/*! \brief Wait for the given number of milliseconds before returning
* \ingroup sleep
*
* \note This method attempts to perform a lower power sleep (using WFE) as much as possible.
*
* \param ms the number of milliseconds to sleep
*/
void sleep_ms(uint32_t ms);
/*! \brief Helper method for blocking on a timeout
* \ingroup sleep
*
* This method will return in response to a an event (as per __wfe) or
* when the target time is reached, or at any point before.
*
* This method can be used to implement a lower power polling loop waiting on
* some condition signalled by an event (__sev()).
*
* This is called \a best_effort because under certain circumstances (notably the default timer pool
* being disabled or full) the best effort is simply to return immediately without a __wfe, thus turning the calling
* code into a busy wait.
*
* Example usage:
* ```c
* bool my_function_with_timeout_us(uint64_t timeout_us) {
* absolute_time_t timeout_time = make_timeout_time_us(timeout_us);
* do {
* // each time round the loop, we check to see if the condition
* // we are waiting on has happened
* if (my_check_done()) {
* // do something
* return true;
* }
* // will try to sleep until timeout or the next processor event
* } while (!best_effort_wfe_or_timeout(timeout_time));
* return false; // timed out
* }
* ```
*
* @param timeout_timestamp the timeout time
* @return true if the target time is reached, false otherwise
*/
bool best_effort_wfe_or_timeout(absolute_time_t timeout_timestamp);
/*!
* \defgroup alarm alarm
* \ingroup pico_time
* \brief Alarm functions for scheduling future execution
*
* Alarms are added to alarm pools, which may hold a certain fixed number of active alarms. Each alarm pool
* utilizes one of four underlying hardware alarms, thus you may have up to four alarm pools. An alarm pool
* calls (except when the callback would happen before or during being set) the callback on the core from which
* the alarm pool was created. Callbacks are called from the hardware alarm IRQ handler, so care must
* be taken in their implementation.
*
* A default pool is created the core specified by PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM
* on core 0, and may be used by the method variants that take no alarm pool parameter.
*
* \sa struct alarm_pool
* \sa hardware_timer
*/
// PICO_CONFIG: PICO_TIME_DEFAULT_ALARM_POOL_DISABLED, Disable the default alarm pool, type=bool, default=0, advanced=true, group=pico_time
#ifndef PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
/*!
* \brief If 1 then the default alarm pool is disabled (so no hardware alarm is claimed for the pool)
*
* \note Setting to 1 may cause some code not to compile as default timer pool related methods are removed
*
* \note When the default alarm pool is disabled, \a sleep_ methods and timeouts are no longer lower powered
* (they become \a busy_wait_)
*
* \ingroup alarm
* \sa #PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM
* \sa alarm_pool_get_default()
*/
#define PICO_TIME_DEFAULT_ALARM_POOL_DISABLED 0
#endif
// PICO_CONFIG: PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM, Select which HW alarm is used for the default alarm pool, min=0, max=3, default=3, advanced=true, group=pico_time
#ifndef PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM
/*!
* \brief Selects which hardware alarm is used for the default alarm pool
* \ingroup alarm
* \sa alarm_pool_get_default()
*/
#define PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM 3
#endif
// PICO_CONFIG: PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS, Selects the maximum number of concurrent timers in the default alarm pool, min=0, max=255, default=16, advanced=true, group=pico_time
#ifndef PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS
/*!
* \brief Selects the maximum number of concurrent timers in the default alarm pool
* \ingroup alarm
*
* \note For implementation reasons this is limited to PICO_PHEAP_MAX_ENTRIES which defaults to 255
* \sa #PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM
* \sa alarm_pool_get_default()
*/
#define PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS 16
#endif
/**
* \brief The identifier for an alarm
*
* \note this identifier is signed because -1 is used as an error condition when creating alarms
*
* \note alarm ids may be reused, however for convenience the implementation makes an attempt to defer
* reusing as long as possible. You should certainly expect it to be hundreds of ids before one is
* reused, although in most cases it is more. Nonetheless care must still be taken when cancelling
* alarms or other functionality based on alarms when the alarm may have expired, as eventually
* the alarm id may be reused for another alarm.
*
* \ingroup alarm
*/
typedef int32_t alarm_id_t; // note this is signed because we use -1 as a meaningful error value
/**
* \brief User alarm callback
* \ingroup alarm
* \param id the alarm_id as returned when the alarm was added
* \param user_data the user data passed when the alarm was added
* \return <0 to reschedule the same alarm this many us from the time the alarm was previously scheduled to fire
* \return >0 to reschedule the same alarm this many us from the time this method returns
* \return 0 to not reschedule the alarm
*/
typedef int64_t (*alarm_callback_t)(alarm_id_t id, void *user_data);
typedef struct alarm_pool alarm_pool_t;
/**
* \brief Create the default alarm pool (if not already created or disabled)
* \ingroup alarm
*/
void alarm_pool_init_default();
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
/*!
* \brief The default alarm pool used when alarms are added without specifying an alarm pool,
* and also used by the SDK to support lower power sleeps and timeouts.
*
* \ingroup alarm
* \sa #PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM
*/
alarm_pool_t *alarm_pool_get_default();
#endif
/**
* \brief Create an alarm pool
*
* The alarm pool will call callbacks from an alarm IRQ Handler on the core of this function is called from.
*
* In many situations there is never any need for anything other than the default alarm pool, however you
* might want to create another if you want alarm callbacks on core 1 or require alarm pools of
* different priority (IRQ priority based preemption of callbacks)
*
* \note This method will hard assert if the hardware alarm is already claimed.
*
* \ingroup alarm
* \param hardware_alarm_num the hardware alarm to use to back this pool
* \param max_timers the maximum number of timers
* \note For implementation reasons this is limited to PICO_PHEAP_MAX_ENTRIES which defaults to 255
* \sa alarm_pool_get_default()
* \sa hardware_claiming
*/
alarm_pool_t *alarm_pool_create(uint hardware_alarm_num, uint max_timers);
/**
* \brief Return the hardware alarm used by an alarm pool
* \ingroup alarm
* \param pool the pool
* \return the hardware alarm used by the pool
*/
uint alarm_pool_hardware_alarm_num(alarm_pool_t *pool);
/**
* \brief Destroy the alarm pool, cancelling all alarms and freeing up the underlying hardware alarm
* \ingroup alarm
* \param pool the pool
* \return the hardware alarm used by the pool
*/
void alarm_pool_destroy(alarm_pool_t *pool);
/*!
* \brief Add an alarm callback to be called at a specific time
* \ingroup alarm
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core the alarm pool was created on. If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param pool the alarm pool to use for scheduling the callback (this determines which hardware alarm is used, and which core calls the callback)
* @param time the timestamp when (after which) the callback should fire
* @param callback the callback function
* @param user_data user data to pass to the callback function
* @param fire_if_past if true, this method will call the callback itself before returning 0 if the timestamp happens before or during this method call
* @return >0 the alarm id
* @return 0 the target timestamp was during or before this method call (whether the callback was called depends on fire_if_past)
* @return -1 if there were no alarm slots available
*/
alarm_id_t alarm_pool_add_alarm_at(alarm_pool_t *pool, absolute_time_t time, alarm_callback_t callback, void *user_data, bool fire_if_past);
/*!
* \brief Add an alarm callback to be called after a delay specified in microseconds
* \ingroup alarm
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core the alarm pool was created on. If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param pool the alarm pool to use for scheduling the callback (this determines which hardware alarm is used, and which core calls the callback)
* @param us the delay (from now) in microseconds when (after which) the callback should fire
* @param callback the callback function
* @param user_data user data to pass to the callback function
* @param fire_if_past if true, this method will call the callback itself before returning 0 if the timestamp happens before or during this method call
* @return >0 the alarm id
* @return 0 the target timestamp was during or before this method call (whether the callback was called depends on fire_if_past)
* @return -1 if there were no alarm slots available
*/
static inline alarm_id_t alarm_pool_add_alarm_in_us(alarm_pool_t *pool, uint64_t us, alarm_callback_t callback, void *user_data, bool fire_if_past) {
return alarm_pool_add_alarm_at(pool, delayed_by_us(get_absolute_time(), us), callback, user_data, fire_if_past);
}
/*!
* \brief Add an alarm callback to be called after a delay specified in milliseconds
* \ingroup alarm
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core the alarm pool was created on. If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param pool the alarm pool to use for scheduling the callback (this determines which hardware alarm is used, and which core calls the callback)
* @param ms the delay (from now) in milliseconds when (after which) the callback should fire
* @param callback the callback function
* @param user_data user data to pass to the callback function
* @param fire_if_past if true, this method will call the callback itself before returning 0 if the timestamp happens before or during this method call
* @return >0 the alarm id
* @return 0 the target timestamp was during or before this method call (whether the callback was called depends on fire_if_past)
* @return -1 if there were no alarm slots available
*/
static inline alarm_id_t alarm_pool_add_alarm_in_ms(alarm_pool_t *pool, uint32_t ms, alarm_callback_t callback, void *user_data, bool fire_if_past) {
return alarm_pool_add_alarm_at(pool, delayed_by_ms(get_absolute_time(), ms), callback, user_data, fire_if_past);
}
/*!
* \brief Cancel an alarm
* \ingroup alarm
* \param pool the alarm_pool containing the alarm
* \param alarm_id the alarm
* \return true if the alarm was cancelled, false if it didn't exist
* \sa alarm_id_t for a note on reuse of IDs
*/
bool alarm_pool_cancel_alarm(alarm_pool_t *pool, alarm_id_t alarm_id);
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
/*!
* \brief Add an alarm callback to be called at a specific time
* \ingroup alarm
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core of the default alarm pool (generally core 0). If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param time the timestamp when (after which) the callback should fire
* @param callback the callback function
* @param user_data user data to pass to the callback function
* @param fire_if_past if true, this method will call the callback itself before returning 0 if the timestamp happens before or during this method call
* @return >0 the alarm id
* @return 0 the target timestamp was during or before this method call (whether the callback was called depends on fire_if_past)
* @return -1 if there were no alarm slots available
*/
static inline alarm_id_t add_alarm_at(absolute_time_t time, alarm_callback_t callback, void *user_data, bool fire_if_past) {
return alarm_pool_add_alarm_at(alarm_pool_get_default(), time, callback, user_data, fire_if_past);
}
/*!
* \brief Add an alarm callback to be called after a delay specified in microseconds
* \ingroup alarm
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core of the default alarm pool (generally core 0). If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param us the delay (from now) in microseconds when (after which) the callback should fire
* @param callback the callback function
* @param user_data user data to pass to the callback function
* @param fire_if_past if true, this method will call the callback itself before returning 0 if the timestamp happens before or during this method call
* @return >0 the alarm id
* @return 0 the target timestamp was during or before this method call (whether the callback was called depends on fire_if_past)
* @return -1 if there were no alarm slots available
*/
static inline alarm_id_t add_alarm_in_us(uint64_t us, alarm_callback_t callback, void *user_data, bool fire_if_past) {
return alarm_pool_add_alarm_in_us(alarm_pool_get_default(), us, callback, user_data, fire_if_past);
}
/*!
* \brief Add an alarm callback to be called after a delay specified in milliseconds
* \ingroup alarm
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core of the default alarm pool (generally core 0). If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param ms the delay (from now) in milliseconds when (after which) the callback should fire
* @param callback the callback function
* @param user_data user data to pass to the callback function
* @param fire_if_past if true, this method will call the callback itself before returning 0 if the timestamp happens before or during this method call
* @return >0 the alarm id
* @return 0 the target timestamp was during or before this method call (whether the callback was called depends on fire_if_past)
* @return -1 if there were no alarm slots available
*/
static inline alarm_id_t add_alarm_in_ms(uint32_t ms, alarm_callback_t callback, void *user_data, bool fire_if_past) {
return alarm_pool_add_alarm_in_ms(alarm_pool_get_default(), ms, callback, user_data, fire_if_past);
}
/*!
* \brief Cancel an alarm from the default alarm pool
* \ingroup alarm
* \param alarm_id the alarm
* \return true if the alarm was cancelled, false if it didn't exist
* \sa alarm_id_t for a note on reuse of IDs
*/
static inline bool cancel_alarm(alarm_id_t alarm_id) {
return alarm_pool_cancel_alarm(alarm_pool_get_default(), alarm_id);
}
#endif
/*!
* \defgroup repeating_timer repeating_timer
* \ingroup pico_time
* \brief Repeating Timer functions for simple scheduling of repeated execution
*
* \note The regular \a alarm_ functionality can be used to make repeating alarms (by return non zero from the callback),
* however these methods abstract that further (at the cost of a user structure to store the repeat delay in (which
* the alarm framework does not have space for).
*/
typedef struct repeating_timer repeating_timer_t;
/**
* \brief Callback for a repeating timer
* \ingroup repeating_timer
* \param rt repeating time structure containing information about the repeating time. user_data is of primary important to the user
* \return true to continue repeating, false to stop.
*/
typedef bool (*repeating_timer_callback_t)(repeating_timer_t *rt);
/**
* \brief Information about a repeating timer
* \ingroup repeating_timer
* \return
*/
struct repeating_timer {
int64_t delay_us;
alarm_pool_t *pool;
alarm_id_t alarm_id;
repeating_timer_callback_t callback;
void *user_data;
};
/*!
* \brief Add a repeating timer that is called repeatedly at the specified interval in microseconds
* \ingroup repeating_timer
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core the alarm pool was created on. If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param pool the alarm pool to use for scheduling the repeating timer (this determines which hardware alarm is used, and which core calls the callback)
* @param delay_us the repeat delay in microseconds; if >0 then this is the delay between one callback ending and the next starting; if <0 then this is the negative of the time between the starts of the callbacks. The value of 0 is treated as 1
* @param callback the repeating timer callback function
* @param user_data user data to pass to store in the repeating_timer structure for use by the callback.
* @param out the pointer to the user owned structure to store the repeating timer info in. BEWARE this storage location must outlive the repeating timer, so be careful of using stack space
* @return false if there were no alarm slots available to create the timer, true otherwise.
*/
bool alarm_pool_add_repeating_timer_us(alarm_pool_t *pool, int64_t delay_us, repeating_timer_callback_t callback, void *user_data, repeating_timer_t *out);
/*!
* \brief Add a repeating timer that is called repeatedly at the specified interval in milliseconds
* \ingroup repeating_timer
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core the alarm pool was created on. If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param pool the alarm pool to use for scheduling the repeating timer (this determines which hardware alarm is used, and which core calls the callback)
* @param delay_ms the repeat delay in milliseconds; if >0 then this is the delay between one callback ending and the next starting; if <0 then this is the negative of the time between the starts of the callbacks. The value of 0 is treated as 1 microsecond
* @param callback the repeating timer callback function
* @param user_data user data to pass to store in the repeating_timer structure for use by the callback.
* @param out the pointer to the user owned structure to store the repeating timer info in. BEWARE this storage location must outlive the repeating timer, so be careful of using stack space
* @return false if there were no alarm slots available to create the timer, true otherwise.
*/
static inline bool alarm_pool_add_repeating_timer_ms(alarm_pool_t *pool, int32_t delay_ms, repeating_timer_callback_t callback, void *user_data, repeating_timer_t *out) {
return alarm_pool_add_repeating_timer_us(pool, delay_ms * (int64_t)1000, callback, user_data, out);
}
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
/*!
* \brief Add a repeating timer that is called repeatedly at the specified interval in microseconds
* \ingroup repeating_timer
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core of the default alarm pool (generally core 0). If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param delay_us the repeat delay in microseconds; if >0 then this is the delay between one callback ending and the next starting; if <0 then this is the negative of the time between the starts of the callbacks. The value of 0 is treated as 1
* @param callback the repeating timer callback function
* @param user_data user data to pass to store in the repeating_timer structure for use by the callback.
* @param out the pointer to the user owned structure to store the repeating timer info in. BEWARE this storage location must outlive the repeating timer, so be careful of using stack space
* @return false if there were no alarm slots available to create the timer, true otherwise.
*/
static inline bool add_repeating_timer_us(int64_t delay_us, repeating_timer_callback_t callback, void *user_data, repeating_timer_t *out) {
return alarm_pool_add_repeating_timer_us(alarm_pool_get_default(), delay_us, callback, user_data, out);
}
/*!
* \brief Add a repeating timer that is called repeatedly at the specified interval in milliseconds
* \ingroup repeating_timer
*
* Generally the callback is called as soon as possible after the time specified from an IRQ handler
* on the core of the default alarm pool (generally core 0). If the callback is in the past or happens before
* the alarm setup could be completed, then this method will optionally call the callback itself
* and then return a return code to indicate that the target time has passed.
*
* \note It is safe to call this method from an IRQ handler (including alarm callbacks), and from either core.
*
* @param delay_ms the repeat delay in milliseconds; if >0 then this is the delay between one callback ending and the next starting; if <0 then this is the negative of the time between the starts of the callbacks. The value of 0 is treated as 1 microsecond
* @param callback the repeating timer callback function
* @param user_data user data to pass to store in the repeating_timer structure for use by the callback.
* @param out the pointer to the user owned structure to store the repeating timer info in. BEWARE this storage location must outlive the repeating timer, so be careful of using stack space
* @return false if there were no alarm slots available to create the timer, true otherwise.
*/
static inline bool add_repeating_timer_ms(int32_t delay_ms, repeating_timer_callback_t callback, void *user_data, repeating_timer_t *out) {
return alarm_pool_add_repeating_timer_us(alarm_pool_get_default(), delay_ms * (int64_t)1000, callback, user_data, out);
}
#endif
/**
* \brief Cancel a repeating timer
* \ingroup repeating_timer
* \param timer the repeating timer to cancel
* \return true if the repeating timer was cancelled, false if it didn't exist
* \sa alarm_id_t for a note on reuse of IDs
*/
bool cancel_repeating_timer(repeating_timer_t *timer);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_TIMEOUT_HELPER_H
#define _PICO_TIMEOUT_HELPER_H
#include "pico/time.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct timeout_state {
absolute_time_t next_timeout;
uint64_t param;
} timeout_state_t;
typedef bool (*check_timeout_fn)(timeout_state_t *ts);
check_timeout_fn init_single_timeout_until(timeout_state_t *ts, absolute_time_t target);
check_timeout_fn init_per_iteration_timeout_us(timeout_state_t *ts, uint64_t per_iteration_timeout_us);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,353 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include "pico.h"
#include "pico/time.h"
#include "pico/util/pheap.h"
#include "hardware/sync.h"
#include "hardware/gpio.h"
const absolute_time_t ABSOLUTE_TIME_INITIALIZED_VAR(nil_time, 0);
const absolute_time_t ABSOLUTE_TIME_INITIALIZED_VAR(at_the_end_of_time, ULONG_MAX);
typedef struct alarm_pool_entry {
absolute_time_t target;
alarm_callback_t callback;
void *user_data;
} alarm_pool_entry_t;
typedef struct alarm_pool {
pheap_t *heap;
spin_lock_t *lock;
alarm_pool_entry_t *entries;
// one byte per entry, used to provide more longevity to public IDs than heap node ids do
// (this is increment every time the heap node id is re-used)
uint8_t *entry_ids_high;
alarm_id_t alarm_in_progress; // this is set during a callback from the IRQ handler... it can be cleared by alarm_cancel to prevent repeats
uint8_t hardware_alarm_num;
} alarm_pool_t;
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
static alarm_pool_t *default_alarm_pool;
#endif
static alarm_pool_t *pools[NUM_TIMERS];
void alarm_pool_init_default() {
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
// allow multiple calls for ease of use from host tests
if (!default_alarm_pool) {
default_alarm_pool = alarm_pool_create(PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM,
PICO_TIME_DEFAULT_ALARM_POOL_MAX_TIMERS);
}
#endif
}
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
alarm_pool_t *alarm_pool_get_default() {
assert(default_alarm_pool);
return default_alarm_pool;
}
#endif
static inline alarm_pool_entry_t *get_entry(alarm_pool_t *pool, pheap_node_id_t id) {
assert(id && id <= pool->heap->max_nodes);
return pool->entries + id - 1;
}
static inline uint8_t *get_entry_id_high(alarm_pool_t *pool, pheap_node_id_t id) {
assert(id && id <= pool->heap->max_nodes);
return pool->entry_ids_high + id - 1;
}
bool timer_pool_entry_comparator(void *user_data, pheap_node_id_t a, pheap_node_id_t b) {
alarm_pool_t *pool = (alarm_pool_t *)user_data;
return to_us_since_boot(get_entry(pool, a)->target) < to_us_since_boot(get_entry(pool, b)->target);
}
static inline alarm_id_t make_public_id(uint8_t id_high, pheap_node_id_t id) {
return ((uint)id_high << 8u * sizeof(id)) | id;
}
static alarm_id_t add_alarm_under_lock(alarm_pool_t *pool, absolute_time_t time, alarm_callback_t callback,
void *user_data, alarm_id_t reuse_id, bool create_if_past, bool *missed) {
alarm_id_t id;
if (reuse_id) {
assert(!ph_contains(pool->heap, reuse_id));
id = reuse_id;
} else {
id = ph_new_node(pool->heap);
}
if (id) {
alarm_pool_entry_t *entry = get_entry(pool, id);
entry->target = time;
entry->callback = callback;
entry->user_data = user_data;
if (id == ph_insert(pool->heap, id)) {
bool is_missed = hardware_alarm_set_target(pool->hardware_alarm_num, time);
if (is_missed && !create_if_past) {
ph_delete(pool->heap, id);
}
if (missed) *missed = is_missed;
}
}
return id;
}
static void alarm_pool_alarm_callback(uint alarm_num) {
// note this is called from timer IRQ handler
alarm_pool_t *pool = pools[alarm_num];
bool again;
do {
absolute_time_t now = get_absolute_time();
alarm_callback_t callback = NULL;
absolute_time_t target = nil_time;
void *user_data = NULL;
uint8_t id_high;
again = false;
uint32_t save = spin_lock_blocking(pool->lock);
pheap_node_id_t next_id = ph_peek_head(pool->heap);
if (next_id) {
alarm_pool_entry_t *entry = get_entry(pool, next_id);
if (absolute_time_diff_us(now, entry->target) <= 0) {
// we reserve the id in case we need to re-add the timer
pheap_node_id_t __unused removed_id = ph_remove_head_reserve(pool->heap, true);
assert(removed_id == next_id); // will be true under lock
target = entry->target;
callback = entry->callback;
user_data = entry->user_data;
assert(callback);
id_high = *get_entry_id_high(pool, next_id);
pool->alarm_in_progress = make_public_id(id_high, removed_id);
} else {
if (hardware_alarm_set_target(alarm_num, entry->target)) {
again = true;
}
}
}
spin_unlock(pool->lock, save);
if (callback) {
int64_t repeat = callback(make_public_id(id_high, next_id), user_data);
save = spin_lock_blocking(pool->lock);
// todo think more about whether we want to keep calling
if (repeat < 0 && pool->alarm_in_progress) {
assert(pool->alarm_in_progress == make_public_id(id_high, next_id));
add_alarm_under_lock(pool, delayed_by_us(target, -repeat), callback, user_data, next_id, true, NULL);
} else if (repeat > 0 && pool->alarm_in_progress) {
assert(pool->alarm_in_progress == make_public_id(id_high, next_id));
add_alarm_under_lock(pool, delayed_by_us(get_absolute_time(), repeat), callback, user_data, next_id,
true, NULL);
} else {
// need to return the id to the heap
ph_add_to_free_list(pool->heap, next_id);
(*get_entry_id_high(pool, next_id))++; // we bump it for next use of id
}
pool->alarm_in_progress = 0;
spin_unlock(pool->lock, save);
again = true;
}
} while (again);
}
// note the timer is create with IRQs on this core
alarm_pool_t *alarm_pool_create(uint hardware_alarm_num, uint max_timers) {
hardware_alarm_claim(hardware_alarm_num);
hardware_alarm_cancel(hardware_alarm_num);
hardware_alarm_set_callback(hardware_alarm_num, alarm_pool_alarm_callback);
alarm_pool_t *pool = (alarm_pool_t *)malloc(sizeof(alarm_pool_t));
pool->lock = spin_lock_instance(next_striped_spin_lock_num());
pool->heap = ph_create(max_timers, timer_pool_entry_comparator, pool);
pool->entries = (alarm_pool_entry_t *)calloc(max_timers, sizeof(alarm_pool_entry_t));
pool->entry_ids_high = (uint8_t *)calloc(max_timers, sizeof(uint8_t));
pool->hardware_alarm_num = hardware_alarm_num;
pools[hardware_alarm_num] = pool;
return pool;
}
void alarm_pool_destroy(alarm_pool_t *pool) {
assert(pools[pool->hardware_alarm_num] == pool);
pools[pool->hardware_alarm_num] = NULL;
// todo clear out timers
ph_destroy(pool->heap);
hardware_alarm_set_callback(pool->hardware_alarm_num, NULL);
hardware_alarm_unclaim(pool->hardware_alarm_num);
free(pool->entry_ids_high);
free(pool->entries);
free(pool);
}
alarm_id_t alarm_pool_add_alarm_at(alarm_pool_t *pool, absolute_time_t time, alarm_callback_t callback,
void *user_data, bool fire_if_past) {
bool missed = false;
uint public_id;
do {
uint8_t id_high = 0;
uint32_t save = spin_lock_blocking(pool->lock);
pheap_node_id_t id = add_alarm_under_lock(pool, time, callback, user_data, 0, false, &missed);
if (id) id_high = *get_entry_id_high(pool, id);
spin_unlock(pool->lock, save);
if (!id) {
return -1;
}
public_id = missed ? 0 : make_public_id(id_high, id);
if (missed && fire_if_past) {
int64_t repeat = callback(public_id, user_data);
if (!repeat) {
public_id = 0;
break;
} else if (repeat < 0) {
time = delayed_by_us(time, -repeat);
} else {
time = delayed_by_us(get_absolute_time(), repeat);
}
} else {
break;
}
} while (true);
return public_id;
}
bool alarm_pool_cancel_alarm(alarm_pool_t *pool, alarm_id_t alarm_id) {
bool rc = false;
uint32_t save = spin_lock_blocking(pool->lock);
pheap_node_id_t id = (pheap_node_id_t) alarm_id;
if (ph_contains(pool->heap, id)) {
assert(alarm_id != pool->alarm_in_progress); // it shouldn't be in the heap if it is in progress
// check we have the right high value
uint8_t id_high = (uint8_t)((uint)alarm_id >> 8u * sizeof(pheap_node_id_t));
if (id_high == *get_entry_id_high(pool, id)) {
rc = ph_delete(pool->heap, id);
// note we don't bother to remove the actual hardware alarm timeout...
// it will either do callbacks or not depending on other alarms, and reset the next timeout itself
assert(rc);
}
} else {
if (alarm_id == pool->alarm_in_progress) {
// make sure the alarm doesn't repeat
pool->alarm_in_progress = 0;
}
}
spin_unlock(pool->lock, save);
return rc;
}
uint alarm_pool_hardware_alarm_num(alarm_pool_t *pool) {
return pool->hardware_alarm_num;
}
static void alarm_pool_dump_key(pheap_node_id_t id, void *user_data) {
alarm_pool_t *pool = (alarm_pool_t *)user_data;
#if PICO_ON_DEVICE
printf("%lld (hi %02x)", to_us_since_boot(get_entry(pool, id)->target), *get_entry_id_high(pool, id));
#else
printf("%ld", to_us_since_boot(get_entry(pool, id)->target));
#endif
}
static int64_t repeating_timer_callback(alarm_id_t id, void *user_data) {
repeating_timer_t *rt = (repeating_timer_t *)user_data;
if (rt->callback(rt)) {
return rt->delay_us;
} else {
rt->alarm_id = 0;
return 0;
}
}
bool alarm_pool_add_repeating_timer_us(alarm_pool_t *pool, int64_t delay_us, repeating_timer_callback_t callback, void *user_data, repeating_timer_t *out) {
if (!delay_us) delay_us = 1;
out->pool = pool;
out->callback = callback;
out->delay_us = delay_us;
out->user_data = user_data;
out->alarm_id = alarm_pool_add_alarm_at(pool, make_timeout_time_us(delay_us >= 0 ? delay_us : -delay_us), repeating_timer_callback, out, true);
return out->alarm_id > 0;
}
bool cancel_repeating_timer(repeating_timer_t *timer) {
bool rc = false;
if (timer->alarm_id) {
rc = alarm_pool_cancel_alarm(timer->pool, timer->alarm_id);
timer->alarm_id = 0;
}
return rc;
}
void alarm_pool_dump(alarm_pool_t *pool) {
uint32_t save = spin_lock_blocking(pool->lock);
ph_dump(pool->heap, alarm_pool_dump_key, pool);
spin_unlock(pool->lock, save);
}
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
static int64_t sev_callback(alarm_id_t id, void *user_data) {
__sev();
return 0;
}
#endif
void sleep_until(absolute_time_t t) {
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
uint64_t t_us = to_us_since_boot(t);
uint64_t t_before_us = t_us - PICO_TIME_SLEEP_OVERHEAD_ADJUST_US;
// needs to work in the first PICO_TIME_SLEEP_OVERHEAD_ADJUST_US of boot
if (t_before_us > t_us) t_before_us = 0;
absolute_time_t t_before;
update_us_since_boot(&t_before, t_before_us);
if (absolute_time_diff_us(get_absolute_time(), t_before) > 0) {
if (add_alarm_at(t_before, sev_callback, NULL, false) >= 0) {
// able to add alarm for just before the time
while (!time_reached(t_before)) {
__wfe();
}
}
}
#endif
// now wait until the exact time
busy_wait_until(t);
}
void sleep_us(uint64_t us) {
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
sleep_until(make_timeout_time_us(us));
#else
if (us >> 32u) {
busy_wait_until(make_timeout_time_us(us));
} else {
busy_wait_us_32(us);
}
#endif
}
void sleep_ms(uint32_t ms) {
sleep_us(ms * 1000ull);
}
bool best_effort_wfe_or_timeout(absolute_time_t timeout_timestamp) {
#if !PICO_TIME_DEFAULT_ALARM_POOL_DISABLED
alarm_id_t id;
id = add_alarm_at(timeout_timestamp, sev_callback, NULL, false);
if (id <= 0) {
tight_loop_contents();
return time_reached(timeout_timestamp);
} else {
__wfe();
// we need to clean up if it wasn't us that caused the wfe; if it was this will be a noop.
cancel_alarm(id);
return time_reached(timeout_timestamp);
}
#else
tight_loop_contents();
return time_reached(timeout_timestamp);
#endif
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/timeout_helper.h"
static bool check_single_timeout_us(timeout_state_t *ts) {
return time_reached(ts->next_timeout);
}
check_timeout_fn init_single_timeout_until(timeout_state_t *ts, absolute_time_t target) {
ts->next_timeout = target;
return check_single_timeout_us;
}
static bool check_per_iteration_timeout_us(timeout_state_t *ts) {
if (time_reached(ts->next_timeout)) {
return true;
}
ts->next_timeout = make_timeout_time_us(ts->param);
return false;
}
check_timeout_fn init_per_iteration_timeout_us(timeout_state_t *ts, uint64_t per_iteration_timeout_us) {
ts->next_timeout = make_timeout_time_us(per_iteration_timeout_us);
ts->param = per_iteration_timeout_us;
return check_per_iteration_timeout_us;
}

View File

@ -0,0 +1,15 @@
if (NOT TARGET pico_util_headers)
add_library(pico_util_headers INTERFACE)
target_include_directories(pico_util_headers INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(pico_util_headers INTERFACE pico_base_headers hardware_sync)
endif()
if (NOT TARGET pico_util)
add_library(pico_util INTERFACE)
target_sources(pico_util INTERFACE
${CMAKE_CURRENT_LIST_DIR}/datetime.c
${CMAKE_CURRENT_LIST_DIR}/pheap.c
${CMAKE_CURRENT_LIST_DIR}/queue.c
)
target_link_libraries(pico_util INTERFACE pico_util_headers)
endif()

View File

@ -0,0 +1,41 @@
#include "pico/util/datetime.h"
#include <stdio.h>
static const char *DATETIME_MONTHS[12] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
static const char *DATETIME_DOWS[7] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
};
void datetime_to_str(char *buf, uint buf_size, const datetime_t *t) {
snprintf(buf,
buf_size,
"%s %d %s %d:%02d:%02d %d",
DATETIME_DOWS[t->dotw],
t->day,
DATETIME_MONTHS[t->month - 1],
t->hour,
t->min,
t->sec,
t->year);
};

View File

@ -0,0 +1,4 @@
/**
* \defgroup pico_util pico_util
* \brief Useful data structures and utility functions
*/

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_DATETIME_H
#define _PICO_DATETIME_H
#include "pico.h"
/** \file datetime.h
* \defgroup util_datetime datetime
* \brief Date/Time formatting
* \ingroup pico_util
*/
/*! \brief Convert a datetime_t structure to a string
* \ingroup util_datetime
*
* \param buf character buffer to accept generated string
* \param buf_size The size of the passed in buffer
* \param t The datetime to be converted.
*/
void datetime_to_str(char *buf, uint buf_size, const datetime_t *t);
#endif

View File

@ -0,0 +1,155 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _PICO_UTIL_PHEAP_H
#define _PICO_UTIL_PHEAP_H
#include "pico.h"
#ifdef __cplusplus
extern "C" {
#endif
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PHEAP, Enable/disable assertions in the pheap module, type=bool, default=0, group=pico_util
#ifndef PARAM_ASSERTIONS_ENABLED_PHEAP
#define PARAM_ASSERTIONS_ENABLED_PHEAP 0
#endif
/**
* \file pheap.h
* \defgroup util_pheap pheap
* Pairing Heap Implementation
* \ingroup pico_util
*
* pheap defines a simple pairing heap. the implementation simply tracks array indexes, it is up to
* the user to provide storage for heap entries and a comparison function.
*
* NOTE: this class is not safe for concurrent usage. It should be externally protected. Furthermore
* if used concurrently, the caller needs to protect around their use of the returned id.
* for example, ph_remove_head returns the id of an element that is no longer in the heap.
*
* The user can still use this to look at the data in their companion array, however obviously further operations
* on the heap may cause them to overwrite that data as the id may be reused on subsequent operations
*
*/
// PICO_CONFIG: PICO_PHEAP_MAX_ENTRIES, Maximum number of entries in the pheap, min=1, max=65534, default=255, group=pico_util
#ifndef PICO_PHEAP_MAX_ENTRIES
#define PICO_PHEAP_MAX_ENTRIES 255
#endif
// public heap_node ids are numbered from 1 (0 means none)
#if PICO_PHEAP_MAX_ENTRIES < 256
typedef uint8_t pheap_node_id_t;
#elif PICO_PHEAP_MAX_ENTRIES < 65535
typedef uint16_t pheap_node_id_t;
#else
#error invalid PICO_PHEAP_MAX_ENTRIES
#endif
typedef struct pheap_node {
pheap_node_id_t child, sibling, parent;
} pheap_node_t;
// return true if a < b in natural order
typedef bool (*pheap_comparator)(void *user_data, pheap_node_id_t a, pheap_node_id_t b);
typedef struct pheap {
pheap_node_t *nodes;
pheap_comparator comparator;
void *user_data;
pheap_node_id_t max_nodes;
pheap_node_id_t root_id;
// we remove from head and add to tail to stop reusing the same ids
pheap_node_id_t free_head_id;
pheap_node_id_t free_tail_id;
} pheap_t;
pheap_t *ph_create(uint max_nodes, pheap_comparator comparator, void *user_data);
void ph_clear(pheap_t *heap);
void ph_destroy(pheap_t *heap);
static inline pheap_node_t *ph_get_node(pheap_t *heap, pheap_node_id_t id) {
assert(id && id <= heap->max_nodes);
return heap->nodes + id - 1;
}
static void ph_add_child_node(pheap_t *heap, pheap_node_id_t parent_id, pheap_node_id_t child_id) {
pheap_node_t *n = ph_get_node(heap, parent_id);
assert(parent_id);
assert(child_id);
assert(parent_id != child_id);
pheap_node_t *c = ph_get_node(heap, child_id);
c->parent = parent_id;
if (!n->child) {
n->child = child_id;
} else {
c->sibling = n->child;
n->child = child_id;
}
}
static pheap_node_id_t ph_merge_nodes(pheap_t *heap, pheap_node_id_t a, pheap_node_id_t b) {
if (!a) return b;
if (!b) return a;
if (heap->comparator(heap->user_data, a, b)) {
ph_add_child_node(heap, a, b);
return a;
} else {
ph_add_child_node(heap, b, a);
return b;
}
}
static inline pheap_node_id_t ph_new_node(pheap_t *heap) {
if (!heap->free_head_id) return 0;
pheap_node_id_t id = heap->free_head_id;
heap->free_head_id = ph_get_node(heap, id)->sibling;
if (!heap->free_head_id) heap->free_tail_id = 0;
return id;
}
// note this will callback the comparator for the node
// returns the (new) root of the heap
static inline pheap_node_id_t ph_insert(pheap_t *heap, pheap_node_id_t id) {
assert(id);
pheap_node_t *hn = ph_get_node(heap, id);
hn->child = hn->sibling = hn->parent = 0;
heap->root_id = ph_merge_nodes(heap, heap->root_id, id);
return heap->root_id;
}
static inline pheap_node_id_t ph_peek_head(pheap_t *heap) {
return heap->root_id;
}
pheap_node_id_t ph_remove_head_reserve(pheap_t *heap, bool reserve);
static inline pheap_node_id_t ph_remove_head(pheap_t *heap) {
return ph_remove_head_reserve(heap, false);
}
static inline bool ph_contains(pheap_t *heap, pheap_node_id_t id) {
return id == heap->root_id || ph_get_node(heap, id)->parent;
}
bool ph_delete(pheap_t *heap, pheap_node_id_t id);
static inline void ph_add_to_free_list(pheap_t *heap, pheap_node_id_t id) {
assert(id && !ph_contains(heap, id));
if (heap->free_tail_id) {
ph_get_node(heap, heap->free_tail_id)->sibling = id;
}
heap->free_tail_id = id;
}
void ph_dump(pheap_t *heap, void (*dump_key)(pheap_node_id_t, void *), void *user_data);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,184 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _UTIL_QUEUE_H
#define _UTIL_QUEUE_H
#include "pico.h"
#include "hardware/sync.h"
/** \file queue.h
* \defgroup queue queue
* Multi-core and IRQ safe queue implementation.
*
* Note that this queue stores values of a specified size, and pushed values are copied into the queue
* \ingroup pico_util
*/
typedef struct {
spin_lock_t *lock;
uint8_t *data;
uint16_t wptr;
uint16_t rptr;
uint16_t element_size;
uint16_t element_count;
} queue_t;
/*! \brief Initialise a queue with a specific spinlock for concurrency protection
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param element_size Size of each value in the queue
* \param element_count Maximum number of entries in the queue
* \param spinlock_num The spin ID used to protect the queue
*/
void queue_init_with_spinlock(queue_t *q, uint element_size, uint element_count, uint spinlock_num);
/*! \brief Initialise a queue, allocating a (possibly shared) spinlock
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param element_size Size of each value in the queue
* \param element_count Maximum number of entries in the queue
*/
static inline void queue_init(queue_t *q, uint element_size, uint element_count) {
return queue_init_with_spinlock(q, element_size, element_count, next_striped_spin_lock_num());
}
/*! \brief Destroy the specified queue.
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
*
* Does not deallocate the queue_t structure itself.
*/
void queue_free(queue_t *q);
/*! \brief Unsafe check of level of the specified queue.
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \return Number of entries in the queue
*
* This does not use the spinlock, so may return incorrect results if the
* spin lock is not externally locked
*/
static inline uint queue_get_level_unsafe(queue_t *q) {
int32_t rc = (int32_t)q->wptr - (int32_t)q->rptr;
if (rc < 0) {
rc += + q->element_count + 1;
}
return rc;
}
/*! \brief Check of level of the specified queue.
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \return Number of entries in the queue
*/
static inline uint queue_get_level(queue_t *q) {
uint32_t save = spin_lock_blocking(q->lock);
uint level = queue_get_level_unsafe(q);
spin_unlock(q->lock, save);
return level;
}
/*! \brief Check if queue is empty
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \return true if queue is empty, false otherwise
*
* This function is interrupt and multicore safe.
*/
static inline bool queue_is_empty(queue_t *q) {
return queue_get_level(q) == 0;
}
/*! \brief Check if queue is full
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \return true if queue is full, false otherwise
*
* This function is interrupt and multicore safe.
*/
static inline bool queue_is_full(queue_t *q) {
return queue_get_level(q) == q->element_count;
}
// nonblocking queue access functions:
/*! \brief Non-blocking add value queue if not full
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param data Pointer to value to be copied into the queue
* \return true if the value was added
*
* If the queue is full this function will return immediately with false, otherwise
* the data is copied into a new value added to the queue, and this function will return true.
*/
bool queue_try_add(queue_t *q, void *data);
/*! \brief Non-blocking removal of entry from the queue if non empty
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param data Pointer to the location to receive the removed value
* \return true if a value was removed
*
* If the queue is not empty function will copy the removed value into the location provided and return
* immediately with true, otherwise the function will return immediately with false.
*/
bool queue_try_remove(queue_t *q, void *data);
/*! \brief Non-blocking peek at the next item to be removed from the queue
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param data Pointer to the location to receive the peeked value
* \return true if there was a value to peek
*
* If the queue is not empty this function will return immediately with true with the peeked entry
* copied into the location specified by the data parameter, otherwise the function will return false.
*/
bool queue_try_peek(queue_t *q, void *data);
// blocking queue access functions:
/*! \brief Blocking add of value to queue
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param data Pointer to value to be copied into the queue
*
* If the queue is full this function will block, until a removal happens on the queue
*/
void queue_add_blocking(queue_t *q, void *data);
/*! \brief Blocking remove entry from queue
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param data Pointer to the location to receive the removed value
*
* If the queue is empty this function will block until a value is added.
*/
void queue_remove_blocking(queue_t *q, void *data);
/*! \brief Blocking peek at next value to be removed from queue
* \ingroup queue
*
* \param q Pointer to a queue_t structure, used as a handle
* \param data Pointer to the location to receive the peeked value
*
* If the queue is empty function will block until a value is added
*/
void queue_peek_blocking(queue_t *q, void *data);
#endif

View File

@ -0,0 +1,130 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include "pico/util/pheap.h"
pheap_t *ph_create(uint max_nodes, pheap_comparator comparator, void *user_data) {
invalid_params_if(PHEAP, !max_nodes || max_nodes >= (1u << sizeof(pheap_node_id_t)));
pheap_t *heap = calloc(1, sizeof(pheap_t));
heap->max_nodes = max_nodes;
heap->comparator = comparator;
heap->nodes = calloc(max_nodes, sizeof(pheap_node_t));
heap->user_data = user_data;
ph_clear(heap);
return heap;
}
void ph_clear(pheap_t *heap) {
heap->root_id = 0;
heap->free_head_id = 1;
heap->free_tail_id = heap->max_nodes;
for(uint i = 1; i < heap->max_nodes; i++) {
ph_get_node(heap, i)->sibling = i + 1;
}
ph_get_node(heap, heap->max_nodes)->sibling = 0;
}
void ph_destroy(pheap_t *heap) {
free(heap->nodes);
free(heap);
}
pheap_node_id_t ph_merge_two_pass(pheap_t *heap, pheap_node_id_t id) {
if (!id || !ph_get_node(heap, id)->sibling) {
return id;
} else {
pheap_node_id_t a, b, new_node;
a = id;
b = ph_get_node(heap, id)->sibling;
new_node = ph_get_node(heap, b)->sibling;
ph_get_node(heap, a)->sibling = ph_get_node(heap, b)->sibling = 0;
return ph_merge_nodes(heap, ph_merge_nodes(heap, a, b), ph_merge_two_pass(heap, new_node));
}
}
static pheap_node_id_t ph_remove_any_head(pheap_t *heap, pheap_node_id_t root_id, bool reserve) {
assert(root_id);
// printf("Removing head %d (parent %d sibling %d)\n", root_id, ph_get_node(heap, root_id)->parent, ph_get_node(heap, root_id)->sibling);
assert(!ph_get_node(heap, root_id)->sibling);
assert(!ph_get_node(heap, root_id)->parent);
pheap_node_id_t new_root_id = ph_merge_two_pass(heap, ph_get_node(heap, root_id)->child);
if (!reserve) {
if (heap->free_tail_id) {
ph_get_node(heap, heap->free_tail_id)->sibling = root_id;
}
heap->free_tail_id = root_id;
}
if (new_root_id) ph_get_node(heap, new_root_id)->parent = 0;
ph_get_node(heap, root_id)->sibling = 0;
return new_root_id;
}
pheap_node_id_t ph_remove_head_reserve(pheap_t *heap, bool reserve) {
pheap_node_id_t old_root_id = ph_peek_head(heap);
heap->root_id = ph_remove_any_head(heap, old_root_id, reserve);
return old_root_id;
}
#include <stdio.h>
bool ph_delete(pheap_t *heap, pheap_node_id_t id) {
// 1) trivial cases
if (!id) return false;
if (id == heap->root_id) {
ph_remove_head(heap);
return true;
}
// 2) unlink the node from the tree
pheap_node_t *node = ph_get_node(heap, id);
if (!node->parent) return false; // not in tree
pheap_node_t *parent = ph_get_node(heap, node->parent);
if (parent->child == id) {
parent->child = node->sibling;
} else {
pheap_node_id_t prev_sibling_id = parent->child;
bool __unused found = false;
do {
pheap_node_t *prev_sibling = ph_get_node(heap, prev_sibling_id);
if (prev_sibling->sibling == id) {
prev_sibling->sibling = node->sibling;
found = true;
break;
}
prev_sibling_id = prev_sibling->sibling;
} while (prev_sibling_id);
assert(found);
}
node->sibling = node->parent = 0;
// ph_dump(heap, NULL, NULL);
// 3) remove it from the head of its own subtree
pheap_node_id_t new_sub_tree = ph_remove_any_head(heap, id, false);
assert(new_sub_tree != heap->root_id);
heap->root_id = ph_merge_nodes(heap, heap->root_id, new_sub_tree);
return true;
}
static uint ph_dump_node(pheap_t *heap, pheap_node_id_t id, void (*dump_key)(pheap_node_id_t, void *), void *user_data, uint indent) {
uint count = 0;
if (id) {
count++;
for (uint i = 0; i < indent * 2; i++) {
putchar(' ');
}
pheap_node_t *node = ph_get_node(heap, id);
printf("%d (c=%d s=%d p=%d) ", id, node->child, node->sibling, node->parent);
if (dump_key) dump_key(id, user_data);
printf("\n");
count += ph_dump_node(heap, node->child, dump_key, user_data, indent + 1);
count += ph_dump_node(heap, node->sibling, dump_key, user_data, indent);
}
return count;
}
void ph_dump(pheap_t *heap, void (*dump_key)(pheap_node_id_t, void *), void *user_data) {
uint count = ph_dump_node(heap, heap->root_id, dump_key, user_data, 0);
printf("node_count %d\n", count);
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <string.h>
#include "pico/util/queue.h"
void queue_init_with_spinlock(queue_t *q, uint element_size, uint element_count, uint spinlock_num) {
q->lock = spin_lock_instance(spinlock_num);
q->data = (uint8_t *)calloc(element_count + 1, element_size);
q->element_count = element_count;
q->element_size = element_size;
q->wptr = 0;
q->rptr = 0;
}
void queue_free(queue_t *q) {
free(q->data);
}
static inline void *element_ptr(queue_t *q, uint index) {
assert(index <= q->element_count);
return q->data + index * q->element_size;
}
static inline uint16_t inc_index(queue_t *q, uint16_t index) {
if (++index > q->element_count) { // > because we have element_count + 1 elements
index = 0;
}
return index;
}
bool queue_try_add(queue_t *q, void *data) {
bool success = false;
uint32_t flags = spin_lock_blocking(q->lock);
if (queue_get_level_unsafe(q) != q->element_count) {
memcpy(element_ptr(q, q->wptr), data, q->element_size);
q->wptr = inc_index(q, q->wptr);
success = true;
}
spin_unlock(q->lock, flags);
if (success) __sev();
return success;
}
bool queue_try_remove(queue_t *q, void *data) {
bool success = false;
uint32_t flags = spin_lock_blocking(q->lock);
if (queue_get_level_unsafe(q) != 0) {
memcpy(data, element_ptr(q, q->rptr), q->element_size);
q->rptr = inc_index(q, q->rptr);
success = true;
}
spin_unlock(q->lock, flags);
if (success) __sev();
return success;
}
bool queue_try_peek(queue_t *q, void *data) {
bool success = false;
uint32_t flags = spin_lock_blocking(q->lock);
if (queue_get_level_unsafe(q) != 0) {
memcpy(data, element_ptr(q, q->rptr), q->element_size);
success = true;
}
spin_unlock(q->lock, flags);
return success;
}
void queue_add_blocking(queue_t *q, void *data) {
bool done;
do {
done = queue_try_add(q, data);
if (done) break;
__wfe();
} while (true);
}
void queue_remove_blocking(queue_t *q, void *data) {
bool done;
do {
done = queue_try_remove(q, data);
if (done) break;
__wfe();
} while (true);
}
void queue_peek_blocking(queue_t *q, void *data) {
bool done;
do {
done = queue_try_peek(q, data);
if (done) break;
__wfe();
} while (true);
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
// ---------------------------------------
// THIS FILE IS AUTOGENERATED; DO NOT EDIT
// ---------------------------------------
#ifndef _PICO_VERSION_H
#define _PICO_VERSION_H
#define PICO_SDK_VERSION_MAJOR 1
#define PICO_SDK_VERSION_MINOR 0
#define PICO_SDK_VERSION_REVISION 1
#define PICO_SDK_VERSION_STRING "1.0.1"
#endif

View File

@ -0,0 +1,10 @@
# For targeting the host for testing purposes
function(pico_add_extra_outputs TARGET)
endfunction()
set(PICO_NO_HARDWARE "1" CACHE INTERNAL "")
set(PICO_ON_DEVICE "0" CACHE INTERNAL "")
add_subdirectory(common)
add_subdirectory(host)

View File

@ -0,0 +1,28 @@
pico_add_subdirectory(hardware_divider)
pico_add_subdirectory(hardware_gpio)
pico_add_subdirectory(hardware_sync)
pico_add_subdirectory(hardware_timer)
pico_add_subdirectory(hardware_uart)
pico_add_subdirectory(pico_bit_ops)
pico_add_subdirectory(pico_divider)
pico_add_subdirectory(pico_multicore)
pico_add_subdirectory(pico_platform)
pico_add_subdirectory(pico_printf)
pico_add_subdirectory(pico_stdio)
pico_add_subdirectory(pico_stdlib)
pico_add_doxygen(${CMAKE_CURRENT_LIST_DIR})
macro(pico_set_float_implementation TARGET IMPL)
endmacro()
macro(pico_set_double_implementation TARGET IMPL)
endmacro()
macro(pico_set_boot_stage2 TARGET IMPL)
endmacro()
set(PICO_HOST_DIR "${CMAKE_CURRENT_LIST_DIR}" CACHE INTERNAL "")
function(pico_define_boot_stage2 NAME)
add_executable(${NAME} ${PICO_HOST_DIR}/boot_stage2.c)
endfunction()

View File

@ -0,0 +1,14 @@
This is a basic set of replacement library implementations sufficient to get simple applications
running on your computer (Raspberry Pi OS, Linux, macOS or Windows using Cygwin or Windows Subsystem for Linux).
It is selected by `PICO_PLATFORM=host` in your CMake build
This can be extremely useful for testing and debugging higher level application code, or porting code which is not yet small enough
to run on the RP2040 device itself.
This base level host library provides a minimal environment to compile programs, but is likely sufficient for programs
that don't access hardware directly.
It is possible however to inject additional SDK library implementations/simulations to provide
more complete functionality. For an example of this see the [pico-host-sdl](https://github.com/raspberrypi/pico-host-sdl)
which uses the SDL2 library to add additional library support for pico_multicore, timers/alarms in pico-time and
pico-audio/pico-scanvideo from [pico-extras](https://github.com/raspberrypi/pico-extras)

View File

@ -0,0 +1 @@
// empty

View File

@ -0,0 +1 @@
pico_simple_hardware_target(divider)

View File

@ -0,0 +1,9 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "hardware/divider.h"
__thread uint64_t hw_divider_result_threadlocal;

View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_DIVIDER_H
#define _HARDWARE_DIVIDER_H
#include "pico/types.h"
typedef uint64_t divmod_result_t;
static inline int __sign_of(int32_t v) {
return v > 0 ? 1 : (v < 0 ? -1 : 0);
}
// divides unsigned values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
static inline uint64_t hw_divider_divmod_u32(uint32_t a, uint32_t b) {
if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-1); // todo check this
return (((uint64_t)(a%b))<<32u) | (a/b);
}
// divides signed values a by b... (a/b) returned in low 32 bits, (a%b) in high 32 bits... results undefined for b==0
static inline uint64_t hw_divider_divmod_s32(int32_t a, int32_t b) {
if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-__sign_of(a));
return (((uint64_t)(a%b))<<32u) | (uint32_t)(a/b);
}
extern __thread divmod_result_t hw_divider_result_threadlocal;
static inline void hw_divider_divmod_s32_start(int32_t a, int32_t b) {
hw_divider_result_threadlocal = hw_divider_divmod_s32(a, b);
}
static inline void hw_divider_divmod_u32_start(uint32_t a, uint32_t b) {
hw_divider_result_threadlocal = hw_divider_divmod_u32(a, b);
}
static inline divmod_result_t hw_divider_result_wait() {
return hw_divider_result_threadlocal;
}
static inline uint64_t hw_divider_result_nowait() {
return hw_divider_result_threadlocal;
}
inline static uint32_t to_quotient_u32(unsigned long long int r) {
return (uint32_t) r;
}
inline static int32_t to_quotient_s32(unsigned long long int r) {
return (int32_t)(uint32_t)r;
}
inline static uint32_t to_remainder_u32(unsigned long long int r) {
return (uint32_t)(r >> 32u);
}
inline static int32_t to_remainder_s32(unsigned long long int r) {
return (int32_t)(r >> 32u);
}
static inline uint32_t hw_divider_u32_quotient_wait() {
return to_quotient_u32(hw_divider_result_wait());
}
static inline uint32_t hw_divider_u32_remainder_wait() {
return to_remainder_u32(hw_divider_result_wait());
}
static inline int32_t hw_divider_s32_quotient_wait() {
return to_quotient_s32(hw_divider_result_wait());
}
static inline int32_t hw_divider_s32_remainder_wait() {
return to_remainder_s32(hw_divider_result_wait());
}
static inline uint32_t hw_divider_u32_quotient(uint32_t a, uint32_t b) {
return b ? (a / b) : -1;
}
static inline uint32_t hw_divider_u32_remainder(uint32_t a, uint32_t b) {
return b ? (a % b) : a;
}
static inline int32_t hw_divider_s32_quotient(int32_t a, int32_t b) {
return b ? (a / b) : -__sign_of(a);
}
static inline int32_t hw_divider_s32_remainder(int32_t a, int32_t b) {
return b ? (a % b) : a;
}
static inline uint32_t hw_divider_u32_quotient_inlined(uint32_t a, uint32_t b) {
return hw_divider_u32_quotient(a,b);
}
static inline uint32_t hw_divider_u32_remainder_inlined(uint32_t a, uint32_t b) {
return hw_divider_u32_remainder(a,b);
}
static inline int32_t hw_divider_s32_quotient_inlined(int32_t a, int32_t b) {
return hw_divider_s32_quotient(a,b);
}
static inline int32_t hw_divider_s32_remainder_inlined(int32_t a, int32_t b) {
return hw_divider_s32_remainder(a,b);
}
typedef uint64_t hw_divider_state_t;
static inline void hw_divider_save_state(hw_divider_state_t *dest) {
*dest = hw_divider_result_threadlocal;
}
static inline void hw_divider_restore_state(hw_divider_state_t *src) {
hw_divider_result_threadlocal = *src;
}
#endif // _HARDWARE_DIVIDER_H

View File

@ -0,0 +1 @@
pico_simple_hardware_target(gpio)

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "hardware/gpio.h"
// todo weak or replace? probably weak
void gpio_set_function(uint gpio, enum gpio_function fn) {
}
void gpio_pull_up(uint gpio) {
}
void gpio_pull_down(uint gpio) {
}
void gpio_disable_pulls(uint gpio) {
}
void gpio_set_pulls(uint gpio, bool up, bool down) {
}
void gpio_set_outover(uint gpio, uint value) {
}
void gpio_set_inover(uint gpio, uint value) {
}
void gpio_set_oeover(uint gpio, uint value) {
}
void gpio_set_irq_enabled(uint gpio, uint32_t events, bool enable) {
}
void gpio_acknowledge_irq(uint gpio, uint32_t events) {
}
void gpio_init(uint gpio) {
}
PICO_WEAK_FUNCTION_DEF(gpio_get)
bool PICO_WEAK_FUNCTION_IMPL_NAME(gpio_get)(uint gpio) {
return 0;
}
uint32_t gpio_get_all() {
return 0;
}
void gpio_set_mask(uint32_t mask) {
}
void gpio_clr_mask(uint32_t mask) {
}
void gpio_xor_mask(uint32_t mask) {
}
void gpio_put_masked(uint32_t mask, uint32_t value) {
}
void gpio_put_all(uint32_t value) {
}
void gpio_put(uint gpio, int value) {
}
void gpio_set_dir_out_masked(uint32_t mask) {
}
void gpio_set_dir_in_masked(uint32_t mask) {
}
void gpio_set_dir_masked(uint32_t mask, uint32_t value) {
}
void gpio_set_dir_all_bits(uint32_t value) {
}
void gpio_set_dir(uint gpio, bool out) {
}
void gpio_debug_pins_init() {
}
void gpio_set_input_enabled(uint gpio, bool enable) {
}
void gpio_init_mask(uint gpio_mask) {
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_GPIO_H_
#define _HARDWARE_GPIO_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "pico.h"
enum gpio_function {
GPIO_FUNC_XIP = 0,
GPIO_FUNC_SPI = 1,
GPIO_FUNC_UART = 2,
GPIO_FUNC_I2C = 3,
GPIO_FUNC_PWM = 4,
GPIO_FUNC_SIO = 5,
GPIO_FUNC_PIO0 = 6,
GPIO_FUNC_PIO1 = 7,
GPIO_FUNC_GPCK = 8,
GPIO_FUNC_USB = 9,
GPIO_FUNC_NULL = 0xf,
};
#define GPIO_OUT 1
#define GPIO_IN 0
#define NUM_BANK0_GPIOS 30
// ----------------------------------------------------------------------------
// Pad Controls + IO Muxing
// ----------------------------------------------------------------------------
// Declarations for gpio.c
void gpio_set_function(uint gpio, enum gpio_function fn);
enum gpio_function gpio_get_function(uint gpio);
void gpio_pull_up(uint gpio);
void gpio_pull_down(uint gpio);
void gpio_disable_pulls(uint gpio);
void gpio_set_pulls(uint gpio, bool up, bool down);
void gpio_set_outover(uint gpio, uint value);
void gpio_set_inover(uint gpio, uint value);
void gpio_set_oeover(uint gpio, uint value);
void gpio_set_input_enabled(uint gpio, bool enable);
// Configure a GPIO for direct input/output from software
void gpio_init(uint gpio);
void gpio_init_mask(uint gpio_mask);
// ----------------------------------------------------------------------------
// Input
// ----------------------------------------------------------------------------
// Get the value of a single GPIO
bool gpio_get(uint gpio);
// Get raw value of all
uint32_t gpio_get_all();
// ----------------------------------------------------------------------------
// Output
// ----------------------------------------------------------------------------
// Drive high every GPIO appearing in mask
void gpio_set_mask(uint32_t mask);
void gpio_clr_mask(uint32_t mask);
// Toggle every GPIO appearing in mask
void gpio_xor_mask(uint32_t mask);
// For each 1 bit in "mask", drive that pin to the value given by
// corresponding bit in "value", leaving other pins unchanged.
// Since this uses the TOGL alias, it is concurrency-safe with e.g. an IRQ
// bashing different pins from the same core.
void gpio_put_masked(uint32_t mask, uint32_t value);
// Drive all pins simultaneously
void gpio_put_all(uint32_t value);
// Drive a single GPIO high/low
void gpio_put(uint gpio, int value);
// ----------------------------------------------------------------------------
// Direction
// ----------------------------------------------------------------------------
// Switch all GPIOs in "mask" to output
void gpio_set_dir_out_masked(uint32_t mask);
// Switch all GPIOs in "mask" to input
void gpio_set_dir_in_masked(uint32_t mask);
// For each 1 bit in "mask", switch that pin to the direction given by
// corresponding bit in "value", leaving other pins unchanged.
// E.g. gpio_set_dir_masked(0x3, 0x2); -> set pin 0 to input, pin 1 to output,
// simultaneously.
void gpio_set_dir_masked(uint32_t mask, uint32_t value);
// Set direction of all pins simultaneously.
// For each bit in value,
// 1 = out
// 0 = in
void gpio_set_dir_all_bits(uint32_t value);
// Set a single GPIO to input/output.
// true = out
// 0 = in
void gpio_set_dir(uint gpio, bool out);
// debugging
#define PICO_DEBUG_PIN_BASE 19u
// note these two macros may only be used once per compilation unit
#define CU_REGISTER_DEBUG_PINS(p, ...)
#define CU_SELECT_DEBUG_PINS(x)
#define DEBUG_PINS_ENABLED(p) false
#define DEBUG_PINS_SET(p, v) ((void)0)
#define DEBUG_PINS_CLR(p, v) ((void)0)
#define DEBUG_PINS_XOR(p, v) ((void)0)
void gpio_debug_pins_init();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,12 @@
pico_simple_hardware_headers_target(sync)
if (NOT TARGET hardware_sync)
add_library(hardware_sync INTERFACE)
target_sources(hardware_sync INTERFACE
${CMAKE_CURRENT_LIST_DIR}/sync_core0_only.c
)
target_link_libraries(hardware_sync INTERFACE hardware_sync_headers pico_platform)
endif()

Some files were not shown because too many files have changed in this diff Show More