Compare commits

...
2 Commits
Author SHA1 Message Date
NaifBanana 89ef1b3938 Now builds with RadioLib! 2026-10-22 14:41:48 -05:00
NaifBanana 7b459214c8 Recognizes and processes commands 2026-10-21 15:32:35 -05:00
9 changed files with 469 additions and 36 deletions
+2
View File
@@ -3,3 +3,5 @@ build
.nvim
.git
!.git/hooks
FreeRTOS_Kernel_import.cmake
pico_sdk_import.cmake
+18 -26
View File
@@ -6,52 +6,44 @@ set(PROJECT_NAME "GateOpener")
set(PICO_SDK_PATH "C:/Users/naifa/Documents/Projects/Hardware/Microcontroller/SDK/PicoC++/pico-sdk")
set(PICO_TOOLCHAIN_PATH "C:/Users/naifa/Documents/Projects/Hardware/Microcontroller/SDK/arm-gnu-toolchain-15.3.rel2-mingw-w64-x86_64-arm-none-eabi" )
set(FREERTOS_KERNEL_PATH "C:/Users/naifa/Documents/Projects/Hardware/FreeRTOS-Kernel")
set(FREERTOS_CFG_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/Config")
set(RADIOLIB_PATH "C:/Program Files (x86)/radiolib")
set(COMMON_FILES "${CMAKE_CURRENT_LIST_DIR}/Common")
# set(RADIOLIB_PATH "C:/Program Files (x86)/radiolib")
set(RADIOLIB_PATH "C:/Users/naifa/Documents/Projects/Hardware/libs/RadioLib")
set(CMAKE_PREFIX_PATH
${CMAKE_PREFIX_PATH}
${FREERTOS_KERNEL_PATH}
${RADIOLIB_PATH}
)
set(FREERTOS_PICO ${FREERTOS_KERNEL_PATH}/portable/ThirdParty/GCC/RP2040)
set(CMAKE_LIBRARY_PATH
${CMAKE_LIBRARY_PATH}
${FREERTOS_KERNEL_PATH}
${RADIOLIB_PATH}/lib
)
include(./pico_sdk_import.cmake)
project(${PROJECT_NAME} VERSION ${VERSION} LANGUAGES CXX C ASM)
include(./FreeRTOS_Kernel_import.cmake)
pico_sdk_init()
add_subdirectory(${RADIOLIB_PATH} ./radiolib)
message(STATUS "Pico SDK dir is ${PICO_SDK_PATH}")
message(STATUS "FreeRTOS root is ${FREERTOS_KERNEL_PATH}")
message(STATUS "Libraries are ${CMAKE_LIBRARY_PATH}")
include_directories(${FREERTOS_CFG_DIRECTORY}/)
include_directories(
${COMMON_FILES}/
)
include_directories(SYSTEM AFTER
# ${FREERTOS_PICO}
${FREERTOS_PICO}/include
${FREERTOS_KERNEL_PATH}/include
${FREERTOS_KERNEL_PATH}/portable/GCC/ARM_CM0
${RADIOLIB_PATH}/include
${FREERTOS_KERNEL_PATH}/portable/GCC/ARM_CM0
)
add_compile_definitions(configTICK_CORE=0)
# include(${FREERTOS_PICO}/FreeRTOS_Kernel_import.cmake)
# add_library(FreeRTOS STATIC
# ${FREERTOS_KERNEL_PATH}/event_groups.c
# ${FREERTOS_KERNEL_PATH}/list.c
# ${FREERTOS_KERNEL_PATH}/queue.c
# ${FREERTOS_KERNEL_PATH}/stream_buffer.c
# ${FREERTOS_KERNEL_PATH}/tasks.c
# ${FREERTOS_KERNEL_PATH}/timers.c
# ${FREERTOS_KERNEL_PATH}/portable/MemMang/heap_3.c
# # ${FREERTOS_SRC_DIRECTORY}/portable/GCC/ARM_CM0/port.c
# ${FREERTOS_PICO}/port.c
# )
add_subdirectory(./Common)
add_subdirectory(./Talky)
add_subdirectory(./Yeller)
# add_subdirectory(./Listener)
# add_subdirectory(./Yeller)
+19
View File
@@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.5.0)
add_library(RadioLibPico
${COMMON_FILES}/PicoHal.cpp
)
target_link_libraries(RadioLibPico
pico_stdlib
hardware_spi
hardware_pwm
RadioLib
)
target_include_directories(RadioLibPico
PUBLIC ${PICO_SDK_PATH}/src/rp2_common/hardware_spi/include
PUBLIC ${PICO_SDK_PATH}/src/rp2_common/hardware_pwm/include
)
target_compile_definitions(RadioLibPico
PUBLIC RADIOLIB_BUILD_RPI_PICO
)
+132
View File
@@ -0,0 +1,132 @@
/*
* FreeRTOS
* Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* https://www.FreeRTOS.org
* https://aws.amazon.com/freertos
*
*/
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#define PICO_LORA_SPI_SPEED 500*1000
#define PICO_LORA_SPI_CS 5
#define PICO_LORA_SPI_DIO1 26
#define PICO_LORA_RST 20
#define PICO_LORA_MISO 16
#define PICO_LORA_MOSI 15
#define PICO_LORA_SCK 14
#define PICO_LORA_BUSY 4
/* Use Pico SDK ISR handlers */
#define vPortSVCHandler isr_svcall
#define xPortPendSVHandler isr_pendsv
#define xPortSysTickHandler isr_systick
/* Scheduler Related */
#define configUSE_PREEMPTION 1 // Allow tasks to be pre-empted
#define configUSE_TIME_SLICING 1 // Allow FreeRTOS to switch tasks at each tick
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
#define configUSE_TICKLESS_IDLE 0
#define configCPU_CLOCK_HZ 133000000 // 133MHz for RP2040
#define configTICK_RATE_HZ (TickType_t)1000 // FreeRTOS beats per second
#define configMAX_PRIORITIES 24 // Max number of priority values (0-24)
#define configMINIMAL_STACK_SIZE (configSTACK_DEPTH_TYPE)512
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configMAX_TASK_NAME_LEN 16
#define configTASK_NOTIFICATION_ARRAY_ENTRIES 3
/* Synchronization Related */
#define configUSE_MUTEXES 0
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_TASK_NOTIFICATIONS 1
#define configUSE_QUEUE_SETS 0
#define configQUEUE_REGISTRY_SIZE 10
#define configUSE_NEWLIB_REENTRANT 0
#define configENABLE_BACKWARD_COMPATIBILITY 0
#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5
/* System */
#define configSTACK_DEPTH_TYPE uint32_t
#define configMESSAGE_BUFFER_LENGTH_TYPE size_t // Defaults to size_t for backward compatibility,
// but can be changed if lengths will always be less
// than the number of bytes in a size_t.
/* Memory allocation related definitions. */
#define configSUPPORT_STATIC_ALLOCATION 0
#define configSUPPORT_DYNAMIC_ALLOCATION 1 // Get FreeRTOS to allocation task memory
#define configAPPLICATION_ALLOCATED_HEAP 0
#define configTOTAL_HEAP_SIZE (128*1024)
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
#define configUSE_DAEMON_TASK_STARTUP_HOOK 0
/* Run time and task stats gathering related definitions. */
#define configGENERATE_RUN_TIME_STATS 0
#define configUSE_TRACE_FACILITY 0
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
/* Co-routine related definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES 1
/* Software timer related definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1)
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH 1024
/* RP2040 specific */
#define configSUPPORT_PICO_SYNC_INTEROP 1
#define configSUPPORT_PICO_TIME_INTEROP 1
/* Define to trap errors during development. */
#include <assert.h>
#define configASSERT( x )
/* Optional functions - most linkers will remove unused functions anyway. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTaskGetCurrentTaskHandle 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_xTaskGetIdleTaskHandle 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTaskAbortDelay 1
#define INCLUDE_xTaskGetHandle 1
#define INCLUDE_xTaskResumeFromISR 1
#define INCLUDE_xQueueGetMutexHolder 1
#define INCLUDE_xResumeFromISR 1
#define INCLUDE_xEventGroupSetBitFromISR 1
#define INCLUDE_xTimerPendFunctionCall 1
/* A header file that defines trace macro can be included here. */
#endif /* FREERTOS_CONFIG_H */
+49
View File
@@ -0,0 +1,49 @@
#include "PicoHal.h"
#if defined(RADIOLIB_BUILD_RPI_PICO)
// pre-calculated pulse-widths for 1200 and 2200Hz
// we do this to save calculation time (see https://github.com/khoih-prog/RP2040_PWM/issues/6)
#define SLEEP_1200 416.666
#define SLEEP_2200 227.272
static uint32_t toneLoopPin;
static unsigned int toneLoopFrequency;
static unsigned long toneLoopDuration;
// === NOTE ===
// The tone(...) implementation uses the second core on the RPi Pico. This is to diminish as much
// jitter in the output tones as possible.
static void toneLoop() {
gpio_set_dir(toneLoopPin, GPIO_OUT);
uint32_t sleep_dur;
if(toneLoopFrequency == 1200) {
sleep_dur = SLEEP_1200;
} else if(toneLoopFrequency == 2200) {
sleep_dur = SLEEP_2200;
} else {
sleep_dur = 500000 / toneLoopFrequency;
}
// tone bitbang
while(1) {
gpio_put(toneLoopPin, 1);
sleep_us(sleep_dur);
gpio_put(toneLoopPin, 0);
sleep_us(sleep_dur);
tight_loop_contents();
}
}
void PicoHal::tone(uint32_t pin, unsigned int frequency, unsigned long duration) {
// tones on the Pico are generated using bitbanging. This process is offloaded to the Pico's second core
multicore_reset_core1();
toneLoopPin = pin;
toneLoopFrequency = frequency;
toneLoopDuration = duration;
multicore_launch_core1(toneLoop);
}
#endif
+221
View File
@@ -0,0 +1,221 @@
#ifndef PICO_HAL_H
#define PICO_HAL_H
#if defined(RADIOLIB_BUILD_RPI_PICO)
// include RadioLib
#include <RadioLib.h>
// include the necessary Pico libraries
#include <pico/stdlib.h>
#include "hardware/spi.h"
#include "hardware/timer.h"
#include "hardware/pwm.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "pico/multicore.h"
#define PI_PICO_MAX_USER_GPIO (NUM_BANK0_GPIOS)
// because the Pico SDK does not allow to pass user data into interrupt handlers,
// we keep it as a global here. This is hacky and means that multiple PicoHal
// instances share the same interrupts, which is weird and will probably break.
// However, there seems to be no real use case for creating multiple intances of the HAL
static irq_handler_t picoHalUserCallbacks[PI_PICO_MAX_USER_GPIO] = { 0 };
static uint32_t picoHalIrqEventMasks[PI_PICO_MAX_USER_GPIO] = { 0 };
static uint64_t picoHalIrqMask = 0;
static void picoInterruptHandler(void) {
for(unsigned int gpio = 0; gpio < PI_PICO_MAX_USER_GPIO; gpio++) {
// Skip pins that are not managed by us.
if(picoHalIrqEventMasks[gpio] == 0) {
continue;
}
uint32_t activeEvents = gpio_get_irq_event_mask(gpio);
if(activeEvents) {
gpio_acknowledge_irq(gpio, activeEvents);
if((activeEvents & picoHalIrqEventMasks[gpio]) && picoHalUserCallbacks[gpio]) {
picoHalUserCallbacks[gpio]();
}
}
}
}
// create a new Raspberry Pi Pico hardware abstraction
// layer using the Pico SDK
// the HAL must inherit from the base RadioLibHal class
// and implement all of its virtual methods
class PicoHal : public RadioLibHal {
public:
PicoHal(spi_inst_t *spiChannel, uint32_t misoPin, uint32_t mosiPin, uint32_t sckPin, uint32_t spiSpeed = 500 * 1000)
: RadioLibHal(GPIO_IN, GPIO_OUT, 0, 1, GPIO_IRQ_EDGE_RISE, GPIO_IRQ_EDGE_FALL),
_spiChannel(spiChannel),
_spiSpeed(spiSpeed),
_misoPin(misoPin),
_mosiPin(mosiPin),
_sckPin(sckPin){}
void init() override {
stdio_init_all();
spiBegin();
}
void term() override {
spiEnd();
}
// GPIO-related methods (pinMode, digitalWrite etc.) should check
// RADIOLIB_NC as an alias for non-connected pins
void pinMode(uint32_t pin, uint32_t mode) override {
if (pin == RADIOLIB_NC) {
return;
}
gpio_init(pin);
gpio_set_dir(pin, mode);
}
void digitalWrite(uint32_t pin, uint32_t value) override {
if (pin == RADIOLIB_NC) {
return;
}
gpio_put(pin, (bool)value);
}
uint32_t digitalRead(uint32_t pin) override {
if (pin == RADIOLIB_NC) {
return 0;
}
return gpio_get(pin);
}
void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override {
if (interruptNum == RADIOLIB_NC) {
return;
}
// set callbacks
picoHalUserCallbacks[interruptNum] = (irq_handler_t)interruptCb;
picoHalIrqEventMasks[interruptNum] = mode;
// is it a new interrupt for us to grab ?
if (!(picoHalIrqMask & (1ULL << interruptNum))) {
// if we have a handler in place, we must remove it to avoid 'assert' in the PDK
// and we must disable interrupt to make sure we don't miss anything during that
// shuffling
if (picoHalIrqMask) {
irq_set_enabled(IO_IRQ_BANK0, false);
gpio_remove_raw_irq_handler_masked64(picoHalIrqMask, picoInterruptHandler);
}
// (re-)add shared handler with the new mask
picoHalIrqMask |= (1ULL << interruptNum);
gpio_add_raw_irq_handler_masked64(picoHalIrqMask, picoInterruptHandler);
irq_set_enabled(IO_IRQ_BANK0, true);
}
// enable GPIO to generate interrupt
gpio_set_irq_enabled(interruptNum, mode, true);
}
void detachInterrupt(uint32_t interruptNum) override {
if (interruptNum == RADIOLIB_NC) {
return;
}
// disable the IRQ
gpio_set_irq_enabled(interruptNum, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
// clear callbacks
picoHalUserCallbacks[interruptNum] = NULL;
picoHalIrqEventMasks[interruptNum] = 0;
}
void delay(unsigned long ms) override {
sleep_ms(ms);
}
void delayMicroseconds(unsigned long us) override {
sleep_us(us);
}
unsigned long millis() override {
return to_ms_since_boot(get_absolute_time());
}
unsigned long micros() override {
return to_us_since_boot(get_absolute_time());
}
long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override {
if (pin == RADIOLIB_NC) {
return 0;
}
this->pinMode(pin, GPIO_IN);
uint32_t start = this->micros();
uint32_t curtick = this->micros();
while (this->digitalRead(pin) == state) {
if ((this->micros() - curtick) > timeout) {
return 0;
}
}
return (this->micros() - start);
}
void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) override;
void noTone(uint32_t pin) override {
(void)pin; // avoid unused parameter warning
multicore_reset_core1();
}
void spiBegin() override {
spi_init(_spiChannel, _spiSpeed);
spi_set_format(_spiChannel, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST);
gpio_set_function(_sckPin, GPIO_FUNC_SPI);
gpio_set_function(_mosiPin, GPIO_FUNC_SPI);
gpio_set_function(_misoPin, GPIO_FUNC_SPI);
}
void spiBeginTransaction() override {}
void spiTransfer(uint8_t *out, size_t len, uint8_t *in) override {
spi_write_read_blocking(_spiChannel, out, in, len);
}
void yield() override {
tight_loop_contents();
}
void spiEndTransaction() override {}
void spiEnd() override {
spi_deinit(_spiChannel);
}
void pullUpDown(uint32_t pin, bool enable, bool up) override {
if (up) {
gpio_pull_up(pin);
} else {
gpio_pull_down(pin);
}
}
private:
// the HAL can contain any additional private members
spi_inst_t *_spiChannel;
uint32_t _spiSpeed;
uint32_t _misoPin;
uint32_t _mosiPin;
uint32_t _sckPin;
};
#endif
#endif
+8 -3
View File
@@ -9,7 +9,7 @@
char NULLCHAR = 0x0;
QueueHandle_t MESSAGES = NULL;
bool LIGHT = false;
void vReadSerial(void* params) {
// Setup
@@ -64,8 +64,13 @@ void vProcessMessages(void* params) {
char* command = pvPortMalloc(BUFFER_SIZE * sizeof(char));
while(MESSAGES != NULL) {
if (xQueueReceive(MESSAGES, command, 50)) {
printf("Recieved message: ");
printf("%s\n", command);
if (strcmp("blink", command)) {
printf("Recieved message: ");
printf("%s\n", command);
} else {
LIGHT = !LIGHT;
gpio_put(PICO_DEFAULT_LED_PIN, LIGHT);
}
}
}
+1 -1
View File
@@ -7,7 +7,7 @@ add_executable(${APP_NAME}
)
target_link_libraries(${APP_NAME} LINK_PUBLIC
pico_stdlib
RadioLibPico
FreeRTOS-Kernel
FreeRTOS-Kernel-Heap4
)
+19 -6
View File
@@ -1,16 +1,29 @@
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include <pico/stdlib.h>
#include <RadioLib/RadioLib.h>
#include <RadioLib/Module.h>
#include <hardware/spi.h>
#include <FreeRTOSConfig.h>
#include <FreeRTOS.h>
#include <Module.h>
#include <PicoHal.h>
int main() {
PicoHal* pico = new PicoHal(
spi0,
PICO_LORA_MISO,
PICO_LORA_MOSI,
PICO_LORA_SCK
);
// Currently written as numbered 1-40th pin on Pico
SX1262 radio = new Module(5, 26, 20, 4);
SX1262 radio = new Module(
pico,
PICO_LORA_SPI_CS,
PICO_LORA_SPI_DIO1,
PICO_LORA_RST,
PICO_LORA_BUSY
);
ConfigLoRa_t config;
config.frequency = 915;
int state = radio.begin(config);
if (state == RADIOLIB_ERR_NONE) {
if (state == RADIOLIB_ERR_NONE){
printf("LoRa module initialized\n");
while(true) {
state = radio.startTransmit("Howdiddly doo");