diff options
-rw-r--r-- | .gitmodules | 5 | ||||
-rw-r--r-- | main/CMakeLists.txt | 28 | ||||
-rw-r--r-- | main/FreeRTOSConfig.h | 69 | ||||
-rw-r--r-- | main/config.def.h | 11 | ||||
-rw-r--r-- | main/init.c | 58 | ||||
-rw-r--r-- | main/init.h | 38 | ||||
m--------- | main/lib/FreeRTOS-Kernel | 0 | ||||
-rw-r--r-- | main/lwipopts.h | 23 | ||||
-rw-r--r-- | main/main.c | 30 | ||||
-rw-r--r-- | main/main.cpp | 36 | ||||
-rw-r--r-- | main/makefile | 22 | ||||
-rw-r--r-- | main/readme.md | 21 | ||||
-rw-r--r-- | main/sock.c | 52 | ||||
-rw-r--r-- | main/sock.h | 5 |
14 files changed, 345 insertions, 53 deletions
diff --git a/.gitmodules b/.gitmodules index c951407..58b768d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -8,3 +8,8 @@ url = https://github.com/google/googletest branch = v1.14.0 shallow = true +[submodule "main/lib/FreeRTOS-Kernel"] + path = main/lib/FreeRTOS-Kernel + url = https://github.com/FreeRTOS/FreeRTOS-Kernel + branch = V11.1.0 + shallow = true diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index e24d9a5..7b8d567 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -1,22 +1,34 @@ cmake_minimum_required(VERSION 3.29) -include(lib/pico-sdk/pico_sdk_init.cmake) - -project(puzzlebox_main C CXX ASM) - set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) set(PICO_BOARD pico_w) +include(lib/pico-sdk/pico_sdk_init.cmake) +include(lib/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/FreeRTOS_Kernel_import.cmake) + +project(puzzlebox_main C CXX ASM) + pico_sdk_init() add_executable(main - main.cpp -) + main.c + init.c + sock.c + ) pico_enable_stdio_usb(main 1) -# pico_enable_stdio_uart(main 1) +pico_enable_stdio_uart(main 0) pico_add_extra_outputs(main) + +include_directories(lib/pico-sdk/lib/lwip/contrib/ports/freertos/include) + target_include_directories(main PRIVATE ${CMAKE_CURRENT_LIST_DIR}) -target_link_libraries(main pico_cyw43_arch_lwip_threadsafe_background pico_stdlib) +target_link_libraries(main + pico_cyw43_arch_lwip_sys_freertos + pico_stdlib + FreeRTOS-Kernel + FreeRTOS-Kernel-Heap4 + ) + diff --git a/main/FreeRTOSConfig.h b/main/FreeRTOSConfig.h new file mode 100644 index 0000000..c811296 --- /dev/null +++ b/main/FreeRTOSConfig.h @@ -0,0 +1,69 @@ +#pragma once +// values from pico-examples/pico_w/wifi/freertos + +#define configUSE_PREEMPTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configUSE_IDLE_HOOK 0 +#define configUSE_TICK_HOOK 0 +#define configTICK_RATE_HZ ((TickType_t) 1000) +#define configMAX_PRIORITIES 32 +#define configMINIMAL_STACK_SIZE ((configSTACK_DEPTH_TYPE) 512) +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configUSE_QUEUE_SETS 1 +#define configUSE_TIME_SLICING 1 +#define configUSE_NEWLIB_REENTRANT 0 +#define configENABLE_BACKWARD_COMPATIBILITY 1 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 5 +#define configSTACK_DEPTH_TYPE uint32_t +#define configMESSAGE_BUFFER_LENGTH_TYPE size_t +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configTOTAL_HEAP_SIZE (128 * 1024) +#define configAPPLICATION_ALLOCATED_HEAP 0 +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#define configGENERATE_RUN_TIME_STATS 0 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES 1 +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) +#define configTIMER_QUEUE_LENGTH 10 +#define configTIMER_TASK_STACK_DEPTH 1024 + +// #define configNUM_CORES 2 +// #define configTICK_CORE 0 +// #define configRUN_MULTIPLE_PRIORITIES 1 +// #define configUSE_CORE_AFFINITY 1 + +#define configSUPPORT_PICO_SYNC_INTEROP 1 +#define configSUPPORT_PICO_TIME_INTEROP 1 + +#include <assert.h> +#define configASSERT(x) assert(x) + +#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_xTimerPendFunctionCall 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xTaskResumeFromISR 1 +#define INCLUDE_xQueueGetMutexHolder 1 + diff --git a/main/config.def.h b/main/config.def.h index 48de559..7fcaed9 100644 --- a/main/config.def.h +++ b/main/config.def.h @@ -1,8 +1,17 @@ #pragma once +#include <pico/cyw43_arch.h> +// wifi credentials #define CONF_NET_SSID "network name" #define CONF_NET_PASS "network password" +#define CONF_NET_AUTH CYW43_AUTH_WPA2_AES_PSK +// max duration (milliseconds) for establishing wifi connection +#define CONF_NET_CONN_TIMEOUT 10e3 -#include "cyw43_country.h" +#include <cyw43_country.h> #define CONF_NET_COUNTRY CYW43_COUNTRY_NETHERLANDS +#define CONF_SRV_PORT 9191 + +#define LED_PIN CYW43_WL_GPIO_LED_PIN + diff --git a/main/init.c b/main/init.c new file mode 100644 index 0000000..dcd3d54 --- /dev/null +++ b/main/init.c @@ -0,0 +1,58 @@ +#include "config.h" +#include "init.h" + +#include <FreeRTOS.h> +#include <task.h> +#include <event_groups.h> + +#include <pico/stdio.h> +#include <pico/cyw43_arch.h> + +EventGroupHandle_t init_complete; + +static void init_stdio() { + stdio_init_all(); +} + +static void init_cyw34() { + if (cyw43_arch_init_with_country(CONF_NET_COUNTRY)) + panic("cyw43_arch_init_with_country failed\n"); +} + +static void init_wifi() { + // enable 'station' mode (connect to an access point instead of acting like one) + cyw43_arch_enable_sta_mode(); + + if (cyw43_arch_wifi_connect_timeout_ms(CONF_NET_SSID, CONF_NET_PASS, CONF_NET_AUTH, CONF_NET_CONN_TIMEOUT)) + panic("cyw43_arch_wifi_connect failed\n"); + + printf("connected to Wi-Fi\n"); + + // TODO: announce hostname(?) +} + +static void async_init() { + init_cyw34(); + // TODO: initialize i2c + init_wifi(); + + xEventGroupSetBits(init_complete, 1); + + // delete self + vTaskDelete(NULL); +} + +void init() { + init_complete = xEventGroupCreate(); + + // used for debug `printf` and `panic` on errors + init_stdio(); + + // defer other initialization until the task scheduler is running (important) + xTaskCreate((TaskFunction_t) async_init, "init", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 4, NULL); +} + +void await_init() { + xEventGroupWaitBits(init_complete, 1, pdFALSE, pdFALSE, portMAX_DELAY); +} + diff --git a/main/init.h b/main/init.h new file mode 100644 index 0000000..de9023c --- /dev/null +++ b/main/init.h @@ -0,0 +1,38 @@ +#pragma once + +#include <FreeRTOS.h> +#include <event_groups.h> + +/** + * \brief init function complete event group handle + * + * This is required to make sure the main task waits until initialization is + * complete. Due to the combination of FreeRTOS + lwIP, the initialization + * should be done while the task scheduler is running. Specifically the + * cyw43_arch_init functions make the pico hang indefinitely when used while + * the task scheduler is not running. + * + * \note `init_complete` only utilizes LSB, so `uxBitsToWaitFor` should always + * be set to *1* + */ +extern EventGroupHandle_t init_complete; + +/** + * \brief initialize all peripherals on the pico + * + * This function only synchronously initializes the standard input/output (used + * for `printf` and `panic`), and queues all other types of initialization in + * the `init` task using FreeRTOS. + * + * \note Tasks dependent on the wifi being initialized should use the + * `init_complete` event group to wait for initialization to complete! + */ +void init(); + +/** + * \brief block task until all initialization is complete + * + * utility function, see above comments + */ +void await_init(); + diff --git a/main/lib/FreeRTOS-Kernel b/main/lib/FreeRTOS-Kernel new file mode 160000 +Subproject dbf70559b27d39c1fdb68dfb9a32140b6a6777a diff --git a/main/lwipopts.h b/main/lwipopts.h index 75a57ee..b2b6e76 100644 --- a/main/lwipopts.h +++ b/main/lwipopts.h @@ -1,13 +1,7 @@ #pragma once -// allow override in some examples -#ifndef NO_SYS -#define NO_SYS 1 -#endif - -#ifndef LWIP_SOCKET -#define LWIP_SOCKET 0 -#endif +#define NO_SYS 0 +#define LWIP_SOCKET 1 #if PICO_CYW43_ARCH_POLL #define MEM_LIBC_MALLOC 1 @@ -83,3 +77,16 @@ #define SLIP_DEBUG LWIP_DBG_OFF #define DHCP_DEBUG LWIP_DBG_OFF +#define TCPIP_THREAD_STACKSIZE 2048 +#define DEFAULT_THREAD_STACKSIZE 1024 +#define DEFAULT_RAW_RECVMBOX_SIZE 8 +#define TCPIP_MBOX_SIZE 8 + +#define DEFAULT_UDP_RECVMBOX_SIZE TCPIP_MBOX_SIZE +#define DEFAULT_TCP_RECVMBOX_SIZE TCPIP_MBOX_SIZE +#define DEFAULT_ACCEPTMBOX_SIZE TCPIP_MBOX_SIZE + +#define LWIP_TIMEVAL_PRIVATE 0 + +#define LWIP_TCPIP_CORE_LOCKING_INPUT 1 + diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..73b6708 --- /dev/null +++ b/main/main.c @@ -0,0 +1,30 @@ +#include <FreeRTOS.h> +#include <task.h> + +#include <pico/stdlib.h> +#include <pico/time.h> + +#include "config.h" +#include "init.h" +#include "sock.h" + +void blink_task() { + await_init(); // `blink_task` uses GPIO + + while (true) { + cyw43_arch_gpio_put(LED_PIN, 0); + vTaskDelay(250 / portTICK_PERIOD_MS); + cyw43_arch_gpio_put(LED_PIN, 1); + vTaskDelay(250 / portTICK_PERIOD_MS); + } +} + +int main() { + init(); + + xTaskCreate((TaskFunction_t) blink_task, "blink", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL); + xTaskCreate((TaskFunction_t) serve_task, "serve", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 2, NULL); + + vTaskStartScheduler(); +} + diff --git a/main/main.cpp b/main/main.cpp deleted file mode 100644 index 9fd3123..0000000 --- a/main/main.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include <stdio.h> - -#include "config.h" - -#include "pico/stdlib.h" -#include "pico/cyw43_arch.h" - -const unsigned int LED_PIN = CYW43_WL_GPIO_LED_PIN; - -int main() { - stdio_init_all(); - sleep_ms(2000); - - if (cyw43_arch_init_with_country(CONF_NET_COUNTRY)) { - printf("failed to initialize\n"); - return 1; - } - cyw43_arch_gpio_put(LED_PIN, 1); - printf("initialised\n"); - - cyw43_arch_enable_sta_mode(); - - if (cyw43_arch_wifi_connect_timeout_ms(CONF_NET_SSID, CONF_NET_PASS, CYW43_AUTH_WPA2_AES_PSK, 10000)) { - printf("failed to connect\n"); - return 1; - } - printf("connected\n"); - - while (true) { - cyw43_arch_gpio_put(LED_PIN, 0); - sleep_ms(250); - cyw43_arch_gpio_put(LED_PIN, 1); - sleep_ms(250); - } -} - diff --git a/main/makefile b/main/makefile new file mode 100644 index 0000000..1986cd3 --- /dev/null +++ b/main/makefile @@ -0,0 +1,22 @@ +# this file is for lazy people (loek) + +.PHONY: FORCE + +all: FORCE build/main.uf2 + +build/build.ninja: CMakeLists.txt + mkdir -p build + cmake -B build -G Ninja --fresh --log-level WARNING + +build/main.uf2: build/build.ninja FORCE + ninja -C build +# ninja automatically builds in parallel, so is preferred + +flash: build/main.uf2 FORCE + picotool load -fx $< +# -f forces a reboot of the pico before flashing +# -x resets the pico after flashing + +clean: FORCE + $(RM) -r build + diff --git a/main/readme.md b/main/readme.md new file mode 100644 index 0000000..425a00b --- /dev/null +++ b/main/readme.md @@ -0,0 +1,21 @@ +# main controller firmware + +This directory contains the software for the main controller of the Puzzle Box. + +## building + +1. make sure the submodules are initialized +2. copy [`config.def.h`](./config.def.h) to `config.h` and edit the defaults +3. `mkdir build` +4. `cmake -B build` +5. `make -C build` or `ninja -C build` (choose your preference) + +alternatively, a makefile is provided for convenience + +## "flashing" + +1. [build](#building) +2. (re)connect the raspberry pi pico while holding the BOOTSEL button (this is + the only button) +3. `picotool load build/main.uf2` + diff --git a/main/sock.c b/main/sock.c new file mode 100644 index 0000000..7064de7 --- /dev/null +++ b/main/sock.c @@ -0,0 +1,52 @@ +#include <pico/stdio.h> + +#include <lwip/opt.h> +#include <lwip/sys.h> +#include <lwip/api.h> + +#include "init.h" + +#include "config.h" + +void recv_handler(struct netconn* conn, struct netbuf* buf) { + void *data; + uint16_t len; + + do { + netbuf_data(buf, &data, &len); + printf("got %d bytes!\n", len); + } while (netbuf_next(buf) >= 0); + + netbuf_delete(buf); +} + +void accept_handler(struct netconn* conn) { + printf("new connection!\n"); + + struct netbuf* buf; + + while (netconn_recv(conn, &buf) == ERR_OK) + recv_handler(conn, buf); + + netconn_close(conn); + netconn_delete(conn); + + printf("connection closed!\n"); +} + +void serve_task() { + await_init(); + + printf("starting server...\n"); + struct netconn* conn = netconn_new(NETCONN_TCP); + netconn_bind(conn, IP_ADDR_ANY, CONF_SRV_PORT); + netconn_listen(conn); + + printf("listening on %s:%d\n", ip4addr_ntoa(netif_ip4_addr(netif_list)), CONF_SRV_PORT); + while (1) { + struct netconn* incoming; + if (netconn_accept(conn, &incoming) == ERR_OK) + accept_handler(incoming); + } +} + diff --git a/main/sock.h b/main/sock.h new file mode 100644 index 0000000..dd7fc61 --- /dev/null +++ b/main/sock.h @@ -0,0 +1,5 @@ +#pragma once + +/** \brief start listening for TCP socket requests */ +void serve_task(); + |