1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-12 19:10:32 +03:00

PICO: Add files version.h, usb_cdc.c to get building again.

TODO Replace usb_cdc.c with stdio_usb.c from pico-sdk, with a few wrappers.
Consider using cmake with pico-sdk.
This commit is contained in:
Matthew Selby 2025-05-28 17:24:26 +01:00
parent 9c8543ac35
commit 45708c41f1
2 changed files with 297 additions and 0 deletions

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 2
#define PICO_SDK_VERSION_MINOR 1
#define PICO_SDK_VERSION_REVISION 0
#define PICO_SDK_VERSION_STRING "2.1.0"
#endif

View file

@ -0,0 +1,278 @@
/*
* This file is part of Betaflight.
*
* Betaflight is free software. You can redistribute this software
* and/or modify this software under the terms of the GNU General
* Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later
* version.
*
* Betaflight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this software.
*
* If not, see <http://www.gnu.org/licenses/>.
*/
// TODO replace with stdio_usb from pico-sdk, with a few wrappers
#include "platform.h"
#include "tusb_config.h"
#include "tusb.h"
#include "usb_cdc.h"
#include "pico/binary_info.h"
#include "pico/time.h"
#include "pico/mutex.h"
#include "pico/critical_section.h"
#include "hardware/irq.h"
#ifndef CDC_USB_TASK_INTERVAL_US
#define CDC_USB_TASK_INTERVAL_US 1000
#endif
#ifndef CDC_USB_WRITE_TIMEOUT_US
#define CDC_USB_WRITE_TIMEOUT_US 1000
#endif
#ifndef CDC_DEADLOCK_TIMEOUT_MS
#define CDC_DEADLOCK_TIMEOUT_MS 1000
#endif
#ifndef CDC_USB_BAUD_RATE
#define CDC_USD_BAUD_RATE 115200
#endif
static bool configured = false;
static mutex_t cdc_usb_mutex;
// if this crit_sec is initialized, we are not in periodic timer mode, and must make sure
// we don't either create multiple one shot timers, or miss creating one. this crit_sec
// is used to protect the one_shot_timer_pending flag
static critical_section_t one_shot_timer_crit_sec;
static volatile bool one_shot_timer_pending;
static uint8_t low_priority_irq_num;
static int64_t timer_task(alarm_id_t id, void *user_data)
{
UNUSED(id);
UNUSED(user_data);
int64_t repeat_time;
if (critical_section_is_initialized(&one_shot_timer_crit_sec)) {
critical_section_enter_blocking(&one_shot_timer_crit_sec);
one_shot_timer_pending = false;
critical_section_exit(&one_shot_timer_crit_sec);
repeat_time = 0; // don't repeat
} else {
repeat_time = CDC_USB_TASK_INTERVAL_US;
}
if (irq_is_enabled(low_priority_irq_num)) {
irq_set_pending(low_priority_irq_num);
return repeat_time;
} else {
return 0; // don't repeat
}
}
static void low_priority_worker_irq(void)
{
if (mutex_try_enter(&cdc_usb_mutex, NULL)) {
tud_task();
mutex_exit(&cdc_usb_mutex);
} else {
// if the mutex is already owned, then we are in non IRQ code in this file.
//
// it would seem simplest to just let that code call tud_task() at the end, however this
// code might run during the call to tud_task() and we might miss a necessary tud_task() call
//
// if we are using a periodic timer (crit_sec is not initialized in this case),
// then we are happy just to wait until the next tick, however when we are not using a periodic timer,
// we must kick off a one-shot timer to make sure the tud_task() DOES run (this method
// will be called again as a result, and will try the mutex_try_enter again, and if that fails
// create another one shot timer again, and so on).
if (critical_section_is_initialized(&one_shot_timer_crit_sec)) {
bool need_timer;
critical_section_enter_blocking(&one_shot_timer_crit_sec);
need_timer = !one_shot_timer_pending;
one_shot_timer_pending = true;
critical_section_exit(&one_shot_timer_crit_sec);
if (need_timer) {
add_alarm_in_us(CDC_USB_TASK_INTERVAL_US, timer_task, NULL, true);
}
}
}
}
static void usb_irq(void)
{
irq_set_pending(low_priority_irq_num);
}
int cdc_usb_write(const uint8_t *buf, unsigned length)
{
static uint64_t last_avail_time;
int written = 0;
if (!mutex_try_enter_block_until(&cdc_usb_mutex, make_timeout_time_ms(CDC_DEADLOCK_TIMEOUT_MS))) {
return -1;
}
if (cdc_usb_connected()) {
for (unsigned i = 0; i < length;) {
unsigned n = length - i;
uint32_t avail = tud_cdc_write_available();
if (n > avail) n = avail;
if (n) {
uint32_t n2 = tud_cdc_write(buf + i, n);
tud_task();
tud_cdc_write_flush();
i += n2;
written = i;
last_avail_time = time_us_64();
} else {
tud_task();
tud_cdc_write_flush();
if (!cdc_usb_connected() || (!tud_cdc_write_available() && time_us_64() > last_avail_time + CDC_USB_WRITE_TIMEOUT_US)) {
break;
}
}
}
} else {
// reset our timeout
last_avail_time = 0;
}
mutex_exit(&cdc_usb_mutex);
return written;
}
void cdc_usb_write_flush(void)
{
if (!mutex_try_enter_block_until(&cdc_usb_mutex, make_timeout_time_ms(CDC_DEADLOCK_TIMEOUT_MS))) {
return;
}
do {
tud_task();
} while (tud_cdc_write_flush());
mutex_exit(&cdc_usb_mutex);
}
int cdc_usb_read(uint8_t *buf, unsigned length)
{
// note we perform this check outside the lock, to try and prevent possible deadlock conditions
// with printf in IRQs (which we will escape through timeouts elsewhere, but that would be less graceful).
//
// these are just checks of state, so we can call them while not holding the lock.
// they may be wrong, but only if we are in the middle of a tud_task call, in which case at worst
// we will mistakenly think we have data available when we do not (we will check again), or
// tud_task will complete running and we will check the right values the next time.
//
int rc = PICO_ERROR_NO_DATA;
if (cdc_usb_connected() && tud_cdc_available()) {
if (!mutex_try_enter_block_until(&cdc_usb_mutex, make_timeout_time_ms(CDC_DEADLOCK_TIMEOUT_MS))) {
return PICO_ERROR_NO_DATA; // would deadlock otherwise
}
if (cdc_usb_connected() && tud_cdc_available()) {
uint32_t count = tud_cdc_read(buf, length);
rc = count ? (int)count : PICO_ERROR_NO_DATA;
} else {
// because our mutex use may starve out the background task, run tud_task here (we own the mutex)
tud_task();
}
mutex_exit(&cdc_usb_mutex);
}
return rc;
}
bool cdc_usb_init(void)
{
if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) {
// included an assertion here rather than just returning false, as this is likely
// a coding bug, rather than anything else.
assert(false);
return false;
}
// initialize TinyUSB, as user hasn't explicitly linked it
tusb_init();
if (!mutex_is_initialized(&cdc_usb_mutex)) {
mutex_init(&cdc_usb_mutex);
}
bool rc = true;
low_priority_irq_num = (uint8_t)user_irq_claim_unused(true);
irq_set_exclusive_handler(low_priority_irq_num, low_priority_worker_irq);
irq_set_enabled(low_priority_irq_num, true);
if (irq_has_shared_handler(USBCTRL_IRQ)) {
critical_section_init_with_lock_num(&one_shot_timer_crit_sec, spin_lock_claim_unused(true));
// we can use a shared handler to notice when there may be work to do
irq_add_shared_handler(USBCTRL_IRQ, usb_irq, PICO_SHARED_IRQ_HANDLER_LOWEST_ORDER_PRIORITY);
} else {
// we use initialization state of the one_shot_timer_critsec as a flag
memset(&one_shot_timer_crit_sec, 0, sizeof(one_shot_timer_crit_sec));
rc = add_alarm_in_us(CDC_USB_TASK_INTERVAL_US, timer_task, NULL, true) >= 0;
}
configured = rc;
return rc;
}
bool cdc_usb_deinit(void)
{
if (get_core_num() != alarm_pool_core_num(alarm_pool_get_default())) {
// included an assertion here rather than just returning false, as this is likely
// a coding bug, rather than anything else.
assert(false);
return false;
}
assert(tud_inited()); // we expect the caller to have initialized when calling sdio_usb_init
if (irq_has_shared_handler(USBCTRL_IRQ)) {
spin_lock_unclaim(spin_lock_get_num(one_shot_timer_crit_sec.spin_lock));
critical_section_deinit(&one_shot_timer_crit_sec);
// we can use a shared handler to notice when there may be work to do
irq_remove_handler(USBCTRL_IRQ, usb_irq);
} else {
// timer is disabled by disabling the irq
}
irq_set_enabled(low_priority_irq_num, false);
user_irq_unclaim(low_priority_irq_num);
configured = false;
return true;
}
bool cdc_usb_configured(void)
{
return configured;
}
bool cdc_usb_connected(void)
{
return tud_cdc_connected();
}
bool cdc_usb_bytes_available(void)
{
return tud_cdc_available();
}
uint32_t cdc_usb_baud_rate(void)
{
return CDC_USD_BAUD_RATE;
}
uint32_t cdc_usb_tx_bytes_free(void)
{
return tud_cdc_write_available();
}