commit f87dbb9aa7651c741e22d168068387756e38bf4d Author: XupaMisto Date: Tue Apr 21 22:21:41 2026 +0100 logic analyzer v2 firmware initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62b24a3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +build/ +.vscode/ +*.uf2 +*.elf +*.bin +*.map +CMakeFiles/ +CMakeCache.txt \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2d4ad32 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,137 @@ +# Generated Cmake Pico project file + +cmake_minimum_required(VERSION 3.13) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Initialise pico_sdk from installed location +# (note this can come from environment, CMake cache etc) + +# == DO NEVER EDIT THE NEXT LINES for Raspberry Pi Pico VS Code Extension to work == +if(WIN32) + set(USERHOME $ENV{USERPROFILE}) +else() + set(USERHOME $ENV{HOME}) +endif() +set(sdkVersion 2.0.0) +set(toolchainVersion 13_2_Rel1) +set(picotoolVersion 2.0.0) +include(LogicAnalyzer_Build_Settings.cmake) +include(${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) + + +# ==================================================================================== +if(NOT BOARD_TYPE) + message(FATAL_ERROR "Board not set, configure the build on LogicAnalyzer_Build_Settings.cmake") +endif() + +if((BOARD_TYPE STREQUAL "BOARD_PICO_W") OR (BOARD_TYPE STREQUAL "BOARD_PICO_W_WIFI")) + message(STATUS "Setting PICO_BOARD to pico_w") + set(PICO_BOARD pico_w CACHE STRING "Board type") + message(STATUS "Forcing Debug for W build") + set(CMAKE_BUILD_TYPE Debug) + if(TURBO_MODE) + message(FATAL_ERROR "Cannot enable turbo mode for the Pico W") + endif() +elseif(BOARD_TYPE STREQUAL "BOARD_PICO_2") + message(STATUS "Setting PICO_BOARD to pico2") + set(PICO_BOARD pico2 CACHE STRING "Board type") +else() + message(STATUS "Setting PICO_BOARD to pico") + set(PICO_BOARD pico CACHE STRING "Board type") +endif() + +if(TURBO_MODE) + message(WARNING "WARNING!! Turbo mode enabled! Device will be extremely overclocked and overvoltaged!") + add_compile_definitions(TURBO_MODE) +endif() + +if(NOT DEBUG_BUILD) + if((BOARD_TYPE STREQUAL "BOARD_PICO") OR (BOARD_TYPE STREQUAL "BOARD_ZERO") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2")) + message(STATUS "Forcing Release for RAM-only build") + set(CMAKE_BUILD_TYPE Release) + endif() +endif() + + +# Pull in Raspberry Pi Pico SDK (must be before project) +include(pico_sdk_import.cmake) + +project(LogicAnalyzer C CXX ASM) + +# Initialise the Raspberry Pi Pico SDK +pico_sdk_init() + +# Add executable. Default name is the project name, version 0.1 + +FILE(GLOB CSources *.c) +ADD_EXECUTABLE(LogicAnalyzer ${CSources}) + +# Enable ram-only build for the base pico and zero, increases timming precission +if(NOT DEBUG_BUILD) + if((BOARD_TYPE STREQUAL "BOARD_PICO") OR (BOARD_TYPE STREQUAL "BOARD_ZERO") OR (BOARD_TYPE STREQUAL "BOARD_PICO_2")) + message(STATUS "Setting RAM-only compilation") + pico_set_binary_type(LogicAnalyzer copy_to_ram) + endif() +endif() + +# Create C header file with the name .pio.h +pico_generate_pio_header(${PROJECT_NAME} + ${CMAKE_CURRENT_LIST_DIR}/LogicAnalyzer.pio +) + +pico_set_program_name(LogicAnalyzer "LogicAnalyzer") +pico_set_program_version(LogicAnalyzer "6.0") +add_compile_definitions(FIRMWARE_VERSION="V6_0") + +pico_enable_stdio_uart(LogicAnalyzer 0) +pico_enable_stdio_usb(LogicAnalyzer 1) + +if(BOARD_TYPE STREQUAL "BOARD_PICO") + message(STATUS "Configuring for Pico") + add_compile_definitions(BUILD_PICO) +endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_2") + message(STATUS "Configuring for Pico 2") + add_compile_definitions(BUILD_PICO_2) +endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_W") + message(STATUS "Configuring for Pico W without WiFi support") + set(PICO_BOARD pico_w) + add_compile_definitions(BUILD_PICO_W) + set (CYW_LIB pico_cyw43_arch_none) +endif() +if(BOARD_TYPE STREQUAL "BOARD_PICO_W_WIFI") + message(STATUS "Configuring for Pico W with WiFi support") + add_compile_definitions(BUILD_PICO_W_WIFI) + set (CYW_LIB pico_cyw43_arch_lwip_poll) +endif() +if(BOARD_TYPE STREQUAL "BOARD_ZERO") + message(STATUS "Configuring for Zero") + add_compile_definitions(BUILD_ZERO) +endif() + +# Add any user requested libraries +target_link_libraries(LogicAnalyzer + pico_stdlib + hardware_dma + hardware_pio + hardware_clocks + hardware_flash + hardware_adc + hardware_exception + hardware_vreg + pico_multicore + pico_base_headers + pico_multicore + cmsis_core + ${CYW_LIB} + ) + +pico_add_extra_outputs(LogicAnalyzer) + +target_include_directories(LogicAnalyzer PRIVATE ${CMAKE_CURRENT_LIST_DIR} ) + +target_compile_definitions(LogicAnalyzer PUBLIC USBD_MANUFACTURER="Dr. Gusman" USBD_PRODUCT="LogicAnalyzer" USBD_VID=0x1209 USBD_PID=0x3020) \ No newline at end of file diff --git a/Event_Machine.c b/Event_Machine.c new file mode 100644 index 0000000..30d79a6 --- /dev/null +++ b/Event_Machine.c @@ -0,0 +1,44 @@ +#include "Event_Machine.h" + +//Initialize the event machine +void event_machine_init(EVENT_MACHINE* machine, EVENT_HANDLER handler, uint8_t event_size, uint8_t queue_depth) +{ + queue_init(&machine->queue, event_size, queue_depth); + machine->handler = handler; +} + +bool event_has_events(EVENT_MACHINE* machine) +{ + return &machine->queue.wptr != &machine->queue.rptr; +} + +//Adds an event to the machine +void event_push(EVENT_MACHINE* machine, void* event) +{ + queue_add_blocking(&machine->queue, event); +} + +//Processes the pending events +void event_process_queue(EVENT_MACHINE* machine, void* event_buffer, uint8_t max_events) +{ + uint8_t evt_count = 0; + while(!queue_is_empty(&machine->queue) && evt_count++ < max_events) + { + queue_remove_blocking(&machine->queue, event_buffer); + machine->handler(event_buffer); + } +} + +//Clears the stored events in the machine +void event_clear(EVENT_MACHINE* machine) +{ + machine->queue.wptr = 0; + machine->queue.rptr = 0; +} + +//Free an event machine +void event_free(EVENT_MACHINE* machine) +{ + queue_free(&machine->queue); + machine->handler = NULL; +} \ No newline at end of file diff --git a/Event_Machine.h b/Event_Machine.h new file mode 100644 index 0000000..337af62 --- /dev/null +++ b/Event_Machine.h @@ -0,0 +1,27 @@ +#ifndef __EVENTMACHINE__ + #define __EVENTMACHINE__ + + #include "pico/stdlib.h" + #include "pico/util/queue.h" + + //Event handler function declaration + typedef void(*EVENT_HANDLER)(void*); + + //Event machine struct + typedef struct _EVENT_MACHINE + { + //Queue to store events + queue_t queue; + //Function to process the events + EVENT_HANDLER handler; + + } EVENT_MACHINE; + + void event_machine_init(EVENT_MACHINE* machine, EVENT_HANDLER handler, uint8_t args_size, uint8_t queue_depth); + bool event_has_events(EVENT_MACHINE* machine); + void event_push(EVENT_MACHINE* machine, void* event); + void event_process_queue(EVENT_MACHINE* machine, void* event_buffer, uint8_t max_events); + void event_clear(EVENT_MACHINE* machine); + void event_free(EVENT_MACHINE* machine); + +#endif \ No newline at end of file diff --git a/LogicAnalyzer.c b/LogicAnalyzer.c new file mode 100644 index 0000000..a025ac2 --- /dev/null +++ b/LogicAnalyzer.c @@ -0,0 +1,774 @@ +#include "LogicAnalyzer_Board_Settings.h" + +#include +#include +#include "pico/stdlib.h" +#include "hardware/dma.h" +#include "hardware/pio.h" +#include "hardware/clocks.h" +#include "hardware/flash.h" +#include "hardware/vreg.h" +#include "pico/multicore.h" +#include "LogicAnalyzer.pio.h" +#include "LogicAnalyzer_Structs.h" +#include "LogicAnalyzer_Capture.h" +#include "hardware/structs/syscfg.h" +#include "hardware/structs/systick.h" +#include "tusb.h" +#include "pico/unique_id.h" +#include "pico/bootrom.h" + +#ifdef WS2812_LED + #include "LogicAnalyzer_W2812.h" +#endif + +#if defined (CYGW_LED) || defined(USE_CYGW_WIFI) + + #include "pico/cyw43_arch.h" + + #ifdef USE_CYGW_WIFI + + #include "Event_Machine.h" + #include "Shared_Buffers.h" + #include "LogicAnalyzer_WiFi.h" + #include "hardware/regs/usb.h" + #include "hardware/structs/usb.h" + + bool usbDisabled = false; + bool cywReady = false; + bool skipWiFiData = false; + bool dataFromWiFi = false; + EVENT_FROM_WIFI wifiEventBuffer; + WIFI_SETTINGS_REQUEST* wReq; + + #define MULTICORE_LOCKOUT_TIMEOUT (uint64_t)10 * 365 * 24 * 60 * 60 * 1000 * 1000 + + #endif + +#endif + +#if defined (GPIO_LED) + #define INIT_LED() {\ + gpio_init(LED_IO); \ + gpio_set_dir(LED_IO, GPIO_OUT); \ + } + #define LED_ON() gpio_put(LED_IO, 1) + #define LED_OFF() gpio_put(LED_IO, 0) +#elif defined (CYGW_LED) + + #define INIT_LED() { } + + #ifdef USE_CYGW_WIFI + #define LED_ON() {\ + EVENT_FROM_FRONTEND lonEvt;\ + lonEvt.event = LED_ON;\ + event_push(&frontendToWifi, &lonEvt);\ + } + + #define LED_OFF() {\ + EVENT_FROM_FRONTEND loffEvt;\ + loffEvt.event = LED_OFF;\ + event_push(&frontendToWifi, &loffEvt);\ + } + #else + #define LED_ON() cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1) + #define LED_OFF() cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0) + #endif + +#elif defined (WS2812_LED) + #define INIT_LED() init_rgb() + #define LED_ON() send_rgb(0,32,0) + #define LED_OFF() send_rgb(0,0,32) +#endif + +//Buffer used to store received data +uint8_t messageBuffer[128]; +//Position in the buffer +uint8_t bufferPos = 0; +//Capture status +bool capturing = false; + +bool blink = false; +uint32_t blinkCount = 0; + +//Capture request pointer +CAPTURE_REQUEST* req; + +#ifdef USE_CYGW_WIFI + +/// @brief Stores a new WiFi configuration in the flash of the device +/// @param settings Settings to store +void storeSettings(WIFI_SETTINGS* settings) +{ + uint8_t buffer[FLASH_PAGE_SIZE]; + memcpy(buffer, settings, sizeof(WIFI_SETTINGS)); + //multicore_lockout_start_blocking (); + multicore_lockout_start_timeout_us(MULTICORE_LOCKOUT_TIMEOUT); + + uint32_t intStatus = save_and_disable_interrupts(); + + flash_range_erase(FLASH_SETTINGS_OFFSET, FLASH_SECTOR_SIZE); + + for(int buc = 0; buc < 1000; buc++) + { + asm("nop"); + asm("nop"); + asm("nop"); + asm("nop"); + asm("nop"); + } + + flash_range_program(FLASH_SETTINGS_OFFSET, buffer, FLASH_PAGE_SIZE); + + for(int buc = 0; buc < 1000; buc++) + { + asm("nop"); + asm("nop"); + asm("nop"); + asm("nop"); + asm("nop"); + } + + restore_interrupts(intStatus); + + bool unlocked = false; + + do { + unlocked = multicore_lockout_end_timeout_us(MULTICORE_LOCKOUT_TIMEOUT); + } while(!unlocked); + + sleep_ms(500); + +} + +#endif + +/// @brief Sends a response message to the host application in string mode +/// @param response The message to be sent (null terminated) +/// @param toWiFi If true the message is sent to a WiFi endpoint, else to the USB connection through STDIO +void sendResponse(const char* response, bool toWiFi) +{ + #ifdef USE_CYGW_WIFI + if(toWiFi) + { + EVENT_FROM_FRONTEND evt; + evt.event = SEND_DATA; + evt.dataLength = strlen(response); + memset(evt.data, 0, 32); + memcpy(evt.data, response, evt.dataLength); + event_push(&frontendToWifi, &evt); + } + else + #endif + printf(response); +} + +/// @brief Transfer a buffer of data through USB using the TinyUSB CDC functions +/// @param data Buffer of data to transfer +/// @param len Length of the buffer +void cdc_transfer(unsigned char* data, int len) +{ + + int left = len; + int pos = 0; + + while(left > 0) + { + int avail = (int) tud_cdc_write_available(); + + if(avail > left) + avail = left; + + if(avail) + { + int transferred = (int) tud_cdc_write(data + pos, avail); + tud_task(); + tud_cdc_write_flush(); + + pos += transferred; + left -= transferred; + } + else + { + tud_task(); + tud_cdc_write_flush(); + if (!tud_cdc_connected()) + break; + } + } +} + +/// @brief Processes data received from the host application +/// @param data The received data +/// @param length Length of the data +/// @param fromWiFi If true the message comes from a WiFi connection +void processData(uint8_t* data, uint length, bool fromWiFi) +{ + for(uint pos = 0; pos < length; pos++) + { + //Store char in buffer and increment position + messageBuffer[bufferPos++] = data[pos]; + + //If we have stored the first byte and it is not 0x55 restart reception + if(bufferPos == 1 && messageBuffer[0] != 0x55) + bufferPos = 0; + else if(bufferPos == 2 && messageBuffer[1] != 0xAA) //If we have stored the second byte and it is not 0xAA restart reception + bufferPos = 0; + else if(bufferPos >= 256) //Have we overflowed the buffer? then inform to the host and restart reception + { + sendResponse("ERR_MSG_OVERFLOW\n", fromWiFi); + bufferPos = 0; + } + else if(bufferPos > 2) //Try to parse the data + { + if(messageBuffer[bufferPos - 2] == 0xAA && messageBuffer[bufferPos - 1] == 0x55) //Do we have the stop condition? + { + + //Yes, unescape the buffer, + int dest = 0; + + for(int src = 0; src < bufferPos; src++) + { + if(messageBuffer[src] == 0xF0) + { + messageBuffer[dest] = messageBuffer[src + 1] ^ 0xF0; + src++; + } + else + messageBuffer[dest] = messageBuffer[src]; + + dest++; + } + + switch(messageBuffer[2]) //Check the command we received + { + + case 0: //ID request + + if(bufferPos != 5) //Malformed message? + sendResponse("ERR_UNKNOWN_MSG\n", fromWiFi); + else + { + sendResponse("LOGIC_ANALYZER_"BOARD_NAME"_"FIRMWARE_VERSION"\n", fromWiFi); + + char msg[64]; + + sprintf(msg, "FREQ:%d\n", MAX_FREQ); + sendResponse(msg, fromWiFi); + sprintf(msg, "BLASTFREQ:%d\n", MAX_BLAST_FREQ); + sendResponse(msg, fromWiFi); + sprintf(msg, "BUFFER:%d\n", CAPTURE_BUFFER_SIZE); + sendResponse(msg, fromWiFi); + sprintf(msg, "CHANNELS:%d\n", MAX_CHANNELS); + sendResponse(msg, fromWiFi); + } + break; + + case 1: //Capture request + + req = (CAPTURE_REQUEST*)&messageBuffer[3]; //Get the request pointer + + bool started = false; + + #ifdef SUPPORTS_COMPLEX_TRIGGER + + if(req->triggerType == 1) //Start complex trigger capture + started = StartCaptureComplex(req->frequency, req->preSamples, req->postSamples, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->count, req->triggerValue, req->captureMode); + else if(req->triggerType == 2) //start fast trigger capture + started = StartCaptureFast(req->frequency, req->preSamples, req->postSamples, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->count, req->triggerValue, req->captureMode); + else if(req->triggerType == 3) + started = StartCaptureBlast(req->frequency, req->postSamples, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->inverted, req->captureMode); + else //Start simple trigger capture + started = StartCaptureSimple(req->frequency, req->preSamples, req->postSamples, req->loopCount, req->measure, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->inverted, req->captureMode); + + #else + + if(req->triggerType == 1 || req->triggerType == 2) + { + sendResponse("CAPTURE_ERROR\n", fromWiFi); + break; + } + else if(req->triggerType == 3) + started = StartCaptureBlast(req->frequency, req->postSamples, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->inverted, req->captureMode); + else //Start simple trigger capture + started = StartCaptureSimple(req->frequency, req->preSamples, req->postSamples, req->loopCount, req->measure, (uint8_t*)&req->channels, req->channelCount, req->trigger, req->inverted, req->captureMode); + + #endif + + if(started) //If started successfully inform to the host + { + sendResponse("CAPTURE_STARTED\n", fromWiFi); + capturing = true; + } + else + sendResponse("CAPTURE_ERROR\n", fromWiFi); //Else notify the error + + break; + + #ifdef USE_CYGW_WIFI + + case 2: //Update WiFi settings + + wReq = (WIFI_SETTINGS_REQUEST*)&messageBuffer[3]; + WIFI_SETTINGS settings; + memcpy(settings.apName, wReq->apName, 33); + memcpy(settings.passwd, wReq->passwd, 64); + memcpy(settings.ipAddress, wReq->ipAddress, 16); + settings.port = wReq->port; + + for(int buc = 0; buc < 33; buc++) + settings.checksum += settings.apName[buc]; + + for(int buc = 0; buc < 64; buc++) + settings.checksum += settings.passwd[buc]; + + for(int buc = 0; buc < 16; buc++) + settings.checksum += settings.ipAddress[buc]; + + settings.checksum += settings.port; + + settings.checksum += 0x0f0f; + + storeSettings(&settings); + + wifiSettings = settings; + + EVENT_FROM_FRONTEND evt; + evt.event = CONFIG_RECEIVED; + event_push(&frontendToWifi, &evt); + + sendResponse("SETTINGS_SAVED\n", fromWiFi); + + break; + + case 3: //Read power status + + if(!fromWiFi) + sendResponse("ERR_UNSUPPORTED\n", fromWiFi); + else + { + EVENT_FROM_FRONTEND powerEvent; + powerEvent.event = GET_POWER_STATUS; + event_push(&frontendToWifi, &powerEvent); + } + + break; + + #else + + case 2: + case 3: + + sendResponse("ERR_UNSUPPORTED\n", fromWiFi); + break; + + #endif + + case 4: + + sendResponse("RESTARTING_BOOTLOADER\n", fromWiFi); + sleep_ms(1000); + reset_usb_boot(0, 0); + break; + + case 5: + + blink = true; + blinkCount = 0; + sendResponse("BLINKON\n", fromWiFi); + break; + + case 6: + + blink = false; + blinkCount = 0; + sendResponse("BLINKOFF\n", fromWiFi); + LED_ON(); + break; + + default: + + sendResponse("ERR_UNKNOWN_MSG\n", fromWiFi); //Unknown message + break; + + } + + bufferPos = 0; //Reset buffer position + } + } + + } + + //PROTOCOL EXPLAINED: + // + //The protocol is very basic, it receives binary frames and sends strings terminated by a carriage return. + // + //Each binary frame has a start and an end condition, being these two secuences of two bytes: + // start condition: 0x55 0xAA + // stop condition: 0xAA 0x55 + // + //This kind of framing can cause problems if the packets contain the frame condition bytes, there needs to be implemented + //a scape character to avoid this.The char 0xF0 is used as escape character. Escaping is done by XOR'ing the scape character + //with the scaped char. For example, if we need to send 0xAA we would send { 0xF0, 0x5A }, which is 0xAA XOR 0xF0 = 0x5A. + //In case of sending the scape char we would send { 0xF0, 0x00 }. + // + //Inside each frame we have a command byte and additional data. Based on the command a binary struct will be deserialized + //from the buffer. Right now the protocol has only two commands: ID request and capture request. ID request does not + //have any data, but the capture request has a CAPTURE_REQUEST struct as data. +} + +/// @brief Receive and process USB data from the host application +/// @param skipProcessing If true the received data is not processed (used for cleanup) +/// @return True if anything is received, false if not +bool processUSBInput(bool skipProcessing) +{ + //Try to get char + uint data = getchar_timeout_us(0); + + //Timeout? Then leave + if(data == PICO_ERROR_TIMEOUT) + return false; + + uint8_t filteredData = (uint8_t)data; + + if(!skipProcessing) + processData(&filteredData, 1, false); + + return true; + +} + +#ifdef USE_CYGW_WIFI + +/// @brief Purges any pending data in the USB input +void purgeUSBData() +{ + while(getchar_timeout_us(0) != PICO_ERROR_TIMEOUT); +} + +/// @brief Send a string response with the power status +/// @param status Status received from the WiFi core +void sendPowerStatus(POWER_STATUS* status) +{ + char buffer[32]; + memset(buffer, 0, 32); + int len = sprintf(buffer, "%.2f", status->vsysVoltage); + buffer[len++] = '_'; + buffer[len++] = status->vbusConnected ? '1' : '0'; + buffer[len] = '\n'; + sendResponse(buffer, true); +} + +/// @brief Callback for the WiFi event queue +/// @param event Received event +void wifiEvent(void* event) +{ + EVENT_FROM_WIFI* wEvent = (EVENT_FROM_WIFI*)event; + + switch(wEvent->event) + { + case CYW_READY: + cywReady = true; + break; + case CONNECTED: + usbDisabled = true; + //disableUSB(); + break; + case DISCONNECTED: + usbDisabled = false; + purgeUSBData(); + //enableUSB(); + break; + case DATA_RECEIVED: + if(skipWiFiData) + dataFromWiFi = true; + else + processData(wEvent->data, wEvent->dataLength, true); + break; + case POWER_STATUS_DATA: + { + POWER_STATUS status; + memcpy(&status, wEvent->data, sizeof(POWER_STATUS)); + sendPowerStatus(&status); + } + break; + } +} + +/// @brief Receives and processes input from the host application (when connected through WiFi) +/// @param skipProcessing /// @param skipProcessing If true the received data is not processed (used for cleanup) +/// @return True if anything is received, false if not +bool processWiFiInput(bool skipProcessing) +{ + bool res = event_has_events(&wifiToFrontend); + + if(skipProcessing) + { + skipWiFiData = true; + dataFromWiFi = false; + } + + event_process_queue(&wifiToFrontend, &wifiEventBuffer, 8); + + skipWiFiData = false; + + return dataFromWiFi; +} + +#endif + +/// @brief Process input data from the host application if it is available +void processInput() +{ + #ifdef USE_CYGW_WIFI + if(!usbDisabled) + processUSBInput(false); + + processWiFiInput(false); + #else + processUSBInput(false); + #endif +} + +/// @brief Processes input data from the host application to check if there is any cancel capture request +/// @return True if there was input data +bool processCancel() +{ + #ifdef USE_CYGW_WIFI + if(!usbDisabled) + if(processUSBInput(true)) + return true; + + return processWiFiInput(true); + #else + return processUSBInput(true); + #endif +} + +/// @brief Main app loop +/// @return Exit code +int main() +{ + #if defined (TURBO_MODE) + + vreg_disable_voltage_limit(); + vreg_set_voltage(VREG_VOLTAGE_1_30); + sleep_ms(1); + + //Overclock Powerrrr! + set_sys_clock_khz(400000, true); + + #else + + set_sys_clock_khz(200000, true); + + #endif + + //Enable systick using CPU clock + systick_hw->csr = 0x05; + + pico_unique_board_id_t id; + pico_get_unique_board_id(&id); + + uint16_t delay = 0; + + for(int buc = 0; buc < PICO_UNIQUE_BOARD_ID_SIZE_BYTES; buc++) + delay += id.id[buc]; + + delay = (delay & 0x3ff) + ((delay & 0xFC00) >> 6); + + sleep_ms(delay); + + //Initialize USB stdio + stdio_init_all(); + + #if defined (BUILD_PICO_W) + cyw43_arch_init(); + #elif defined (BUILD_PICO_W_WIFI) + event_machine_init(&wifiToFrontend, wifiEvent, sizeof(EVENT_FROM_WIFI), 8); + multicore_launch_core1(runWiFiCore); + while(!cywReady) + event_process_queue(&wifiToFrontend, &wifiEventBuffer, 1); + #endif + + //A bit of delay, if the program tries to send data before Windows has identified the device it may crash + sleep_ms(1000); + + //Clear message buffer + memset(messageBuffer, 0, 128); + + //Configure led + INIT_LED(); + LED_ON(); + + while(1) + { + //Are we capturing? + if(capturing) + { + //Is the PIO units still working? + if(!IsCapturing()) + { + //Retrieve the capture buffer and get info about it. + uint32_t length, first; + CHANNEL_MODE mode; + uint8_t* buffer = GetBuffer(&length, &first, &mode); + + uint8_t stampsLength; + volatile uint32_t* timestamps = GetTimestamps(&stampsLength); + + //Send the data to the host + uint8_t* lengthPointer = (uint8_t*)&length; + + //Send capture length + sleep_ms(100); + + #ifdef USE_CYGW_WIFI + + if(usbDisabled) + { + EVENT_FROM_FRONTEND evt; + evt.event = SEND_DATA; + evt.dataLength = 4; + memcpy(evt.data, lengthPointer, 4); + event_push(&frontendToWifi, &evt); + } + else + cdc_transfer(lengthPointer, 4); + + #else + cdc_transfer(lengthPointer, 4); + #endif + + sleep_ms(100); + + //Tanslate sample numbers to byte indexes, makes easier to send data + switch(mode) + { + case MODE_16_CHANNEL: + length *= 2; + first *= 2; + break; + case MODE_24_CHANNEL: + length *= 4; + first *= 4; + break; + } + + #ifdef USE_CYGW_WIFI + + //Send the samples + if(usbDisabled) + { + EVENT_FROM_FRONTEND evt; + evt.event = SEND_DATA; + + int pos = 0; + int filledData; + while(pos < length) + { + filledData = 0; + while(pos < length && filledData < 32) + { + evt.data[filledData] = buffer[first++]; + + if(first >= 131072) + first = 0; + + pos++; + filledData++; + } + + evt.dataLength = filledData; + event_push(&frontendToWifi, &evt); + } + + evt.data[0] = stampsLength; + evt.dataLength = 1; + event_push(&frontendToWifi, &evt); + + for(int buc = 0; buc < stampsLength; buc++) + { + *((uint32_t*)evt.data) = timestamps[buc]; + evt.dataLength = 4; + event_push(&frontendToWifi, &evt); + } + } + else + { + if(first + length > CAPTURE_BUFFER_SIZE) + { + cdc_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first); + cdc_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE); + } + else + cdc_transfer(buffer + first, length); + + cdc_transfer(&stampsLength, 1); + + if(stampsLength > 1) + cdc_transfer((unsigned char*)timestamps, stampsLength * 4); + } + #else + + if(first + length > CAPTURE_BUFFER_SIZE) + { + cdc_transfer(buffer + first, CAPTURE_BUFFER_SIZE - first); + cdc_transfer(buffer, (first + length) - CAPTURE_BUFFER_SIZE); + } + else + cdc_transfer(buffer + first, length); + + cdc_transfer(&stampsLength, 1); + + if(stampsLength > 1) + cdc_transfer((unsigned char*)timestamps, stampsLength * 4); + + #endif + //Done! + capturing = false; + } + else + { + LED_OFF(); + sleep_ms(1000); + + //Check for cancel request + if(processCancel()) + { + //Stop capture + StopCapture(); + capturing = false; + LED_ON(); + } + else + { + LED_ON(); + #ifdef SUPPORTS_COMPLEX_TRIGGER + check_fast_interrupt(); + #endif + sleep_ms(1000); + } + } + } + else + { + if(blink) + { + if(blinkCount++ == 200000) + { + LED_OFF(); + } + else if(blinkCount == 400000) + { + LED_ON(); + blinkCount = 0; + } + } + + processInput(); //Read incomming data + } + } + + return 0; +} \ No newline at end of file diff --git a/LogicAnalyzer.pio b/LogicAnalyzer.pio new file mode 100644 index 0000000..b594578 --- /dev/null +++ b/LogicAnalyzer.pio @@ -0,0 +1,241 @@ +;-------------------------------------------------------------------------------------------- +.program BLAST_CAPTURE + +LOOP: + + jmp pin LOOP ;wait for trigger + +.wrap_target + + in pins 32 ;capture + +.wrap + +;-------------------------------------------------------------------------------------------- +.program POSITIVE_CAPTURE + + pull + out y 32 ;read loop count + pull + mov x, osr ;read capture length (use MOV instead of PULL so we can MOV it again on each loop) + +.wrap_target + + in pins 32 ;read sample + jmp pin POST_CAPTURE ;exit wrap if pin is set + +.wrap + +POST_CAPTURE: + + in pins 32 ;read sample + jmp x-- POST_CAPTURE ;loop if more samples needed + + jmp y-- LOOP ;jump to loop control + + irq 0 ;notify to the main program that we have finished capturing + +LOCK: + + jmp LOCK ;block the program + +LOOP: + + mov x, osr ;read loop count + +INNER_LOOP: + + jmp pin POST_CAPTURE ;wait for trigger + jmp INNER_LOOP + +;-------------------------------------------------------------------------------------------- +.program NEGATIVE_CAPTURE + + pull + out y 32 ;read loop count + pull + mov x, osr ;read capture length (use MOV instead of PULL so we can MOV it again on each loop) + +PRE_CAPTURE: + + in pins 32 ;read sample + jmp pin PRE_CAPTURE ;loop if pin is set + +POST_CAPTURE: + +.wrap_target + + in pins 32 ;read sample + jmp x-- POST_CAPTURE ;loop if more samples needed + + jmp y-- LOOP ;jump to loop control + + irq 0 ;notify to the main program that we have finished capturing + +LOCK: + + jmp LOCK ;block the program + +LOOP: + + mov x, osr ;read loop count + +INNER_LOOP: + jmp pin INNER_LOOP ;wait for trigger + +.wrap + +;-------------------------------------------------------------------------------------------- +.program POSITIVE_CAPTURE_MEASUREBURSTS + + pull + out y 32 ;read loop count + pull + mov x, osr ;read capture length (use MOV instead of PULL so we can MOV it again on each loop) + irq wait 1 ;trigger NMI to sync first timestamp + +.wrap_target + + in pins 32 ;read sample + jmp pin POST_CAPTURE ;exit wrap if pin is set + +.wrap + +POST_CAPTURE: + + in pins 32 ;read sample + jmp x-- POST_CAPTURE ;loop if more samples needed + + jmp y-- LOOP ;jump to loop control + + irq 1 ;trigger NMI on the CPU to capture burst timestamp + irq 0 ;notify to the main program that we have finished capturing + +LOCK: + + jmp LOCK ;block the program + +LOOP: + + irq 1 ;trigger NMI on the CPU to capture burst timestamp + mov x, osr ;read loop count + +INNER_LOOP: + + jmp pin POST_CAPTURE ;wait for trigger + jmp INNER_LOOP + +;-------------------------------------------------------------------------------------------- +.program NEGATIVE_CAPTURE_MEASUREBURSTS + + pull + out y 32 ;read loop count + pull + mov x, osr ;read capture length (use MOV instead of PULL so we can MOV it again on each loop) + irq wait 1 ;trigger NMI to sync first timestamp + +PRE_CAPTURE: + + in pins 32 ;read sample + jmp pin PRE_CAPTURE ;loop if pin is set + +POST_CAPTURE: + +.wrap_target + + in pins 32 ;read sample + jmp x-- POST_CAPTURE ;loop if more samples needed + + jmp y-- LOOP ;jump to loop control + + irq 1 ;trigger NMI on the CPU to capture burst timestamp + irq 0 ;notify to the main program that we have finished capturing + +LOCK: + + jmp LOCK ;block the program + +LOOP: + + irq 1 ;trigger NMI on the CPU to capture burst timestamp + mov x, osr ;read loop count + +INNER_LOOP: + jmp pin INNER_LOOP ;wait for trigger + +.wrap + +;-------------------------------------------------------------------------------------------- +.program COMPLEX_CAPTURE + + pull + out x 32 ;read capture length + + wait irq 7 ;wait for trigger program to be ready + +.wrap_target + + in pins 29 ;read sample + jmp pin POST_CAPTURE ;exit wrap if pin is set + +.wrap + +POST_CAPTURE: + + in pins 29 ;read sample + jmp x-- POST_CAPTURE ;loop if more samples needed + + irq 0 ;notify to the main program that we have finished capturing + +LOCK: + + jmp LOCK ;block the program + +;-------------------------------------------------------------------------------------------- +.program FAST_CAPTURE + + pull + out x 32 ;read capture length + +.wrap_target + + in pins 29 ;read sample + jmp pin POST_CAPTURE ;exit wrap if pin is set + +.wrap + +POST_CAPTURE: + + in pins 29 ;read sample + jmp x-- POST_CAPTURE ;loop if more samples needed + + irq 0 ;notify to the main program that we have finished capturing + +LOCK: + + jmp LOCK ;block the program + +;-------------------------------------------------------------------------------------------- +;--------Kept only for reference, the program is stored in volatile memory as it must-------- +;---------be modified for concrete trigger parameters.--------------------------------------- +;-------------------------------------------------------------------------------------------- +;.program COMPLEX_TRIGGER + +; pull +; out x 32 ;read trigger value + +; set pins 0 ;set trigger pin to low + +; irq 7 ;Release capture program + +;TRIGGER_LOOP: + +; mov osr, pins ;read pin status to output shift register +; out y, 4 ;output 4 bits to Y (writes 32 bits) +; jmp x!=y TRIGGER_LOOP ;loop if trigger not met + +; set pins 1 ;set trigger pin to high (trigger met) + +;LOCK: + +; jmp LOCK ;block program \ No newline at end of file diff --git a/LogicAnalyzer_Board_Settings.h b/LogicAnalyzer_Board_Settings.h new file mode 100644 index 0000000..f93611b --- /dev/null +++ b/LogicAnalyzer_Board_Settings.h @@ -0,0 +1,122 @@ +#ifndef __LOGICANALYZER_BOARD_SETTINGS__ + + #define __LOGICANALYZER_BOARD_SETTINGS__ + + #include "pico/stdlib.h" + //#include "LogicAnalyzer_Build_Settings.h" + + //Board definitions + + //This defines the name sent to the software + //#define BOARD_NAME "PICO" + //If defined the device supports complex, fast and external triggers + //#define SUPPORTS_COMPLEX_TRIGGER + //Stablishes the channel base GPIO + //#define INPUT_PIN_BASE 2 + //Complex/fast/ext trigger output pin + //#define COMPLEX_TRIGGER_OUT_PIN 0 + //Complex/fast/ext trigger input pin + //#define COMPLEX_TRIGGER_IN_PIN 1 + //If defined, the onboard led is a led connected to a GPIO + //#define GPIO_LED + //If defined, the onboard led is a led connected to a CYGW module (for the Pico W) + //#define CYGW_LED + //If defined, the onboard led is a RGB led connected to a GPIO + //#define WS2812_LED + //Defines the used GPIO used for the GPIO and WS2812 led types + //#define LED_IO 25 + //If defined enables the Pico W WiFi module + //#define USE_CYGW_WIFI + + #if defined (BUILD_PICO) + + #define BOARD_NAME "PICO" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define GPIO_LED + #define LED_IO 25 + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 24 + + #elif defined (BUILD_PICO_2) + + #define BOARD_NAME "PICO_2" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define GPIO_LED + #define LED_IO 25 + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 3 * 1024) + #define MAX_CHANNELS 24 + + #elif defined (BUILD_PICO_W) + + #define BOARD_NAME "W" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define CYGW_LED + + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 24 + + #elif defined (BUILD_PICO_W_WIFI) + + #define BOARD_NAME "WIFI" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 2 + #define COMPLEX_TRIGGER_OUT_PIN 0 + #define COMPLEX_TRIGGER_IN_PIN 1 + #define CYGW_LED + #define USE_CYGW_WIFI + + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 24 + + #elif defined (BUILD_ZERO) + + #define BOARD_NAME "ZERO" + #define SUPPORTS_COMPLEX_TRIGGER + #define INPUT_PIN_BASE 0 + #define COMPLEX_TRIGGER_OUT_PIN 17 + #define COMPLEX_TRIGGER_IN_PIN 18 + #define WS2812_LED + #define LED_IO 16 + + #ifdef TURBO_MODE + #define MAX_FREQ 200000000 + #define MAX_BLAST_FREQ 400000000 + #else + #define MAX_FREQ 100000000 + #define MAX_BLAST_FREQ 200000000 + #endif + #define CAPTURE_BUFFER_SIZE (128 * 1024) + #define MAX_CHANNELS 24 + + #endif + +#endif \ No newline at end of file diff --git a/LogicAnalyzer_Build_Settings.cmake b/LogicAnalyzer_Build_Settings.cmake new file mode 100644 index 0000000..99fc369 --- /dev/null +++ b/LogicAnalyzer_Build_Settings.cmake @@ -0,0 +1,10 @@ +# This file controls the build settings, set your board version +# Current versions: "BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2" +set(BOARD_TYPE "BOARD_PICO_2") + +# Set to 1 to enable 200Mhz mode (warning! extreme overclock and overvoltage!) +# Not available for the Pico W +set(TURBO_MODE 1) + +# Uncomment to be able to debug the build +# set(DEBUG_BUILD 1) diff --git a/LogicAnalyzer_Capture.c b/LogicAnalyzer_Capture.c new file mode 100644 index 0000000..b8e2ba2 --- /dev/null +++ b/LogicAnalyzer_Capture.c @@ -0,0 +1,1375 @@ +#include "LogicAnalyzer_Board_Settings.h" +#include "LogicAnalyzer_Capture.h" +#include "hardware/gpio.h" +#include "hardware/dma.h" +#include "hardware/irq.h" +#include "hardware/clocks.h" +#include "string.h" +#include "hardware/sync.h" +#include "hardware/exception.h" +#include "hardware/structs/syscfg.h" +#include "hardware/structs/systick.h" +#include "hardware/structs/bus_ctrl.h" +#include "LogicAnalyzer.pio.h" + +//Static variables for the PIO programs +static PIO capturePIO; +static PIO triggerPIO; + +static uint sm_Capture; +static uint captureOffset; + +static uint sm_Trigger; +static uint triggerOffset; + +//Static variables for DMA channels +static uint32_t dmaPingPong0; +static uint32_t dmaPingPong1; +static uint32_t transferCount; + +//Static information of the last capture +static uint8_t lastCapturePins[24]; //List of captured pins +static uint8_t lastCapturePinCount; //Count of captured pins +static uint32_t lastTriggerCapture; //Moment where the trigger happened inside the circular pre buffer +static uint32_t lastPreSize; //Pre-trigger buffer size +static uint32_t lastPostSize; //Post-trigger buffer size +static uint32_t lastLoopCount; //Number of loops +static bool lastTriggerInverted; //Inverted? +static uint8_t lastTriggerPin; +static uint32_t lastStartPosition; +static bool lastCaptureComplexFast; +static uint8_t lastCaptureType; +static uint8_t lastTriggerPinBase; +static uint32_t lastTriggerPinCount; +static uint32_t lastTail; +static CHANNEL_MODE lastCaptureMode = MODE_8_CHANNEL; + +//Static information of the current capture +static bool captureFinished; +static bool captureProcessed; + +//NEW// +static volatile uint32_t loopTimestamp[256]; +static volatile uint8_t timestampIndex; +static volatile uint8_t systickLoops; +static exception_handler_t oldNMIHandler; +static exception_handler_t oldSysTickHandler; + +//Pin mapping, used to map the channels to the PIO program +//COMPLEX_TRIGGER_IN_PIN is added at the end of the array to support the chained mode +#if defined (BUILD_PICO) + const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; +#elif defined (BUILD_PICO_2) + const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; +#elif defined (BUILD_PICO_W) + const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; +#elif defined (BUILD_PICO_W_WIFI) + const uint8_t pinMap[] = {2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,26,27,28,COMPLEX_TRIGGER_IN_PIN}; +#elif defined (BUILD_ZERO) + const uint8_t pinMap[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,26,27,28,29,22,23,24,25,COMPLEX_TRIGGER_IN_PIN}; +#endif + +//Main capture buffer, aligned at a dword boundary. +static uint8_t captureBuffer[CAPTURE_BUFFER_SIZE] __attribute__((aligned(4))); + +#define CAPTURE_TYPE_SIMPLE 0 +#define CAPTURE_TYPE_COMPLEX 1 +#define CAPTURE_TYPE_FAST 2 +#define CAPTURE_TYPE_BLAST 3 + +//----------------------------------------------------------------------------- +//--------------Complex trigger PIO program------------------------------------ +//----------------------------------------------------------------------------- +#ifdef SUPPORTS_COMPLEX_TRIGGER + +#define COMPLEX_TRIGGER_wrap_target 0 +#define COMPLEX_TRIGGER_wrap 8 + +uint16_t COMPLEX_TRIGGER_program_instructions[] = { + // .wrap_target + 0x80a0, // 0: pull block + 0x6020, // 1: out x, 32 + 0xe000, // 2: set pins, 0 + 0xc007, // 3: irq nowait 7 + 0xa0e0, // 4: mov osr, pins + 0x6044, // 5: out y, 4 + 0x00a4, // 6: jmp x != y, 4 + 0xe001, // 7: set pins, 1 + 0x0008, // 8: jmp 8 + // .wrap +}; + +struct pio_program COMPLEX_TRIGGER_program = { + .instructions = COMPLEX_TRIGGER_program_instructions, + .length = 9, + .origin = -1, +}; + +static inline pio_sm_config COMPLEX_TRIGGER_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + COMPLEX_TRIGGER_wrap_target, offset + COMPLEX_TRIGGER_wrap); + return c; +} +#endif +//----------------------------------------------------------------------------- +//--------------Complex trigger PIO program END-------------------------------- +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +//--------------Fast trigger PIO program--------------------------------------- +//----------------------------------------------------------------------------- +#ifdef SUPPORTS_COMPLEX_TRIGGER + +#define FAST_TRIGGER_wrap_target 0 +#define FAST_TRIGGER_wrap 31 + +uint16_t FAST_TRIGGER_program_instructions[32]; + +struct pio_program FAST_TRIGGER_program = { + .instructions = FAST_TRIGGER_program_instructions, + .length = 32, + .origin = 0, +}; + +static inline pio_sm_config FAST_TRIGGER_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + FAST_TRIGGER_wrap_target, offset + FAST_TRIGGER_wrap); + sm_config_set_sideset(&c, 1, false, false); + return c; +} + +//Creates the fast trigger PIO program +uint8_t create_fast_trigger_program(uint8_t pattern, uint8_t length) +{ + //This creates a 32 instruction jump table. Each instruction is a MOV PC, PINS except for the addresses that + //match the specified pattern. + + uint8_t i; + uint8_t mask = (1 << length) - 1; //Mask for testing address vs pattern + uint8_t first = 255; + + for(i = 0; i < 32; i++) + { + if((i & mask) == pattern) + FAST_TRIGGER_program_instructions[i] = 0x1000 | i; //JMP i SIDE 1 + else + { + FAST_TRIGGER_program_instructions[i] = 0xA0A0; //MOV PC, PINS SIDE 0 + first = i; + } + } + + return first; +} +#endif +//----------------------------------------------------------------------------- +//--------------Fast trigger PIO program END----------------------------------- +//----------------------------------------------------------------------------- + +//Find the last captured sample index +uint32_t find_capture_tail() +{ + int transferCount; + + switch(lastCaptureMode) + { + case MODE_8_CHANNEL: + transferCount = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + transferCount = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + transferCount = CAPTURE_BUFFER_SIZE / 4; + break; + } + + //Add a delay in case the transfer is still in progress (just a safety measure, should not happen) + //This is a massive delay in comparison to the needs of the DMA channel, but hey, 5ms is not going to be noticed anywhere :D + busy_wait_ms(5); + + int32_t transferPos = 0xFFFFFFFF; + + //First we need to determine which DMA channel is busy (in the middle of a transfer) + if(dma_channel_is_busy(dmaPingPong0)) + { + transferPos = dma_channel_hw_addr(dmaPingPong0)->transfer_count; + } + else if(dma_channel_is_busy(dmaPingPong1)) + { + transferPos = dma_channel_hw_addr(dmaPingPong1)->transfer_count; + } + + //No channel busy?? WTF??? + if(transferPos == 0xFFFFFFFF) + return 0xFFFFFFFF; + + //Ok, now we need to know at which transfer the DMA is. The value equals to (MAX_TRANSFERS - TRANSFERS_LEFT) - 1 (DMA channel decrements transfer_count when it starts :/). + uint32_t transfer = (transferCount - transferPos) - 1; //TODO: CHECK + + //Our capture absolute last position + return transfer; +} + +//Disable the trigger GPIOs to avoid triggering again a chained device +void disable_gpios() +{ + #ifdef SUPPORTS_COMPLEX_TRIGGER + gpio_deinit(COMPLEX_TRIGGER_OUT_PIN); + gpio_deinit(COMPLEX_TRIGGER_IN_PIN); + #endif + + for(uint8_t i = 0; i < lastCapturePinCount; i++) + gpio_deinit(lastCapturePins[i]); + + + gpio_set_inover(lastTriggerPin, 0); +} + + +//DMA channel handler, not in memory to speed it up +void __not_in_flash_func(dma_handler)() +{ + + //Did channel0 triggered the irq? + if(dma_channel_get_irq0_status(dmaPingPong0)) + { + //Clear the irq + dma_channel_acknowledge_irq0(dmaPingPong0); + //Rewrite the write address without triggering the channel + dma_channel_set_write_addr(dmaPingPong0, captureBuffer, false); + } + else + { + //Clear the irq + dma_channel_acknowledge_irq0(dmaPingPong1); + //Rewrite the write address without triggering the channel + dma_channel_set_write_addr(dmaPingPong1, captureBuffer, false); + } + +} + +void abort_DMAs() +{ + hw_clear_bits(&dma_hw->ch[dmaPingPong0].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_clear_bits(&dma_hw->ch[dmaPingPong1].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + + //Abort any pending transfer + dma_channel_abort(dmaPingPong0); + dma_channel_abort(dmaPingPong1); + + //Disable IRQ0 + dma_channel_set_irq0_enabled(dmaPingPong0, false); //Enable IRQ 0 + dma_channel_set_irq0_enabled(dmaPingPong1, false); //Enable IRQ 0 + irq_set_enabled(DMA_IRQ_0, false); + + irq_remove_handler (DMA_IRQ_0, dma_handler); + + //Unclaim the channels + dma_channel_unclaim(dmaPingPong0); + dma_channel_unclaim(dmaPingPong1); +} + +#ifdef SUPPORTS_COMPLEX_TRIGGER + +//Triggered when a fast capture ends +void fast_capture_completed() +{ + //Disable the GPIO's + disable_gpios(); + + lastTail = find_capture_tail(); + + //Abort DMA channels + abort_DMAs(); + + //Clear PIO interrupt 0 and unhook handler + pio_interrupt_clear(capturePIO, 0); + + //Stop PIO capture program and clear + pio_sm_set_enabled(capturePIO, sm_Capture, false); + pio_sm_unclaim(capturePIO, sm_Capture); + + pio_remove_program(capturePIO, &FAST_CAPTURE_program, captureOffset); + + //Stop PIO trigger program and clear + pio_sm_set_enabled(triggerPIO, sm_Trigger, false); + pio_sm_set_pins(triggerPIO, sm_Trigger, 0); + pio_sm_unclaim(triggerPIO, sm_Trigger); + + pio_remove_program(triggerPIO, &FAST_TRIGGER_program, triggerOffset); + + //Mark the capture as finished + captureFinished = true; +} + +//Check if the capture has finished, this is done because the W messes the PIO interrupts +void check_fast_interrupt() +{ + if(lastCaptureType == CAPTURE_TYPE_FAST && capturePIO->irq & 1) + fast_capture_completed(); +} + +//Triggered when a complex capture ends +void complex_capture_completed() +{ + //Disable the GPIO's + disable_gpios(); + + lastTail = find_capture_tail(); + + //Abort DMA channels + abort_DMAs(); + + //Clear PIO interrupt 0 and unhook handler + pio_interrupt_clear(capturePIO, 0); + irq_set_enabled(PIO0_IRQ_0, false); + irq_remove_handler(PIO0_IRQ_0, complex_capture_completed); + + //Stop PIO capture program and clear + pio_sm_set_enabled(capturePIO, sm_Capture, false); + pio_sm_unclaim(capturePIO, sm_Capture); + + pio_remove_program(capturePIO, &COMPLEX_CAPTURE_program, captureOffset); + + //Stop PIO trigger program and clear + pio_sm_set_enabled(capturePIO, sm_Trigger, false); + pio_sm_set_pins(capturePIO, sm_Trigger, 0); + pio_sm_unclaim(capturePIO, sm_Trigger); + + pio_remove_program(capturePIO, &COMPLEX_TRIGGER_program, triggerOffset); + + //Mark the capture as finished + captureFinished = true; +} + +#endif + + +//Triggered when a blast capture ends +void blast_capture_completed() +{ + //Clear the irq + dma_channel_acknowledge_irq0(dmaPingPong0); + + //Not needed, left for sanity + hw_clear_bits(&dma_hw->ch[dmaPingPong0].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + + //Disable IRQ0 + dma_channel_set_irq0_enabled(dmaPingPong0, false); //Enable IRQ 0 + irq_set_enabled(DMA_IRQ_0, false); + irq_remove_handler (DMA_IRQ_0, blast_capture_completed); + + //Unclaim the channels + dma_channel_unclaim(dmaPingPong0); + + //Restore DMA priority to normal + bus_ctrl_hw->priority = 0; + + lastTail = lastPostSize; //TODO: CHECK + + //Stop PIO program and clear + pio_sm_set_enabled(capturePIO, sm_Capture, false); + pio_sm_unclaim(capturePIO, sm_Capture); + + pio_remove_program(capturePIO, &BLAST_CAPTURE_program, captureOffset); + + //Mark the capture as finished + captureFinished = true; +} + + +//Triggered when a simple capture ends +void simple_capture_completed() +{ + //Disable the GPIO's + disable_gpios(); + + lastTail = find_capture_tail(); + + //Abort DMA channels + abort_DMAs(); + + //Clear PIO interrupt 0 and unhook handler + pio_interrupt_clear(capturePIO, 0); + irq_set_enabled(PIO0_IRQ_0, false); + irq_remove_handler(PIO0_IRQ_0, simple_capture_completed); + + //Clear PIO interrupt 1, disable NMI and reset systick + pio_interrupt_clear (capturePIO, 1); + pio_set_irq1_source_enabled(capturePIO, pis_interrupt1, false); + + if(timestampIndex) + { +#if defined(BUILD_PICO_2) + EPPB->NMI_MASK0 = 0; +#else + syscfg_hw->proc0_nmi_mask = 0; +#endif + exception_restore_handler(NMI_EXCEPTION, oldNMIHandler); + systick_hw->csr = 0; + exception_restore_handler(SYSTICK_EXCEPTION, oldSysTickHandler); + } + + //Stop PIO program and clear + pio_sm_set_enabled(capturePIO, sm_Capture, false); + pio_sm_unclaim(capturePIO, sm_Capture); + + if(lastTriggerInverted) + pio_remove_program(capturePIO, &POSITIVE_CAPTURE_program, captureOffset); + else + pio_remove_program(capturePIO, &NEGATIVE_CAPTURE_program, captureOffset); + + //Mark the capture as finished + captureFinished = true; + +} + +//TODO: HERE +void configureBlastDMA(CHANNEL_MODE channelMode, uint32_t length) +{ + enum dma_channel_transfer_size transferSize; + dma_channel_config dmaConfig; + + switch(channelMode) + { + case MODE_8_CHANNEL: + transferSize = DMA_SIZE_8; + break; + case MODE_16_CHANNEL: + transferSize = DMA_SIZE_16; + break; + case MODE_24_CHANNEL: + transferSize = DMA_SIZE_32; + break; + } + + dmaPingPong0 = dma_claim_unused_channel(true); + + //Configure first capture DMA + dmaConfig = dma_channel_get_default_config(dmaPingPong0); + channel_config_set_read_increment(&dmaConfig, false); //Do not increment read address + channel_config_set_write_increment(&dmaConfig, true); //Increment write address + channel_config_set_transfer_data_size(&dmaConfig, transferSize); //Transfer size is based on capture mode + channel_config_set_dreq(&dmaConfig, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO + channel_config_set_enable(&dmaConfig, true); //Enable the channel + + dma_channel_set_irq0_enabled(dmaPingPong0, true); //Enable IRQ 0 + + //Set interrupt handler and enable it + irq_set_exclusive_handler(DMA_IRQ_0, blast_capture_completed); + irq_set_enabled(DMA_IRQ_0, true); + irq_set_priority(DMA_IRQ_0, 0); + + //Full priority to the DMA + bus_ctrl_hw->priority = BUSCTRL_BUS_PRIORITY_DMA_W_BITS | BUSCTRL_BUS_PRIORITY_DMA_R_BITS; + + dma_channel_configure(dmaPingPong0, &dmaConfig, captureBuffer, &capturePIO->rxf[sm_Capture], length, true); //Configure the channel and trigger it + +} + +//Configure the two DMA channels +void configureCaptureDMAs(CHANNEL_MODE channelMode) +{ + + enum dma_channel_transfer_size transferSize; + dma_channel_config dmaPingPong0Config; + dma_channel_config dmaPingPong1Config; + + switch(channelMode) + { + case MODE_8_CHANNEL: + transferSize = DMA_SIZE_8; + transferCount = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + transferSize = DMA_SIZE_16; + transferCount = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + transferSize = DMA_SIZE_32; + transferCount = CAPTURE_BUFFER_SIZE / 4; + break; + } + + dmaPingPong0 = dma_claim_unused_channel(true); + dmaPingPong1 = dma_claim_unused_channel(true); + + //Configure first capture DMA + dmaPingPong0Config = dma_channel_get_default_config(dmaPingPong0); + channel_config_set_read_increment(&dmaPingPong0Config, false); //Do not increment read address + channel_config_set_write_increment(&dmaPingPong0Config, true); //Increment write address + channel_config_set_transfer_data_size(&dmaPingPong0Config, transferSize); //Transfer size is based on capture mode + channel_config_set_chain_to(&dmaPingPong0Config, dmaPingPong1); //Chain to the second dma channel + channel_config_set_dreq(&dmaPingPong0Config, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO + channel_config_set_enable(&dmaPingPong0Config, true); //Enable the channel + + dma_channel_set_irq0_enabled(dmaPingPong0, true); //Enable IRQ 0 + + //Configure second capture DMA + dmaPingPong1Config = dma_channel_get_default_config(dmaPingPong1); + channel_config_set_read_increment(&dmaPingPong1Config, false); //Do not increment read address + channel_config_set_write_increment(&dmaPingPong1Config, true); //Increment write address + channel_config_set_transfer_data_size(&dmaPingPong1Config, transferSize); //Transfer size is based on capture mode + channel_config_set_chain_to(&dmaPingPong1Config, dmaPingPong0); //Chain to the first dma channel + channel_config_set_dreq(&dmaPingPong1Config, pio_get_dreq(capturePIO, sm_Capture, false)); //Set DREQ as RX FIFO + channel_config_set_enable(&dmaPingPong1Config, true); //Enable the channel + + dma_channel_set_irq0_enabled(dmaPingPong1, true); //Enable IRQ 0 + + //Set interrupt handler and enable it + irq_set_exclusive_handler(DMA_IRQ_0, dma_handler); + irq_set_enabled(DMA_IRQ_0, true); + irq_set_priority(DMA_IRQ_0, 0); + + dma_channel_configure(dmaPingPong1, &dmaPingPong1Config, captureBuffer, &capturePIO->rxf[sm_Capture], transferCount, false); //Configure the channel but don't trigger it + dma_channel_configure(dmaPingPong0, &dmaPingPong0Config, captureBuffer, &capturePIO->rxf[sm_Capture], transferCount, true); //Configure the and trigger it + +} + +void StopCapture() +{ + if(!captureFinished) + { + //Ensure the DMA channels are stopped, else they will overrun the buffer when the interrupts are disabled + hw_clear_bits(&dma_hw->ch[dmaPingPong0].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_clear_bits(&dma_hw->ch[dmaPingPong1].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + + uint32_t int_status = save_and_disable_interrupts(); + + #ifdef SUPPORTS_COMPLEX_TRIGGER + + if(lastCaptureType == CAPTURE_TYPE_SIMPLE) + simple_capture_completed(); + else if(lastCaptureType == CAPTURE_TYPE_COMPLEX) + complex_capture_completed(); + else if(lastCaptureType == CAPTURE_TYPE_FAST) + fast_capture_completed(); + else if(lastCaptureType == CAPTURE_TYPE_BLAST) + blast_capture_completed(); + + #else + + if(lastCaptureType == CAPTURE_TYPE_SIMPLE) + simple_capture_completed(); + else if(lastCaptureType == CAPTURE_TYPE_BLAST) + blast_capture_completed(); + + #endif + + restore_interrupts(int_status); + } +} + +#ifdef SUPPORTS_COMPLEX_TRIGGER + +bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue, CHANNEL_MODE captureMode) +{ + + //ABOUT THE FAST TRIGGER + // + //The fast trigger is an evolution of the complex trigger. + //Like the complex trigger this is a sepparate program that checks for a pattern to trigger the capture program second stage. + // + //The main difference is the maximum length of the pattern to match and the sampling speed. This fast trigger + //can only use a pattern up to 5 bits, but it captures at maximum speed of 100Msps (it could even sample up to 200Mhz but to match the + //maximum speed of the sampling it is limited to 100Msps). + //To achieve this the program occupies all 32 instructions of a PIO module, this is basically a jump table, each + //instruction moves the pin values to the program counter except for the ones that match the pattern, which activate the + //trigger pin using the side pins and create an infinite loop jumping to itself (basically a JMP currentpc SIDE 1). + // + //This solves the speed and latency problem, the speed reaches 100Msps and the latency is reduced to a maximum of 2 cycles, but + //still can glitch on low speeds and also occupies a complete PIO module (but we have one unused, so its not a problem) + + + int maxSamples; + + switch(captureMode) + { + case MODE_8_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 4; + break; + } + + //Too many samples requested? + if(preLength + postLength > maxSamples) + return false; + + //Frequency too high? + if(freq > MAX_FREQ) + return false; + + //Incorrect pin count? + if(capturePinCount < 0 || capturePinCount > MAX_CHANNELS) + return false; + + //Bad trigger? + if(triggerPinBase > 15 || triggerPinCount > 5 || triggerPinCount < 1 || triggerPinCount + triggerPinBase > 16) + return false; + + //Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled) + memset(captureBuffer, 0, sizeof(captureBuffer)); + + //Store info about the capture + lastPreSize = preLength; + lastPostSize = postLength; + lastLoopCount = 0; + lastCapturePinCount = capturePinCount; + lastCaptureComplexFast = true; + lastCaptureMode = captureMode; + + //Map channels to pins + for(uint8_t i = 0; i < capturePinCount; i++) + lastCapturePins[i] = pinMap[capturePins[i]]; + + //Store trigger info + triggerPinBase = pinMap[triggerPinBase]; + lastTriggerPinBase = triggerPinBase; + + //Calculate clock divider based on frequency, it generates a clock 2x faster than the capture freequency + float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq * 2); + + //Store the PIO units and clear program memory + capturePIO = pio1; //Cannot clear it in PIO1 because the W uses PIO1 to transfer data + triggerPIO = pio0; + + pio_clear_instruction_memory(triggerPIO); + + //Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins) + pio_gpio_init(triggerPIO, COMPLEX_TRIGGER_OUT_PIN); + pio_gpio_init(capturePIO, COMPLEX_TRIGGER_IN_PIN); + + for(uint8_t i = 0; i < 24; i++) + pio_gpio_init(capturePIO, pinMap[i]); + + //Configure capture SM + sm_Capture = pio_claim_unused_sm(capturePIO, true); + pio_sm_clear_fifos(capturePIO, sm_Capture); + pio_sm_restart(capturePIO, sm_Capture); + captureOffset = pio_add_program(capturePIO, &FAST_CAPTURE_program); + + //Modified for the W + for(int i = 0; i < 24; i++) + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + + //Configure state machines + pio_sm_config smConfig = FAST_CAPTURE_program_get_default_config(captureOffset); + + //Inputs start at pin INPUT_PIN_BASE + sm_config_set_in_pins(&smConfig, INPUT_PIN_BASE); + + //Set clock to 2x required frequency + sm_config_set_clkdiv(&smConfig, clockDiv); + + //Autopush per 29 bits + sm_config_set_in_shift(&smConfig, false, true, 29); + + //Configure fast trigger pin (COMPLEX_TRIGGER_IN_PIN) as JMP pin. + sm_config_set_jmp_pin(&smConfig, COMPLEX_TRIGGER_IN_PIN); + + //Configure interrupt 0 + pio_interrupt_clear (capturePIO, 0); + + //Reset timestamp index + timestampIndex = 0; + + //Initialize state machine + pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig); + + //Configure trigger SM + sm_Trigger = pio_claim_unused_sm(triggerPIO, true); + pio_sm_clear_fifos(triggerPIO, sm_Trigger); + pio_sm_restart(triggerPIO, sm_Trigger); + + //Create trigger program + uint8_t triggerFirstInstruction = create_fast_trigger_program(triggerValue, triggerPinCount); + + //Configure trigger state machine + triggerOffset = pio_add_program(triggerPIO, &FAST_TRIGGER_program); + pio_sm_set_consecutive_pindirs(triggerPIO, sm_Trigger, COMPLEX_TRIGGER_OUT_PIN, 1, true); //Pin COMPLEX_TRIGGER_OUT_PIN as output (connects to Pin COMPLEX_TRIGGER_IN_PIN, to trigger capture) + pio_sm_set_consecutive_pindirs(triggerPIO, sm_Trigger, triggerPinBase, triggerPinCount, false); //Trigger pins start at triggerPinBase + + smConfig = FAST_TRIGGER_program_get_default_config(triggerOffset); + + sm_config_set_in_pins(&smConfig, triggerPinBase); //Trigger input starts at pin base + sm_config_set_set_pins(&smConfig, COMPLEX_TRIGGER_OUT_PIN, 1); //Trigger output is a set pin + sm_config_set_sideset_pins(&smConfig, COMPLEX_TRIGGER_OUT_PIN); //Trigger output is a side pin + sm_config_set_clkdiv(&smConfig, 1); //Trigger always runs at max speed + + //Configure DMA's + configureCaptureDMAs(captureMode); + + //Enable capture state machine + pio_sm_set_enabled(capturePIO, sm_Capture, true); + + //Write capture length to post program + pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1); + + //Initialize trigger state machine + pio_sm_init(triggerPIO, sm_Trigger, triggerOffset, &smConfig); + + //Enable trigger state machine + pio_sm_set_enabled(triggerPIO, sm_Trigger, true); + + //Finally clear capture status and process flags + captureFinished = false; + captureProcessed = false; + lastCaptureType = CAPTURE_TYPE_FAST; + + //We're done + return true; +} + +bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue, CHANNEL_MODE captureMode) +{ + + //ABOUT THE COMPLEX TRIGGER + // + //The complex trigger is a hack to achieve the maximum speed in the capture program. + //To get to 100Msps with a 200Mhz clock each capture must be excuted in two instructions. For this the basic + //capture programs (the positive and negative ones) use the JMP PIN instruction, this redirects the program flow based in the + //state of a pin, so with an IN instruction and a JMP instruction we can create a loop that captures data until the trigger pin + //is in the correct edge and then jumps to another subroutine that captures until the post-trigger samples are met. + // + //Unfortunately there is no way to jump to a subroutine based in the status of more than one pin, you can jump based in the + //comparison of the scratch registers, but this requires more than one instruction to prepare the data. + //So, what I have implemented here is an asynchronouss trigger, a second state machine running at máximum speed checks if the trigger + //condition is met and then notifies to the first state machine. But... there is no way to notify of something between state machines + //except for interrupts, and interrupts blocks the code execution (you WAIT for the interrupt) so this is not viable, so we use a hack, we + //interconnect two pins (GPIO0 and GPIO1), one is an output from the trigger state machine and the other is the JMP PIN for the capture + //state machine. When the trigger condition is met the output pin is set to 1 so the JMP PIN pin receives this signal and we can keep + //our capture program to use two instructions. + //This carries some limitations, the trigger can only work up to 66Msps but the capture can go up to 100Msps as they are independent. + //Also, as the trigger always runs at maximum speed there may happen a glitch in the trigger signal for lower capture speeds, the + //condition may be met but for less time than a capture cycle, so the capture machine will not sample this trigger condition. + //Finally the trigger also has some cycles of delay, 3 instructions plus 2 cycles of propagation to the ISR, so a maximum of + //25ns of delay can happen. + + int maxSamples; + + switch(captureMode) + { + case MODE_8_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 4; + break; + } + + //Too many samples requested? + if(preLength + postLength > maxSamples) + return false; + + //Frequency too high? + if(freq > MAX_FREQ) + return false; + + //Incorrect pin count? + if(capturePinCount < 0 || capturePinCount > MAX_CHANNELS) + return false; + + //Bad trigger? + if(triggerPinBase > 15 || triggerPinCount > 16 || triggerPinCount < 1 || triggerPinCount + triggerPinBase > 16) + return false; + + //Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled) + memset(captureBuffer, 0, sizeof(captureBuffer)); + + //Store info about the capture + lastPreSize = preLength; + lastPostSize = postLength; + lastLoopCount = 0; + lastCapturePinCount = capturePinCount; + lastCaptureComplexFast = true; + lastCaptureMode = captureMode; + + //Map channels to pins + for(uint8_t i = 0; i < capturePinCount; i++) + lastCapturePins[i] = pinMap[capturePins[i]]; + + //Store trigger info + triggerPinBase = pinMap[triggerPinBase]; + lastTriggerPinBase = triggerPinBase; + + //Calculate clock divider based on frequency, it generates a clock 2x faster than the capture freequency + float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq * 2); + + //Store the PIO unit and clear program memory + capturePIO = pio0; + pio_clear_instruction_memory(capturePIO); + + //Configure 24 + 2 IO's to be used by the PIO (24 channels + 2 trigger pins) + pio_gpio_init(capturePIO, COMPLEX_TRIGGER_OUT_PIN); + pio_gpio_init(capturePIO, COMPLEX_TRIGGER_IN_PIN); + + for(uint8_t i = 0; i < 24; i++) + pio_gpio_init(capturePIO, pinMap[i]); + + //Configure capture SM + sm_Capture = pio_claim_unused_sm(capturePIO, true); + pio_sm_clear_fifos(capturePIO, sm_Capture); + pio_sm_restart(capturePIO, sm_Capture); + captureOffset = pio_add_program(capturePIO, &COMPLEX_CAPTURE_program); + + for(int i = 0; i < 24; i++) + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + + //Configure state machines + pio_sm_config smConfig = COMPLEX_CAPTURE_program_get_default_config(captureOffset); + + //Inputs start at pin INPUT_PIN_BASE + sm_config_set_in_pins(&smConfig, INPUT_PIN_BASE); + + //Set clock to 2x required frequency + sm_config_set_clkdiv(&smConfig, clockDiv); + + //Autopush per 29 bits + sm_config_set_in_shift(&smConfig, false, true, 29); + + //Configure complex trigger pin (pin COMPLEX_TRIGGER_IN_PIN) as JMP pin. + sm_config_set_jmp_pin(&smConfig, COMPLEX_TRIGGER_IN_PIN); + + //Configure interrupt 0 + pio_interrupt_clear (capturePIO, 0); + pio_set_irq0_source_enabled(capturePIO, pis_interrupt0, true); + irq_set_exclusive_handler(PIO0_IRQ_0, complex_capture_completed); + irq_set_enabled(PIO0_IRQ_0, true); + + //Reset timestamp index + timestampIndex = 0; + + //Initialize state machine + pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig); + + //Configure trigger SM + sm_Trigger = pio_claim_unused_sm(capturePIO, true); + pio_sm_clear_fifos(capturePIO, sm_Trigger); + pio_sm_restart(capturePIO, sm_Trigger); + + //Modify trigger program to use the correct pins + COMPLEX_TRIGGER_program_instructions[5] = 0x6040 | triggerPinCount; + + //Configure trigger state machine + triggerOffset = pio_add_program(capturePIO, &COMPLEX_TRIGGER_program); + pio_sm_set_consecutive_pindirs(capturePIO, sm_Trigger, COMPLEX_TRIGGER_OUT_PIN, 1, true); //Pin COMPLEX_TRIGGER_OUT_PIN as output (connects to Pin COMPLEX_TRIGGER_IN_PIN, to trigger capture) + pio_sm_set_consecutive_pindirs(capturePIO, sm_Trigger, triggerPinBase, triggerPinCount, false); //Trigger pins start at triggerPinBase + + smConfig = COMPLEX_TRIGGER_program_get_default_config(triggerOffset); + sm_config_set_in_pins(&smConfig, triggerPinBase); //Trigger input starts at pin base + sm_config_set_set_pins(&smConfig, COMPLEX_TRIGGER_OUT_PIN, 1); //Trigger output is a set pin + sm_config_set_clkdiv(&smConfig, 1); //Trigger always runs at max speed + sm_config_set_in_shift(&smConfig, false, false, 0); //Trigger shifts left to right + + //Initialize trigger state machine + pio_sm_init(capturePIO, sm_Trigger, triggerOffset, &smConfig); //Init trigger + + //Configure DMA's + configureCaptureDMAs(captureMode); + + //Enable capture state machine + pio_sm_set_enabled(capturePIO, sm_Capture, true); + + //Write capture length to post program + pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1); + + //Enable trigger state machine + pio_sm_set_enabled(capturePIO, sm_Trigger, true); + + //Write trigger value to trigger program + pio_sm_put_blocking(capturePIO, sm_Trigger, triggerValue); + + //Finally clear capture status and process flags + captureFinished = false; + captureProcessed = false; + lastCaptureType = CAPTURE_TYPE_COMPLEX; + + //We're done + return true; +} + +#endif + +void __not_in_flash_func(sysTickRoll)() +{ + systickLoops++; +} + +void __not_in_flash_func(loopEndHandler)() +{ + //Save timestamp + loopTimestamp[timestampIndex++] = systick_hw->cvr | systickLoops << 24; //timestamp; + //Clear PIO interrupt + capturePIO->irq = (1u << 1); +} + +bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode) +{ + int maxSamples; + + switch(captureMode) + { + case MODE_8_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 4; + break; + } + + //Too many samples requested? + if(length > maxSamples) + return false; + + //Frequency too high? + if(freq > MAX_BLAST_FREQ) + return false; + + //Incorrect pin count? + if(capturePinCount < 1 || capturePinCount > MAX_CHANNELS) + return false; + + //Incorrect trigger pin? + if(triggerPin < 0 || triggerPin > MAX_CHANNELS) + return false; + + //Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled) + memset(captureBuffer, 0, sizeof(captureBuffer)); + + //Store info about the capture + lastPreSize = 0; + lastPostSize = length; + lastLoopCount = 0; + lastCapturePinCount = capturePinCount; + lastTriggerInverted = invertTrigger; + lastCaptureComplexFast = false; + lastCaptureMode = captureMode; + + //Map channels to pins + for(uint8_t i = 0; i < capturePinCount; i++) + lastCapturePins[i] = pinMap[capturePins[i]]; + + //Store trigger info + triggerPin = pinMap[triggerPin]; + lastTriggerPin = triggerPin; + + //Calculate clock divider based on frequency, in blast mode it is a 1:1 clock + float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq); + + //Store the PIO unit and clear program memory + capturePIO = pio0; + pio_clear_instruction_memory(capturePIO); + + //Configure capture SM + sm_Capture = pio_claim_unused_sm(capturePIO, true); + pio_sm_clear_fifos(capturePIO, sm_Capture); + pio_sm_restart(capturePIO, sm_Capture); + + //Load program + captureOffset = pio_add_program(capturePIO, &BLAST_CAPTURE_program); + + //Configure capture pins + for(int i = 0; i < 24; i++) + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + + for(uint8_t i = 0; i < 24; i++) + pio_gpio_init(capturePIO, pinMap[i]); + + //Configure trigger pin + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); + pio_gpio_init(capturePIO, triggerPin); + + if(!invertTrigger) + gpio_set_inover(triggerPin, 1); + + //Configure state machines + pio_sm_config smConfig = BLAST_CAPTURE_program_get_default_config(captureOffset); + + //Input starts at pin INPUT_PIN_BASE + sm_config_set_in_pins(&smConfig, INPUT_PIN_BASE); + + //Set clock to required frequency + sm_config_set_clkdiv(&smConfig, clockDiv); + + //Autopush per dword + sm_config_set_in_shift(&smConfig, true, true, 0); + + //Configure trigger pin as JMP pin. + sm_config_set_jmp_pin(&smConfig, triggerPin); + + //Disable state machine + pio_sm_set_enabled(capturePIO, sm_Capture, false); + + //Initialize state machine + pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig); + + //Configure DMA's + configureBlastDMA(captureMode, length); + + //Enable state machine + pio_sm_set_enabled(capturePIO, sm_Capture, true); + + + //Finally clear capture status, process flags and capture type + captureFinished = false; + captureProcessed = false; + lastCaptureType = CAPTURE_TYPE_BLAST; + + //We're done + return true; +} + +bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint8_t loopCount, uint8_t measureBursts, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode) +{ + int maxSamples; + + switch(captureMode) + { + case MODE_8_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + maxSamples = CAPTURE_BUFFER_SIZE / 4; + break; + } + + //Too many samples requested? + if(preLength + (postLength * (loopCount + 1)) > maxSamples) + return false; + + //Frequency too high? + if(freq > MAX_FREQ) + return false; + + //Incorrect pin count? + if(capturePinCount < 1 || capturePinCount > MAX_CHANNELS) + return false; + + //Incorrect trigger pin? + if(triggerPin < 0 || triggerPin > MAX_CHANNELS) + return false; + + //Clear capture buffer (to avoid sending bad data if the trigger happens before the presamples are filled) + memset(captureBuffer, 0, sizeof(captureBuffer)); + + //Store info about the capture + lastPreSize = preLength; + lastPostSize = postLength; + lastLoopCount = loopCount; + lastCapturePinCount = capturePinCount; + lastTriggerInverted = invertTrigger; + lastCaptureComplexFast = false; + lastCaptureMode = captureMode; + + //Map channels to pins + for(uint8_t i = 0; i < capturePinCount; i++) + lastCapturePins[i] = pinMap[capturePins[i]]; + + //Store trigger info + triggerPin = pinMap[triggerPin]; + lastTriggerPin = triggerPin; + + //Calculate clock divider based on frequency, it generates a clock 2x faster than the capture freequency + float clockDiv = (float)clock_get_hz(clk_sys) / (float)(freq * 2); + + //Store the PIO unit and clear program memory + capturePIO = pio0; + pio_clear_instruction_memory(capturePIO); + + //Configure capture SM + sm_Capture = pio_claim_unused_sm(capturePIO, true); + pio_sm_clear_fifos(capturePIO, sm_Capture); + pio_sm_restart(capturePIO, sm_Capture); + + //Load correct program, depending on the trigger edge + if(invertTrigger) + { + if(measureBursts) + captureOffset = pio_add_program(capturePIO, &NEGATIVE_CAPTURE_MEASUREBURSTS_program); + else + captureOffset = pio_add_program(capturePIO, &NEGATIVE_CAPTURE_program); + } + else + { + if(measureBursts) + captureOffset = pio_add_program(capturePIO, &POSITIVE_CAPTURE_MEASUREBURSTS_program); + else + captureOffset = pio_add_program(capturePIO, &POSITIVE_CAPTURE_program); + + } + + //Configure capture pins + for(int i = 0; i < 24; i++) + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, pinMap[i], 1, false); + + for(uint8_t i = 0; i < 24; i++) + pio_gpio_init(capturePIO, pinMap[i]); + + //Configure trigger pin + pio_sm_set_consecutive_pindirs(capturePIO, sm_Capture, triggerPin, 1, false); + pio_gpio_init(capturePIO, triggerPin); + + //Configure state machines + pio_sm_config smConfig = measureBursts? + (invertTrigger? + NEGATIVE_CAPTURE_MEASUREBURSTS_program_get_default_config(captureOffset): + POSITIVE_CAPTURE_MEASUREBURSTS_program_get_default_config(captureOffset)) : + (invertTrigger? + NEGATIVE_CAPTURE_program_get_default_config(captureOffset): + POSITIVE_CAPTURE_program_get_default_config(captureOffset)); + + //Input starts at pin INPUT_PIN_BASE + sm_config_set_in_pins(&smConfig, INPUT_PIN_BASE); + + //Set clock to 2x required frequency + sm_config_set_clkdiv(&smConfig, clockDiv); + + //Autopush per dword + sm_config_set_in_shift(&smConfig, true, true, 0); + + //Configure trigger pin as JMP pin. + sm_config_set_jmp_pin(&smConfig, triggerPin); + + //Configure interupt 0 + pio_interrupt_clear (capturePIO, 0); + pio_set_irq0_source_enabled(capturePIO, pis_interrupt0, true); + irq_set_exclusive_handler(PIO0_IRQ_0, simple_capture_completed); + irq_set_enabled(PIO0_IRQ_0, true); + + //Set-up burst measure + if(loopCount > 0 && measureBursts) + { + //Configure NMI to get capture timestamp + pio_interrupt_clear (capturePIO, 1); + pio_set_irq1_source_enabled(capturePIO, pis_interrupt1, true); + //irq_set_exclusive_handler(PIO0_IRQ_1, loopEndHandler); + irq_set_priority(PIO0_IRQ_1, 0); + //irq_set_enabled(PIO0_IRQ_1, true); + + //syscfg_hw->proc0_nmi_mask = 1 << PIO0_IRQ_1; + +#if defined(BUILD_PICO_2) + EPPB->NMI_MASK0 = 1 << PIO0_IRQ_1; +#else + syscfg_hw->proc0_nmi_mask = 1 << PIO0_IRQ_1; +#endif + + oldNMIHandler = exception_set_exclusive_handler(NMI_EXCEPTION, loopEndHandler); + + //Reset loop counter + systickLoops = 0; + + //Enable systick + oldSysTickHandler = exception_set_exclusive_handler(SYSTICK_EXCEPTION, sysTickRoll); + systick_hw->rvr = 0x00FFFFFF; + systick_hw->cvr = 0x00FFFFFF; + systick_hw->csr = 0x7; + + } + + //Reset timestamp index + timestampIndex = 0; + + //Initialize state machine + pio_sm_init(capturePIO, sm_Capture, captureOffset, &smConfig); + + //Configure DMA's + configureCaptureDMAs(captureMode); + + //Enable state machine + pio_sm_set_enabled(capturePIO, sm_Capture, true); + + //Write loop count and capture length to post program to start the capture process + pio_sm_put_blocking(capturePIO, sm_Capture, loopCount); + pio_sm_put_blocking(capturePIO, sm_Capture, postLength - 1); + + + //Finally clear capture status, process flags and capture type + captureFinished = false; + captureProcessed = false; + lastCaptureType = CAPTURE_TYPE_SIMPLE; + + //We're done + return true; +} + +bool IsCapturing() +{ + //If you need an explanation of this, you're a fool. :P + return !captureFinished; +} + +uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* captureMode) +{ + //Compute total sample count + uint32_t totalSamples = lastPreSize + (lastPostSize * (lastLoopCount + 1)); + + //If we don't have processed the buffer... + if(!captureProcessed) + { + uint32_t maxSize; + + switch(lastCaptureMode) + { + case MODE_8_CHANNEL: + maxSize = CAPTURE_BUFFER_SIZE; + break; + case MODE_16_CHANNEL: + maxSize = CAPTURE_BUFFER_SIZE / 2; + break; + case MODE_24_CHANNEL: + maxSize = CAPTURE_BUFFER_SIZE / 4; + break; + } + //Calculate start position + if(lastTail < totalSamples - 1) + lastStartPosition = (maxSize - totalSamples) + lastTail + 1; + else + lastStartPosition = lastTail - totalSamples + 1; + + uint32_t currentPos = lastStartPosition; + + switch(lastCaptureMode) + { + case MODE_24_CHANNEL: + { + uint32_t oldValue; + uint32_t newValue; + uint32_t* buffer = (uint32_t*)captureBuffer; + uint8_t lastPin = 0; + uint32_t blastMask = 0; + + //If the capture was in blast mode and the trigger edge was positive, invert the value + if(lastCaptureType == CAPTURE_TYPE_BLAST && !lastTriggerInverted) + blastMask = 1 << (lastTriggerPin - INPUT_PIN_BASE); + + //Sort channels + //(reorder captured bits based on the channels requested) + for(uint32_t buc = 0; buc < totalSamples; buc++) + { + oldValue = buffer[currentPos]; //Store current value + newValue = 0; //New value + + //If the capture was in blast mode and the trigger edge was positive, invert the value + if(lastCaptureType == CAPTURE_TYPE_BLAST && !lastTriggerInverted) + oldValue ^= blastMask; + + for(int pin = 0; pin < lastCapturePinCount; pin++) //For each captured channel... + { + lastPin = lastCapturePins[pin] - INPUT_PIN_BASE; + newValue |= (((oldValue & (1 << lastPin))) >> lastPin) << pin; //Place channel data in the correct bit + } + + //Update value in the buffer + buffer[currentPos++] = newValue; + //If we reached the end of the buffer, wrap around + if(currentPos >= maxSize) + currentPos = 0; + } + } + break; + case MODE_16_CHANNEL: + { + uint16_t oldValue; + uint16_t newValue; + uint16_t* buffer = (uint16_t*)captureBuffer; + uint8_t lastPin = 0; + uint16_t blastMask = 0; + + //If the capture was in blast mode and the trigger edge was positive, invert the value + if(lastCaptureType == CAPTURE_TYPE_BLAST && !lastTriggerInverted) + blastMask = 1 << (lastTriggerPin - INPUT_PIN_BASE); + + //Sort channels + //(reorder captured bits based on the channels requested) + for(uint32_t buc = 0; buc < totalSamples; buc++) + { + oldValue = buffer[currentPos]; //Store current value + newValue = 0; //New value + + //If the capture was in blast mode and the trigger edge was positive, invert the value + if(lastCaptureType == CAPTURE_TYPE_BLAST && !lastTriggerInverted) + oldValue ^= blastMask; + + for(int pin = 0; pin < lastCapturePinCount; pin++) //For each captured channel... + { + lastPin = lastCapturePins[pin] - INPUT_PIN_BASE; + newValue |= (((oldValue & (1 << lastPin))) >> lastPin) << pin; //Place channel data in the correct bit + } + + //Update value in the buffer + buffer[currentPos++] = newValue; + //If we reached the end of the buffer, wrap around + if(currentPos >= maxSize) + currentPos = 0; + } + } + break; + case MODE_8_CHANNEL: + { + uint8_t oldValue; + uint8_t newValue; + uint8_t* buffer = (uint8_t*)captureBuffer; + uint8_t lastPin = 0; + uint8_t blastMask = 0; + + //If the capture was in blast mode and the trigger edge was positive, invert the value + if(lastCaptureType == CAPTURE_TYPE_BLAST && !lastTriggerInverted) + blastMask = 1 << (lastTriggerPin - INPUT_PIN_BASE); + + //Sort channels + //(reorder captured bits based on the channels requested) + for(uint32_t buc = 0; buc < totalSamples; buc++) + { + oldValue = buffer[currentPos]; //Store current value + + //If the capture was in blast mode and the trigger edge was positive, invert the value + if(lastCaptureType == CAPTURE_TYPE_BLAST && !lastTriggerInverted) + oldValue ^= blastMask; + + newValue = 0; //New value + + for(int pin = 0; pin < lastCapturePinCount; pin++) //For each captured channel... + { + lastPin = lastCapturePins[pin] - INPUT_PIN_BASE; + newValue |= (((oldValue & (1 << lastPin))) >> lastPin) << pin; //Place channel data in the correct bit + } + + //Update value in the buffer + buffer[currentPos++] = newValue; + //If we reached the end of the buffer, wrap around + if(currentPos >= maxSize) + currentPos = 0; + } + } + break; + } + captureProcessed = true; + } + //Return data + *captureMode = lastCaptureMode; + *bufferSize = totalSamples; + *firstSample = lastStartPosition; + return captureBuffer; +} + +volatile uint32_t* GetTimestamps(uint8_t* length) +{ + *length = timestampIndex; + return loopTimestamp; +} \ No newline at end of file diff --git a/LogicAnalyzer_Capture.h b/LogicAnalyzer_Capture.h new file mode 100644 index 0000000..5ed84be --- /dev/null +++ b/LogicAnalyzer_Capture.h @@ -0,0 +1,29 @@ +#include "LogicAnalyzer_Board_Settings.h" +#ifndef __ANALYZER_CAPTURE__ +#define __ANALYZER_CAPTURE__ + +#if defined(BUILD_PICO_2) +#include +#endif + +typedef enum +{ + MODE_8_CHANNEL, + MODE_16_CHANNEL, + MODE_24_CHANNEL + +} CHANNEL_MODE; + +bool StartCaptureSimple(uint32_t freq, uint32_t preLength, uint32_t postLength, uint8_t loopCount, uint8_t measureBursts, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode); +bool StartCaptureBlast(uint32_t freq, uint32_t length, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPin, bool invertTrigger, CHANNEL_MODE captureMode); +#ifdef SUPPORTS_COMPLEX_TRIGGER +bool StartCaptureComplex(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue, CHANNEL_MODE captureMode); +bool StartCaptureFast(uint32_t freq, uint32_t preLength, uint32_t postLength, const uint8_t* capturePins, uint8_t capturePinCount, uint8_t triggerPinBase, uint8_t triggerPinCount, uint16_t triggerValue, CHANNEL_MODE captureMode); +#endif +void StopCapture(); +bool IsCapturing(); +uint8_t* GetBuffer(uint32_t* bufferSize, uint32_t* firstSample, CHANNEL_MODE* captureMode); +volatile uint32_t* GetTimestamps(uint8_t* length); +void check_fast_interrupt(); + +#endif \ No newline at end of file diff --git a/LogicAnalyzer_Structs.h b/LogicAnalyzer_Structs.h new file mode 100644 index 0000000..18f9e7a --- /dev/null +++ b/LogicAnalyzer_Structs.h @@ -0,0 +1,110 @@ + +#ifndef __ANALYZER_STRUCTS__ + #define __ANALYZER_STRUCTS__ + + #include "pico/stdlib.h" + + + //Capture request issued by the host computer + typedef struct _CAPTURE_REQUEST + { + //Indicates tthe trigger type: 0 = edge, 1 = pattern (complex), 2 = pattern (fast) + uint8_t triggerType; + //Trigger channel (or base channel for pattern trigger) + uint8_t trigger; + + //Union of the trigger characteristics (inverted or pin count) + union + { + uint8_t inverted; + uint8_t count; + }; + + //Trigger value of the pattern trigger + uint16_t triggerValue; + //Channels to capture + uint8_t channels[24]; + //Channel count + uint8_t channelCount; + //Sampling frequency + uint32_t frequency; + //Number of samples stored before the trigger + uint32_t preSamples; + //Number of samples stored after the trigger + uint32_t postSamples; + //Number of capture loops + uint8_t loopCount; + //Measure burst times + uint8_t measure; + //Capture mode (0 = 8 channel, 1 = 16 channel, 2 = 24 channel) + uint8_t captureMode; + + }CAPTURE_REQUEST; + + #ifdef USE_CYGW_WIFI + + typedef struct _WIFI_SETTINGS + { + char apName[33]; + char passwd[64]; + char ipAddress[16]; + uint16_t port; + uint16_t checksum; + + } WIFI_SETTINGS; + + typedef struct _WIFI_SETTINGS_REQUEST + { + char apName[33]; + char passwd[64]; + char ipAddress[16]; + uint16_t port; + + } WIFI_SETTINGS_REQUEST; + + typedef enum + { + CYW_READY, + CONNECTED, + DISCONNECTED, + DATA_RECEIVED, + POWER_STATUS_DATA + + } WIFI_EVENT; + + typedef enum + { + LED_ON, + LED_OFF, + CONFIG_RECEIVED, + SEND_DATA, + GET_POWER_STATUS + + } FRONTEND_EVENT; + + typedef struct _EVENT_FROM_WIFI + { + WIFI_EVENT event; + char data[128]; + uint8_t dataLength; + + } EVENT_FROM_WIFI; + + typedef struct _EVENT_FROM_FRONTEND + { + FRONTEND_EVENT event; + char data[32]; + uint8_t dataLength; + + } EVENT_FROM_FRONTEND; + + typedef struct _POWER_STATUS + { + float vsysVoltage; + bool vbusConnected; + + } POWER_STATUS; + + #endif + +#endif \ No newline at end of file diff --git a/LogicAnalyzer_W2812.c b/LogicAnalyzer_W2812.c new file mode 100644 index 0000000..4d86fb5 --- /dev/null +++ b/LogicAnalyzer_W2812.c @@ -0,0 +1,70 @@ +#include "LogicAnalyzer_Board_Settings.h" + +#ifdef WS2812_LED + + #include "pico/stdlib.h" + #include "LogicAnalyzer_w2812.h" + + #define LONG_START 52.0 * (MAX_FREQ / 100000000.0) + #define SHORT_START 26.0 * (MAX_FREQ / 100000000.0) + #define LONG_END 52.0 * (MAX_FREQ / 100000000.0) + #define SHORT_END 25.0 * (MAX_FREQ / 100000000.0) + + void __attribute__ ((noinline)) delay_cycles4(uint32_t loops) + { + __asm__ __volatile__( + "mov r0, %[input_loops]\r\n" + "1:\r\n" + "sub r0, #1\r\n" + "bne 1b\r\n" + : + : [input_loops] "r" (loops) + ); + } + + unsigned char reverse_bits(unsigned char b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; + } + + void send_rgb(uint8_t r, uint8_t g, uint8_t b) + { + uint32_t rgb = reverse_bits(g) | (reverse_bits(r) << 8) | (reverse_bits(b) << 16); + + for(int buc = 0; buc < 24; buc++) + { + if(rgb & (1 << buc)) + { + gpio_put(LED_IO, true); + delay_cycles4(LONG_START); + gpio_put(LED_IO, false); + delay_cycles4(SHORT_END); + } + else + { + gpio_put(LED_IO, true); + delay_cycles4(SHORT_START); + gpio_put(LED_IO, false); + delay_cycles4(LONG_END); + } + } + + } + + void init_rgb() + { + gpio_init(LED_IO); + gpio_set_dir(LED_IO, true); + gpio_put(LED_IO, true); + sleep_us(500); + gpio_put(LED_IO, false); + sleep_us(500); + send_rgb(0, 0, 0); + sleep_ms(500); + send_rgb(0, 0, 0); + sleep_ms(500); + } + +#endif \ No newline at end of file diff --git a/LogicAnalyzer_W2812.h b/LogicAnalyzer_W2812.h new file mode 100644 index 0000000..8b27d86 --- /dev/null +++ b/LogicAnalyzer_W2812.h @@ -0,0 +1,14 @@ +#include "LogicAnalyzer_Board_Settings.h" + +#ifdef WS2812_LED + + #ifndef __LOGICANALYZER_W2812__ + + #define __LOGICANALYZER_W2812__ + + void send_rgb(uint8_t r, uint8_t g, uint8_t b); + void init_rgb(); + + #endif + +#endif \ No newline at end of file diff --git a/LogicAnalyzer_WiFi.c b/LogicAnalyzer_WiFi.c new file mode 100644 index 0000000..6d3ffd9 --- /dev/null +++ b/LogicAnalyzer_WiFi.c @@ -0,0 +1,308 @@ +#include "LogicAnalyzer_Board_Settings.h" + +#ifdef USE_CYGW_WIFI + +#include "Event_Machine.h" +#include "Shared_Buffers.h" +#include "LogicAnalyzer_WiFi.h" +#include "LogicAnalyzer_Structs.h" +#include +#include +#include "pico/stdlib.h" +#include "pico/cyw43_arch.h" +#include "pico/multicore.h" +#include "hardware/adc.h" +#include "hardware/gpio.h" +#include "hardware/flash.h" +#include "lwip/pbuf.h" +#include "lwip/tcp.h" + +EVENT_FROM_FRONTEND frontendEventBuffer; +WIFI_STATE_MACHINE currentState = VALIDATE_SETTINGS; +ip_addr_t address; +struct tcp_pcb* serverPcb; +struct tcp_pcb* clientPcb; + +bool apConnected = false; +bool boot = false; + +#define LED_ON() cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1) +#define LED_OFF() cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0) + +void getPowerStatus() +{ + EVENT_FROM_WIFI evtPower; + evtPower.event = POWER_STATUS_DATA; + evtPower.dataLength = sizeof(POWER_STATUS); + POWER_STATUS* status = (POWER_STATUS*)&evtPower.data; + + adc_init(); + + uint32_t oldInt = save_and_disable_interrupts(); + uint32_t old_pad = pads_bank0_hw->io[29]; + uint32_t old_ctrl = io_bank0_hw->io[29].ctrl; + + adc_gpio_init(29); + adc_select_input(3); + + sleep_ms(100); + + const float conversion_factor = 3.3f / (1 << 12); + status->vsysVoltage = adc_read() * conversion_factor * 3; + + gpio_init(29); + + pads_bank0_hw->io[29] = old_pad; + io_bank0_hw->io[29].ctrl = old_ctrl; + restore_interrupts(oldInt); + + status->vbusConnected = cyw43_arch_gpio_get(2); + + event_push(&wifiToFrontend, &evtPower); + +} + +void readSettings() +{ + wifiSettings = *((volatile WIFI_SETTINGS*)(FLASH_SETTINGS_ADDRESS)); +} + +void stopServer() +{ + if(serverPcb == NULL) + return; + + tcp_close(serverPcb); + serverPcb = NULL; +} + +void killClient() +{ + if(clientPcb != NULL) + { + tcp_recv(clientPcb, NULL); + tcp_err(clientPcb, NULL); + tcp_close(clientPcb); + clientPcb = NULL; + } + currentState = WAITING_TCP_CLIENT; +} + +void sendData(uint8_t* data, uint8_t len) +{ + while(clientPcb && tcp_sndbuf(clientPcb) < len) + { + cyw43_arch_poll(); + sleep_ms(1); + } + + if(tcp_write(clientPcb, data, len, TCP_WRITE_FLAG_COPY)) + { + killClient(); + EVENT_FROM_WIFI evt; + evt.event = DISCONNECTED; + event_push(&wifiToFrontend, &evt); + } + +} + +void serverError(void *arg, err_t err) +{ + killClient(); + + EVENT_FROM_WIFI evt; + evt.event = DISCONNECTED; + event_push(&wifiToFrontend, &evt); +} + +err_t serverReceiveData(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + EVENT_FROM_WIFI evt; + + //Client disconnected + if(!p || p->tot_len == 0) + { + if(p) + pbuf_free(p); + + killClient(); + evt.event = DISCONNECTED; + event_push(&wifiToFrontend, &evt); + return ERR_ABRT; + } + + uint16_t left = p->tot_len; + uint16_t pos = 0; + + while(left) + { + uint8_t copy = left > 128 ? 128 : left; + evt.event = DATA_RECEIVED; + evt.dataLength = copy; + pbuf_copy_partial(p, evt.data, copy, pos); + event_push(&wifiToFrontend, &evt); + pos += copy; + left -= copy; + } + + pbuf_free(p); + + return ERR_OK; + +} + +err_t acceptConnection(void *arg, struct tcp_pcb *client_pcb, err_t err) +{ + if (err != ERR_OK || client_pcb == NULL || clientPcb != NULL || currentState != WAITING_TCP_CLIENT) + return ERR_VAL; + + clientPcb = client_pcb; + + tcp_recv(clientPcb, serverReceiveData); + tcp_err(clientPcb, serverError); + + currentState = TCP_CLIENT_CONNECTED; + + EVENT_FROM_WIFI evt; + evt.event = CONNECTED; + event_push(&wifiToFrontend, &evt); + + return ERR_OK; +} + +bool tryStartServer() +{ + serverPcb = tcp_new_ip_type(IPADDR_TYPE_V4); + err_t err = tcp_bind(serverPcb, &address, wifiSettings.port); + + if (err) + return false; + + serverPcb = tcp_listen_with_backlog(serverPcb, 1); + + if(!serverPcb) + return false; + + tcp_accept(serverPcb, acceptConnection); +} + +bool tryConnectAP() +{ + if(cyw43_arch_wifi_connect_timeout_ms((const char*)wifiSettings.apName, (const char*)wifiSettings.passwd, CYW43_AUTH_WPA2_AES_PSK, 10000)) + return false; + + ipaddr_aton((const char*)wifiSettings.ipAddress, &address); + + netif_set_ipaddr(netif_list, &address); + + apConnected = true; + + return true; +} + +void disconnectAP() +{ + if(!apConnected) + return; + + cyw43_wifi_leave(&cyw43_state, 0); + apConnected = false; + +} + +void processWifiMachine() +{ + switch (currentState) + { + case VALIDATE_SETTINGS: + { + if(!boot) + readSettings(); + + boot = true; + + uint16_t checksum = 0; + + for(int buc = 0; buc < 33; buc++) + checksum += wifiSettings.apName[buc]; + + for(int buc = 0; buc < 64; buc++) + checksum += wifiSettings.passwd[buc]; + + for(int buc = 0; buc < 16; buc++) + checksum += wifiSettings.ipAddress[buc]; + + checksum += wifiSettings.port; + + checksum += 0x0f0f; + + if(wifiSettings.checksum == checksum) + currentState = CONNECTING_AP; + else + currentState = WAITING_SETTINGS; + } + break; + + case CONNECTING_AP: + if(tryConnectAP()) + currentState = STARTING_TCP_SERVER; + break; + case STARTING_TCP_SERVER: + if(tryStartServer()) + currentState = WAITING_TCP_CLIENT; + break; + default: + break; + } +} + +void frontendEvent(void* event) +{ + EVENT_FROM_FRONTEND* evt = (EVENT_FROM_FRONTEND*)event; + switch(evt->event) + { + case LED_ON: + LED_ON(); + break; + + case LED_OFF: + LED_OFF(); + break; + case CONFIG_RECEIVED: + + killClient(); + stopServer(); + disconnectAP(); + currentState = VALIDATE_SETTINGS; + break; + + case SEND_DATA: + sendData(evt->data, evt->dataLength); + break; + + case GET_POWER_STATUS: + getPowerStatus(); + break; + } +} + +void runWiFiCore() +{ + event_machine_init(&frontendToWifi, frontendEvent, sizeof(EVENT_FROM_FRONTEND), 8); + multicore_lockout_victim_init(); + cyw43_arch_init(); + cyw43_arch_enable_sta_mode(); + EVENT_FROM_WIFI evtRdy; + evtRdy.event = CYW_READY; + event_push(&wifiToFrontend, &evtRdy); + + while(true) + { + event_process_queue(&frontendToWifi, &frontendEventBuffer, 8); + processWifiMachine(); + if(currentState > CONNECTING_AP) + cyw43_arch_poll(); + } +} + +#endif \ No newline at end of file diff --git a/LogicAnalyzer_WiFi.h b/LogicAnalyzer_WiFi.h new file mode 100644 index 0000000..daec851 --- /dev/null +++ b/LogicAnalyzer_WiFi.h @@ -0,0 +1,24 @@ +#include "LogicAnalyzer_Board_Settings.h" + +#ifdef USE_CYGW_WIFI + + #ifndef __LOGICANALYZER_WIFI__ + #define __LOGICANALYZER_WIFI__ + + + typedef enum + { + VALIDATE_SETTINGS, + WAITING_SETTINGS, + CONNECTING_AP, + STARTING_TCP_SERVER, + WAITING_TCP_CLIENT, + TCP_CLIENT_CONNECTED + + } WIFI_STATE_MACHINE; + + void runWiFiCore(); + bool getVsysState(); + #endif + +#endif \ No newline at end of file diff --git a/Shared_Buffers.c b/Shared_Buffers.c new file mode 100644 index 0000000..b307bdf --- /dev/null +++ b/Shared_Buffers.c @@ -0,0 +1,11 @@ +#include "LogicAnalyzer_Board_Settings.h" +#ifdef USE_CYGW_WIFI + #include "Shared_Buffers.h" + #include "LogicAnalyzer_Structs.h" + #include "Event_Machine.h" + + + volatile WIFI_SETTINGS wifiSettings; + EVENT_MACHINE wifiToFrontend; + EVENT_MACHINE frontendToWifi; +#endif \ No newline at end of file diff --git a/Shared_Buffers.h b/Shared_Buffers.h new file mode 100644 index 0000000..35cc36b --- /dev/null +++ b/Shared_Buffers.h @@ -0,0 +1,16 @@ +#include "LogicAnalyzer_Board_Settings.h" +#ifdef USE_CYGW_WIFI + #ifndef __SHARED_BUFFERS__ + #define __SHARED_BUFFERS__ + #include "LogicAnalyzer_Structs.h" + #include "Event_Machine.h" + #include "hardware/flash.h" + + #define FLASH_SETTINGS_OFFSET ((2048 * 1024) - FLASH_SECTOR_SIZE) + #define FLASH_SETTINGS_ADDRESS (XIP_BASE + FLASH_SETTINGS_OFFSET) + + volatile extern WIFI_SETTINGS wifiSettings; + extern EVENT_MACHINE wifiToFrontend; + extern EVENT_MACHINE frontendToWifi; + #endif +#endif \ No newline at end of file diff --git a/lwipopts.h b/lwipopts.h new file mode 100644 index 0000000..217cb13 --- /dev/null +++ b/lwipopts.h @@ -0,0 +1,90 @@ +#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H +#define _LWIPOPTS_EXAMPLE_COMMONH_H + + +// Common settings used in most of the pico_w examples +// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details) + +// allow override in some examples +#ifndef NO_SYS +#define NO_SYS 1 +#endif +// allow override in some examples +#ifndef LWIP_SOCKET +#define LWIP_SOCKET 0 +#endif +#if PICO_CYW43_ARCH_POLL +#define MEM_LIBC_MALLOC 1 +#else +// MEM_LIBC_MALLOC is incompatible with non polling versions +#define MEM_LIBC_MALLOC 0 +#endif +#define MEM_ALIGNMENT 4 +#define MEM_SIZE 4000 +#define MEMP_NUM_TCP_SEG 32 +#define MEMP_NUM_ARP_QUEUE 10 +#define PBUF_POOL_SIZE 24 +#define LWIP_ARP 1 +#define LWIP_ETHERNET 1 +#define LWIP_ICMP 1 +#define LWIP_RAW 1 +#define TCP_WND (8 * TCP_MSS) +#define TCP_MSS 1460 +#define TCP_SND_BUF (8 * TCP_MSS) +#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS)) +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETCONN 0 +#define MEM_STATS 0 +#define SYS_STATS 0 +#define MEMP_STATS 0 +#define LINK_STATS 0 +// #define ETH_PAD_SIZE 2 +#define LWIP_CHKSUM_ALGORITHM 3 +#define LWIP_DHCP 1 +#define LWIP_IPV4 1 +#define LWIP_TCP 1 +#define LWIP_UDP 1 +#define LWIP_DNS 1 +#define LWIP_TCP_KEEPALIVE 1 +#define LWIP_NETIF_TX_SINGLE_PBUF 1 +#define DHCP_DOES_ARP_CHECK 0 +#define LWIP_DHCP_DOES_ACD_CHECK 0 + +#ifndef NDEBUG +#define LWIP_DEBUG 1 +#define LWIP_STATS 1 +#define LWIP_STATS_DISPLAY 1 +#endif + +#define ETHARP_DEBUG LWIP_DBG_OFF +#define NETIF_DEBUG LWIP_DBG_OFF +#define PBUF_DEBUG LWIP_DBG_OFF +#define API_LIB_DEBUG LWIP_DBG_OFF +#define API_MSG_DEBUG LWIP_DBG_OFF +#define SOCKETS_DEBUG LWIP_DBG_OFF +#define ICMP_DEBUG LWIP_DBG_OFF +#define INET_DEBUG LWIP_DBG_OFF +#define IP_DEBUG LWIP_DBG_OFF +#define IP_REASS_DEBUG LWIP_DBG_OFF +#define RAW_DEBUG LWIP_DBG_OFF +#define MEM_DEBUG LWIP_DBG_OFF +#define MEMP_DEBUG LWIP_DBG_OFF +#define SYS_DEBUG LWIP_DBG_OFF +#define TCP_DEBUG LWIP_DBG_OFF +#define TCP_INPUT_DEBUG LWIP_DBG_OFF +#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF +#define TCP_RTO_DEBUG LWIP_DBG_OFF +#define TCP_CWND_DEBUG LWIP_DBG_OFF +#define TCP_WND_DEBUG LWIP_DBG_OFF +#define TCP_FR_DEBUG LWIP_DBG_OFF +#define TCP_QLEN_DEBUG LWIP_DBG_OFF +#define TCP_RST_DEBUG LWIP_DBG_OFF +#define UDP_DEBUG LWIP_DBG_OFF +#define TCPIP_DEBUG LWIP_DBG_OFF +#define PPP_DEBUG LWIP_DBG_OFF +#define SLIP_DEBUG LWIP_DBG_OFF +#define DHCP_DEBUG LWIP_DBG_OFF + +#endif /* __LWIPOPTS_H__ */ diff --git a/pico_sdk_import.cmake b/pico_sdk_import.cmake new file mode 100644 index 0000000..a0721d0 --- /dev/null +++ b/pico_sdk_import.cmake @@ -0,0 +1,84 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_TAG} AND (NOT PICO_SDK_FETCH_FROM_GIT_TAG)) + set(PICO_SDK_FETCH_FROM_GIT_TAG $ENV{PICO_SDK_FETCH_FROM_GIT_TAG}) + message("Using PICO_SDK_FETCH_FROM_GIT_TAG from environment ('${PICO_SDK_FETCH_FROM_GIT_TAG}')") +endif () + +if (PICO_SDK_FETCH_FROM_GIT AND NOT PICO_SDK_FETCH_FROM_GIT_TAG) + set(PICO_SDK_FETCH_FROM_GIT_TAG "master") + message("Using master as default value for PICO_SDK_FETCH_FROM_GIT_TAG") +endif() + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") +set(PICO_SDK_FETCH_FROM_GIT_TAG "${PICO_SDK_FETCH_FROM_GIT_TAG}" CACHE FILEPATH "release tag for SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG ${PICO_SDK_FETCH_FROM_GIT_TAG} + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/publish.ps1 b/publish.ps1 new file mode 100644 index 0000000..9d6ca9a --- /dev/null +++ b/publish.ps1 @@ -0,0 +1,96 @@ +param ( + [Parameter(Mandatory=$true)] + [string]$packageName = "LogicAnalyzer" +) + +# Define board types and turbo mode options +$boardTypes = @("BOARD_PICO", "BOARD_PICO_W", "BOARD_PICO_W_WIFI", "BOARD_ZERO", "BOARD_PICO_2") +$turboModes = @("0", "1") + +# Path to the build settings file +$buildSettingsFile = "LogicAnalyzer_Build_Settings.cmake" + +# Paths from settings.json +$cmakePath = "${env:USERPROFILE}/.pico-sdk/cmake/v3.28.6/bin/cmake" +$ninjaPath = "${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1/ninja" +$picoSdkPath = "${env:USERPROFILE}/.pico-sdk/sdk/2.0.0" +$picoToolchainPath = "${env:USERPROFILE}/.pico-sdk/toolchain/13_2_Rel1" + +# Function to update the build settings file +function Update-BuildSettings { + param ( + [string]$boardType, + [string]$turboMode + ) + $content = Get-Content $buildSettingsFile + $content = $content -replace '(set\(BOARD_TYPE ".*"\))', "set(BOARD_TYPE `"$boardType`")" + $content = $content -replace '(set\(TURBO_MODE .*\))', "set(TURBO_MODE $turboMode)" + Set-Content $buildSettingsFile $content +} + +# Get the number of processors +$processorCount = [Environment]::ProcessorCount + +# Create the publish directory if it doesn't exist +$publishDir = ".\publish" +if (-Not (Test-Path -Path $publishDir)) { + New-Item -ItemType Directory -Path $publishDir +} else { + # Clear the publish directory + Remove-Item -Recurse -Force "$publishDir\*" +} + +# Loop through each board type and turbo mode combination +foreach ($boardType in $boardTypes) { + foreach ($turboMode in $turboModes) { + # Skip turbo mode for BOARD_PICO_W variants + if ($turboMode -eq "1" -and ($boardType -eq "BOARD_PICO_W" -or $boardType -eq "BOARD_PICO_W_WIFI")) { + continue + } + + # Update the build settings file + Update-BuildSettings -boardType $boardType -turboMode $turboMode + + # Clean the build directory + Remove-Item -Recurse -Force "build" + New-Item -ItemType Directory -Path "build" + Set-Location -Path "build" + + # Set environment variables + $env:PICO_SDK_PATH = $picoSdkPath + $env:PICO_TOOLCHAIN_PATH = $picoToolchainPath + $env:Path = "${env:USERPROFILE}/.pico-sdk/toolchain/13_2_Rel1/bin;${env:USERPROFILE}/.pico-sdk/picotool/2.0.0/picotool;${env:USERPROFILE}/.pico-sdk/cmake/v3.28.6/bin;${env:USERPROFILE}/.pico-sdk/ninja/v1.12.1;${env:Path}" + + # Run the CMake configuration command + & $cmakePath -G "Ninja" .. + + # Run the CMake build command + & $cmakePath --build . --config Release -- -j $processorCount + + # Check if the .uf2 file exists before moving it + $uf2File = "LogicAnalyzer.uf2" + if (Test-Path -Path $uf2File) { + # Determine the final binary name + if ($turboMode -eq "1") { + $binaryName = "${packageName}_${boardType}_Turbo.uf2" + } else { + $binaryName = "${packageName}_${boardType}.uf2" + } + + # Move the generated .uf2 file + Move-Item -Path $uf2File -Destination "..\$publishDir\$binaryName" + } else { + Write-Host "Error: $uf2File not found for $boardType with Turbo $turboMode" + } + + # Return to the root directory + Set-Location -Path ".." + } +} + +# Compress the .uf2 files and delete the originals +Get-ChildItem -Path $publishDir -Filter *.uf2 | ForEach-Object { + $zipFileName = "$($_.BaseName).zip" + Compress-Archive -Path $_.FullName -DestinationPath "$publishDir\$zipFileName" + Remove-Item -Path $_.FullName +} \ No newline at end of file