logic analyzer v2 firmware initial

This commit is contained in:
XupaMisto 2026-04-21 22:21:41 +01:00
commit f87dbb9aa7
20 changed files with 3590 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
build/
.vscode/
*.uf2
*.elf
*.bin
*.map
CMakeFiles/
CMakeCache.txt

137
CMakeLists.txt Normal file
View File

@ -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 program>.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)

44
Event_Machine.c Normal file
View File

@ -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;
}

27
Event_Machine.h Normal file
View File

@ -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

774
LogicAnalyzer.c Normal file
View File

@ -0,0 +1,774 @@
#include "LogicAnalyzer_Board_Settings.h"
#include <stdio.h>
#include <string.h>
#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;
}

241
LogicAnalyzer.pio Normal file
View File

@ -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

View File

@ -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

View File

@ -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)

1375
LogicAnalyzer_Capture.c Normal file

File diff suppressed because it is too large Load Diff

29
LogicAnalyzer_Capture.h Normal file
View File

@ -0,0 +1,29 @@
#include "LogicAnalyzer_Board_Settings.h"
#ifndef __ANALYZER_CAPTURE__
#define __ANALYZER_CAPTURE__
#if defined(BUILD_PICO_2)
#include <RP2350.h>
#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

110
LogicAnalyzer_Structs.h Normal file
View File

@ -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

70
LogicAnalyzer_W2812.c Normal file
View File

@ -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

14
LogicAnalyzer_W2812.h Normal file
View File

@ -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

308
LogicAnalyzer_WiFi.c Normal file
View File

@ -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 <stdio.h>
#include <string.h>
#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

24
LogicAnalyzer_WiFi.h Normal file
View File

@ -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

11
Shared_Buffers.c Normal file
View File

@ -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

16
Shared_Buffers.h Normal file
View File

@ -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

90
lwipopts.h Normal file
View File

@ -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__ */

84
pico_sdk_import.cmake Normal file
View File

@ -0,0 +1,84 @@
# This is a copy of <PICO_SDK_PATH>/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})

96
publish.ps1 Normal file
View File

@ -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
}