2023-02-04 14:49:15 +00:00
// -------------------------------------------------- //
// This file is autogenerated by pioasm; do not edit! //
// -------------------------------------------------- //
# pragma once
# if !PICO_NO_HARDWARE
# include "hardware/pio.h"
# endif
// ---------------- //
// POSITIVE_CAPTURE //
// ---------------- //
# define POSITIVE_CAPTURE_wrap_target 2
# define POSITIVE_CAPTURE_wrap 3
static const uint16_t POSITIVE_CAPTURE_program_instructions [ ] = {
0x80a0 , // 0: pull block
0x6020 , // 1: out x, 32
// .wrap_target
0x4000 , // 2: in pins, 32
0x00c4 , // 3: jmp pin, 4
// .wrap
0x4000 , // 4: in pins, 32
0x0044 , // 5: jmp x--, 4
0xc000 , // 6: irq nowait 0
0x0007 , // 7: jmp 7
} ;
# if !PICO_NO_HARDWARE
static const struct pio_program POSITIVE_CAPTURE_program = {
. instructions = POSITIVE_CAPTURE_program_instructions ,
. length = 8 ,
. origin = - 1 ,
} ;
static inline pio_sm_config POSITIVE_CAPTURE_program_get_default_config ( uint offset ) {
pio_sm_config c = pio_get_default_sm_config ( ) ;
sm_config_set_wrap ( & c , offset + POSITIVE_CAPTURE_wrap_target , offset + POSITIVE_CAPTURE_wrap ) ;
return c ;
}
# endif
// ---------------- //
// NEGATIVE_CAPTURE //
// ---------------- //
# define NEGATIVE_CAPTURE_wrap_target 0
# define NEGATIVE_CAPTURE_wrap 7
static const uint16_t NEGATIVE_CAPTURE_program_instructions [ ] = {
// .wrap_target
0x80a0 , // 0: pull block
0x6020 , // 1: out x, 32
0x4000 , // 2: in pins, 32
0x00c2 , // 3: jmp pin, 2
0x4000 , // 4: in pins, 32
0x0044 , // 5: jmp x--, 4
0xc000 , // 6: irq nowait 0
0x0007 , // 7: jmp 7
// .wrap
} ;
# if !PICO_NO_HARDWARE
static const struct pio_program NEGATIVE_CAPTURE_program = {
. instructions = NEGATIVE_CAPTURE_program_instructions ,
. length = 8 ,
. origin = - 1 ,
} ;
static inline pio_sm_config NEGATIVE_CAPTURE_program_get_default_config ( uint offset ) {
pio_sm_config c = pio_get_default_sm_config ( ) ;
sm_config_set_wrap ( & c , offset + NEGATIVE_CAPTURE_wrap_target , offset + NEGATIVE_CAPTURE_wrap ) ;
return c ;
}
# endif
// --------------- //
// COMPLEX_CAPTURE //
// --------------- //
# define COMPLEX_CAPTURE_wrap_target 3
# define COMPLEX_CAPTURE_wrap 4
static const uint16_t COMPLEX_CAPTURE_program_instructions [ ] = {
0x80a0 , // 0: pull block
0x6020 , // 1: out x, 32
0x20c7 , // 2: wait 1 irq, 7
// .wrap_target
0x401d , // 3: in pins, 29
0x00c5 , // 4: jmp pin, 5
// .wrap
0x401d , // 5: in pins, 29
0x0045 , // 6: jmp x--, 5
0xc000 , // 7: irq nowait 0
0x0008 , // 8: jmp 8
} ;
# if !PICO_NO_HARDWARE
static const struct pio_program COMPLEX_CAPTURE_program = {
. instructions = COMPLEX_CAPTURE_program_instructions ,
. length = 9 ,
. origin = - 1 ,
} ;
static inline pio_sm_config COMPLEX_CAPTURE_program_get_default_config ( uint offset ) {
pio_sm_config c = pio_get_default_sm_config ( ) ;
sm_config_set_wrap ( & c , offset + COMPLEX_CAPTURE_wrap_target , offset + COMPLEX_CAPTURE_wrap ) ;
return c ;
}
# endif
// ------------ //
// FAST_CAPTURE //
// ------------ //
# define FAST_CAPTURE_wrap_target 2
# define FAST_CAPTURE_wrap 3
static const uint16_t FAST_CAPTURE_program_instructions [ ] = {
0x80a0 , // 0: pull block
0x6020 , // 1: out x, 32
// .wrap_target
0x401d , // 2: in pins, 29
0x00c4 , // 3: jmp pin, 4
// .wrap
0x401d , // 4: in pins, 29
0x0044 , // 5: jmp x--, 4
0xc000 , // 6: irq nowait 0
0x0007 , // 7: jmp 7
} ;
# if !PICO_NO_HARDWARE
static const struct pio_program FAST_CAPTURE_program = {
. instructions = FAST_CAPTURE_program_instructions ,
. length = 8 ,
. origin = - 1 ,
} ;
static inline pio_sm_config FAST_CAPTURE_program_get_default_config ( uint offset ) {
pio_sm_config c = pio_get_default_sm_config ( ) ;
sm_config_set_wrap ( & c , offset + FAST_CAPTURE_wrap_target , offset + FAST_CAPTURE_wrap ) ;
return c ;
}
# include "../LogicAnalyzer_Build_Settings.h"
# include "hardware/gpio.h"
# include "hardware/dma.h"
# include "hardware/irq.h"
# include "string.h"
# include "hardware/sync.h"
typedef enum
{
MODE_8_CHANNEL ,
MODE_16_CHANNEL ,
MODE_24_CHANNEL
} CHANNEL_MODE ;
//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 dmaPingPong2 ;
static uint32_t dmaPingPong3 ;
//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 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 ;
//Pin mapping, used to map the channels to the PIO program
//Could be stored into flash memory but it causes problems
2023-02-25 11:25:37 +00:00
//Channel25 (array pos 24) is only used for the external trigger and the simple capture program
//Any other usage will cause an error
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 , 1 } ;
2023-02-04 14:49:15 +00:00
//Main capture buffer, aligned at a 32k boundary, to use the maxixmum ring size supported by DMA channels
static uint8_t captureBuffer [ 128 * 1024 ] __attribute__ ( ( aligned ( 32768 ) ) ) ;
//-----------------------------------------------------------------------------
//--------------Complex trigger PIO program------------------------------------
//-----------------------------------------------------------------------------
# define COMPLEX_TRIGGER_wrap_target 0
# define COMPLEX_TRIGGER_wrap 8
# define CAPTURE_TYPE_SIMPLE 0
# define CAPTURE_TYPE_COMPLEX 1
# define CAPTURE_TYPE_FAST 2
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 ;
}
//-----------------------------------------------------------------------------
//--------------Complex trigger PIO program END--------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//--------------Fast trigger PIO program---------------------------------------
//-----------------------------------------------------------------------------
# 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 ;
}
//-----------------------------------------------------------------------------
//--------------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 = 32768 ;
break ;
case MODE_16_CHANNEL :
transferCount = 16384 ;
break ;
case MODE_24_CHANNEL :
transferCount = 8192 ;
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 ) ;
uint32_t busy_channel = 0xFFFFFFFF ;
uint32_t busy_offset = 0xFFFFFFFF ;
//First we need to determine which DMA channel is busy (in the middle of a transfer)
if ( dma_channel_is_busy ( dmaPingPong0 ) )
{
busy_channel = dmaPingPong0 ;
busy_offset = 0 ;
}
if ( dma_channel_is_busy ( dmaPingPong1 ) )
{
busy_channel = dmaPingPong1 ;
busy_offset = transferCount ;
}
if ( dma_channel_is_busy ( dmaPingPong2 ) )
{
busy_channel = dmaPingPong2 ;
busy_offset = transferCount * 2 ;
}
if ( dma_channel_is_busy ( dmaPingPong3 ) )
{
busy_channel = dmaPingPong3 ;
busy_offset = transferCount * 3 ;
}
//No channel busy?? WTF???
if ( busy_channel = = 0xFFFFFFFF )
return 0xFFFFFFFF ;
//Ok, now we need to know at which transfer the DMA is. The value equals to MAX_TRANSFERS - TRANSFERS_LEFT.
int32_t transfer = transferCount - dma_channel_hw_addr ( busy_channel ) - > transfer_count ;
//Now compute the last capture position
transfer = ( transfer + busy_offset ) - 1 ;
//Wrap around?
if ( transfer < 0 )
transfer = ( transferCount * 4 ) - 1 ;
//Our capture absolute last position
return ( uint32_t ) transfer ;
}
2023-02-25 11:25:37 +00:00
//Disable the trigger GPIOs to avoid triggering again a chained device
void disable_gpios ( )
{
gpio_deinit ( 0 ) ;
gpio_deinit ( 1 ) ;
for ( uint8_t i = 0 ; i < lastCapturePinCount ; i + + )
gpio_deinit ( lastCapturePins [ i ] ) ;
}
2023-02-04 14:49:15 +00:00
//Triggered when a fast capture ends
void fast_capture_completed ( )
{
2023-02-25 11:25:37 +00:00
//Disable the GPIO's
disable_gpios ( ) ;
2023-02-04 14:49:15 +00:00
//Mark the capture as finished
captureFinished = true ;
lastTail = find_capture_tail ( ) ;
//Abort DMA channels
dma_channel_abort ( dmaPingPong0 ) ;
dma_channel_abort ( dmaPingPong1 ) ;
dma_channel_abort ( dmaPingPong2 ) ;
dma_channel_abort ( dmaPingPong3 ) ;
//Clear PIO interrupt 0 and unhook handler
pio_interrupt_clear ( capturePIO , 0 ) ;
irq_set_enabled ( pio_get_dreq ( capturePIO , sm_Capture , false ) , false ) ;
//Disable all DMA channels
dma_channel_unclaim ( dmaPingPong0 ) ;
dma_channel_unclaim ( dmaPingPong1 ) ;
dma_channel_unclaim ( dmaPingPong2 ) ;
dma_channel_unclaim ( dmaPingPong3 ) ;
//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 ) ;
}
2023-02-25 11:25:37 +00:00
//Check if the capture has finished, this is done because the W messes the PIO interrupts
2023-02-04 14:49:15 +00:00
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 ( )
{
2023-02-25 11:25:37 +00:00
//Disable the GPIO's
disable_gpios ( ) ;
2023-02-04 14:49:15 +00:00
//Mark the capture as finished
captureFinished = true ;
lastTail = find_capture_tail ( ) ;
//Abort DMA channels
dma_channel_abort ( dmaPingPong0 ) ;
dma_channel_abort ( dmaPingPong1 ) ;
dma_channel_abort ( dmaPingPong2 ) ;
dma_channel_abort ( dmaPingPong3 ) ;
//Clear PIO interrupt 0 and unhook handler
pio_interrupt_clear ( capturePIO , 0 ) ;
irq_set_enabled ( PIO0_IRQ_0 , false ) ;
irq_set_enabled ( pio_get_dreq ( capturePIO , sm_Capture , false ) , false ) ;
irq_remove_handler ( PIO0_IRQ_0 , complex_capture_completed ) ;
//Disable all DMA channels
dma_channel_unclaim ( dmaPingPong0 ) ;
dma_channel_unclaim ( dmaPingPong1 ) ;
dma_channel_unclaim ( dmaPingPong2 ) ;
dma_channel_unclaim ( dmaPingPong3 ) ;
//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 ) ;
}
//Triggered when a simple capture ends
void simple_capture_completed ( )
{
2023-02-25 11:25:37 +00:00
//Disable the GPIO's
disable_gpios ( ) ;
2023-02-04 14:49:15 +00:00
//Mark the capture as finished
captureFinished = true ;
lastTail = find_capture_tail ( ) ;
//Abort DMA channels
dma_channel_abort ( dmaPingPong0 ) ;
dma_channel_abort ( dmaPingPong1 ) ;
dma_channel_abort ( dmaPingPong2 ) ;
dma_channel_abort ( dmaPingPong3 ) ;
//Clear PIO interrupt 0 and unhook handler
pio_interrupt_clear ( capturePIO , 0 ) ;
irq_set_enabled ( PIO0_IRQ_0 , false ) ;
irq_set_enabled ( pio_get_dreq ( capturePIO , sm_Capture , false ) , false ) ;
irq_remove_handler ( PIO0_IRQ_0 , simple_capture_completed ) ;
dma_channel_unclaim ( dmaPingPong0 ) ;
dma_channel_unclaim ( dmaPingPong1 ) ;
dma_channel_unclaim ( dmaPingPong2 ) ;
dma_channel_unclaim ( dmaPingPong3 ) ;
//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 ) ;
}
//Configure the four DMA channels
void configureCaptureDMAs ( CHANNEL_MODE channelMode )
{
enum dma_channel_transfer_size transferSize ;
uint32_t transferCount ;
switch ( channelMode )
{
case MODE_8_CHANNEL :
transferSize = DMA_SIZE_8 ;
transferCount = 32768 ;
break ;
case MODE_16_CHANNEL :
transferSize = DMA_SIZE_16 ;
transferCount = 16384 ;
break ;
case MODE_24_CHANNEL :
transferSize = DMA_SIZE_32 ;
transferCount = 8192 ;
break ;
}
//Claim four DMA channels, each channel writes to 32Kb of the buffer (8192 samples) as that's the maximum ring size supported
dmaPingPong0 = dma_claim_unused_channel ( true ) ;
dmaPingPong1 = dma_claim_unused_channel ( true ) ;
dmaPingPong2 = dma_claim_unused_channel ( true ) ;
dmaPingPong3 = dma_claim_unused_channel ( true ) ;
//Configure first capture DMA
dma_channel_config 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 32 bits each time
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_ring ( & dmaPingPong0Config , true , 15 ) ; //Ring at 32768 bytes
//Configure second capture DMA
dma_channel_config 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 32 bits each time
channel_config_set_chain_to ( & dmaPingPong1Config , dmaPingPong2 ) ; //Chain to the third dma channel
channel_config_set_dreq ( & dmaPingPong1Config , pio_get_dreq ( capturePIO , sm_Capture , false ) ) ; //Set DREQ as RX FIFO
channel_config_set_ring ( & dmaPingPong1Config , true , 15 ) ; //Ring at 32768 bytes
//Configure third capture DMA
dma_channel_config dmaPingPong2Config = dma_channel_get_default_config ( dmaPingPong2 ) ;
channel_config_set_read_increment ( & dmaPingPong2Config , false ) ; //Do not increment read address
channel_config_set_write_increment ( & dmaPingPong2Config , true ) ; //Increment write address
channel_config_set_transfer_data_size ( & dmaPingPong2Config , transferSize ) ; //Transfer 32 bits each time
channel_config_set_chain_to ( & dmaPingPong2Config , dmaPingPong3 ) ; //Chain to the fourth dma channel
channel_config_set_dreq ( & dmaPingPong2Config , pio_get_dreq ( capturePIO , sm_Capture , false ) ) ; //Set DREQ as RX FIFO
channel_config_set_ring ( & dmaPingPong2Config , true , 15 ) ; //Ring at 32768 bytes
//Configure fourth capture DMA
dma_channel_config dmaPingPong3Config = dma_channel_get_default_config ( dmaPingPong3 ) ;
channel_config_set_read_increment ( & dmaPingPong3Config , false ) ; //Do not increment read address
channel_config_set_write_increment ( & dmaPingPong3Config , true ) ; //Increment write address
channel_config_set_transfer_data_size ( & dmaPingPong3Config , transferSize ) ; //Transfer 32 bits each time
channel_config_set_chain_to ( & dmaPingPong3Config , dmaPingPong0 ) ; //Chain to the first dma channel
channel_config_set_dreq ( & dmaPingPong3Config , pio_get_dreq ( capturePIO , sm_Capture , false ) ) ; //Set DREQ as RX FIFO
channel_config_set_ring ( & dmaPingPong3Config , true , 15 ) ; //Ring at 32768 bytes
//Configure the DMA channels
dma_channel_configure ( dmaPingPong3 , & dmaPingPong3Config , & captureBuffer [ 32768 * 3 ] , & capturePIO - > rxf [ sm_Capture ] , transferCount , false ) ; //Configure the channel
dma_channel_configure ( dmaPingPong2 , & dmaPingPong2Config , & captureBuffer [ 32768 * 2 ] , & capturePIO - > rxf [ sm_Capture ] , transferCount , false ) ; //Configure the channel
dma_channel_configure ( dmaPingPong1 , & dmaPingPong1Config , & captureBuffer [ 32768 ] , & capturePIO - > rxf [ sm_Capture ] , transferCount , false ) ; //Configure the channel
dma_channel_configure ( dmaPingPong0 , & dmaPingPong0Config , captureBuffer , & capturePIO - > rxf [ sm_Capture ] , transferCount , true ) ;
}
void stopCapture ( )
{
if ( ! captureFinished )
{
uint32_t int_status = save_and_disable_interrupts ( ) ;
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 ( ) ;
restore_interrupts ( int_status ) ;
}
}
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 = 131072 ;
break ;
case MODE_16_CHANNEL :
maxSamples = 65536 ;
break ;
case MODE_24_CHANNEL :
maxSamples = 32768 ;
break ;
}
//Too many samples requested?
if ( preLength + postLength > = maxSamples )
return false ;
//Frequency too high?
if ( freq > 100000000 )
return false ;
//Incorrect pin count?
if ( capturePinCount < 0 | | capturePinCount > 24 )
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 ;
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 , 0 ) ;
pio_gpio_init ( capturePIO , 1 ) ;
2023-02-25 11:25:37 +00:00
for ( uint8_t i = 0 ; i < 24 ; i + + )
pio_gpio_init ( capturePIO , pinMap [ i ] ) ;
2023-02-04 14:49:15 +00:00
//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 2
sm_config_set_in_pins ( & smConfig , 2 ) ;
//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 (pin 1) as JMP pin.
sm_config_set_jmp_pin ( & smConfig , 1 ) ;
//Configure interrupt 0
pio_interrupt_clear ( capturePIO , 0 ) ;
irq_set_enabled ( pio_get_dreq ( capturePIO , sm_Capture , false ) , true ) ;
//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 , 0 , 1 , true ) ; //Pin 0 as output (connects to Pin 1, 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 , 0 , 1 ) ; //Trigger output is a set pin
sm_config_set_sideset_pins ( & smConfig , 0 ) ; //Trigger output is a side pin
2023-02-25 11:25:37 +00:00
sm_config_set_clkdiv ( & smConfig , 1 ) ; //Trigger always runs at max speed
2023-02-04 14:49:15 +00:00
//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 = 131072 ;
break ;
case MODE_16_CHANNEL :
maxSamples = 65536 ;
break ;
case MODE_24_CHANNEL :
maxSamples = 32768 ;
break ;
}
//Too many samples requested?
if ( preLength + postLength > = maxSamples )
return false ;
//Frequency too high?
if ( freq > 100000000 )
return false ;
//Incorrect pin count?
if ( capturePinCount < 0 | | capturePinCount > 24 )
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 ;
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 , 0 ) ;
pio_gpio_init ( capturePIO , 1 ) ;
2023-02-25 11:25:37 +00:00
for ( uint8_t i = 0 ; i < 24 ; i + + )
pio_gpio_init ( capturePIO , pinMap [ i ] ) ;
2023-02-04 14:49:15 +00:00
//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 2
sm_config_set_in_pins ( & smConfig , 2 ) ;
//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 comples trigger pin (pin 1) as JMP pin.
sm_config_set_jmp_pin ( & smConfig , 1 ) ;
//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 ) ;
irq_set_enabled ( pio_get_dreq ( capturePIO , sm_Capture , false ) , true ) ;
//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 , 0 , 1 , true ) ; //Pin 0 as output (connects to Pin 1, 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 , 0 , 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 ;
}
bool startCaptureSimple ( uint32_t freq , uint32_t preLength , uint32_t postLength , const uint8_t * capturePins , uint8_t capturePinCount , uint8_t triggerPin , bool invertTrigger , CHANNEL_MODE captureMode )
{
int maxSamples ;
switch ( captureMode )
{
case MODE_8_CHANNEL :
maxSamples = 131072 ;
break ;
case MODE_16_CHANNEL :
maxSamples = 65536 ;
break ;
case MODE_24_CHANNEL :
maxSamples = 32768 ;
break ;
}
//Too many samples requested?
if ( preLength + postLength > = maxSamples )
return false ;
//Frequency too high?
if ( freq > 100000000 )
return false ;
//Incorrect pin count?
if ( capturePinCount < 0 | | capturePinCount > 24 )
return false ;
2023-02-25 11:25:37 +00:00
//Incorrect trigger pin?
if ( triggerPin < 0 | | triggerPin > 24 )
return false ;
2023-02-04 14:49:15 +00:00
//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 ;
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 )
captureOffset = pio_add_program ( capturePIO , & NEGATIVE_CAPTURE_program ) ;
else
captureOffset = pio_add_program ( capturePIO , & POSITIVE_CAPTURE_program ) ;
2023-02-25 11:25:37 +00:00
//Configure capture pins
2023-02-04 14:49:15 +00:00
for ( int i = 0 ; i < 24 ; i + + )
pio_sm_set_consecutive_pindirs ( capturePIO , sm_Capture , pinMap [ i ] , 1 , false ) ;
2023-02-25 11:25:37 +00:00
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 ) ;
2023-02-04 14:49:15 +00:00
pio_gpio_init ( capturePIO , triggerPin ) ;
//Configure state machines
pio_sm_config smConfig = invertTrigger ?
NEGATIVE_CAPTURE_program_get_default_config ( captureOffset ) :
POSITIVE_CAPTURE_program_get_default_config ( captureOffset ) ;
//All pins are inputs
sm_config_set_in_pins ( & smConfig , 2 ) ;
//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 ) ;
irq_set_enabled ( pio_get_dreq ( capturePIO , sm_Capture , false ) , true ) ;
//Initialize state machine
pio_sm_init ( capturePIO , sm_Capture , captureOffset , & smConfig ) ;
irq_clear ( pio_get_dreq ( capturePIO , sm_Capture , false ) ) ;
//Configure DMA's
configureCaptureDMAs ( captureMode ) ;
//Enabl state machine
pio_sm_set_enabled ( capturePIO , sm_Capture , true ) ;
//Write capture length to post program to start the capture process
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 )
{
//If we don't have processed the buffer...
if ( ! captureProcessed )
{
int maxSize ;
switch ( lastCaptureMode )
{
case MODE_8_CHANNEL :
maxSize = 131072 ;
break ;
case MODE_16_CHANNEL :
maxSize = 65536 ;
break ;
case MODE_24_CHANNEL :
maxSize = 32768 ;
break ;
}
//Calculate start position
if ( lastTail < lastPreSize + lastPostSize - 1 )
lastStartPosition = maxSize - ( ( lastPreSize + lastPostSize ) - ( lastTail - 1 ) ) ;
else
lastStartPosition = lastTail - ( lastPreSize + lastPostSize ) + 1 ;
uint32_t currentPos = lastStartPosition ;
switch ( lastCaptureMode )
{
case MODE_24_CHANNEL :
{
uint32_t oldValue ;
uint32_t newValue ;
uint32_t * buffer = ( uint32_t * ) captureBuffer ;
//Sort channels
//(reorder captured bits based on the channels requested)
for ( int buc = 0 ; buc < lastPreSize + lastPostSize ; buc + + )
{
oldValue = buffer [ currentPos ] ; //Store current value
newValue = 0 ; //New value
for ( int pin = 0 ; pin < lastCapturePinCount ; pin + + ) //For each captured channel...
newValue | = ( oldValue & ( 1 < < ( lastCapturePins [ pin ] - 2 ) ) ) > > ( ( lastCapturePins [ pin ] - 2 ) - pin ) ; //Store its value 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 ;
//Sort channels
//(reorder captured bits based on the channels requested)
for ( int buc = 0 ; buc < lastPreSize + lastPostSize ; buc + + )
{
oldValue = buffer [ currentPos ] ; //Store current value
newValue = 0 ; //New value
for ( int pin = 0 ; pin < lastCapturePinCount ; pin + + ) //For each captured channel...
newValue | = ( oldValue & ( 1 < < ( lastCapturePins [ pin ] - 2 ) ) ) > > ( ( lastCapturePins [ pin ] - 2 ) - pin ) ; //Store its value 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 ;
//Sort channels
//(reorder captured bits based on the channels requested)
for ( int buc = 0 ; buc < lastPreSize + lastPostSize ; buc + + )
{
oldValue = buffer [ currentPos ] ; //Store current value
newValue = 0 ; //New value
for ( int pin = 0 ; pin < lastCapturePinCount ; pin + + ) //For each captured channel...
newValue | = ( oldValue & ( 1 < < ( lastCapturePins [ pin ] - 2 ) ) ) > > ( ( lastCapturePins [ pin ] - 2 ) - pin ) ; //Store its value 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 = lastPreSize + lastPostSize ;
* firstSample = lastStartPosition ;
return captureBuffer ;
}
# endif