├── Hex ├── tycmd └── tycmd.exe ├── Users_Guide.odt ├── Users_Guide.pdf ├── LICENSE ├── record_high_speed_data_8_channels.h ├── installation.md ├── interrupts.ino ├── types.h ├── LogicSniffer ├── ols.profile-teensy_lc_48.cfg ├── ols.profile-teensy_lc_48_demo.cfg ├── ols.profile-teensy_96.cfg ├── ols.profile-teensy_144.cfg ├── ols.profile-teensy_96_demo.cfg ├── ols.profile-teensy35_120.cfg ├── ols.profile-teensy36_240.cfg ├── ols.profile-teensy_lc_48_hardware.cfg ├── ols.profile-teensy35_120_demo.cfg ├── ols.profile-teensy35_120_hardware.cfg ├── ols.profile-teensy_144_hardware.cfg ├── ols.profile-teensy36_240_demo.cfg ├── ols.profile-teensy40_600.cfg ├── ols.profile-teensy36_240_hardware.cfg └── ols.profile-teensy_96_hardware.cfg ├── README.md ├── t4_overclock.ino ├── record_low_speed_data.ino ├── Test └── Test_Cases.txt ├── send_data.ino ├── record_rle_data.ino ├── record_hardware.h ├── record_high_speed_data_8_channels.cpp ├── record_high_speed_rle_data.ino └── record_hardware.ino /Hex/tycmd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAtimes2/TeensyLogicAnalyzer/HEAD/Hex/tycmd -------------------------------------------------------------------------------- /Hex/tycmd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAtimes2/TeensyLogicAnalyzer/HEAD/Hex/tycmd.exe -------------------------------------------------------------------------------- /Users_Guide.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAtimes2/TeensyLogicAnalyzer/HEAD/Users_Guide.odt -------------------------------------------------------------------------------- /Users_Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LAtimes2/TeensyLogicAnalyzer/HEAD/Users_Guide.pdf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 LAtimes2 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /record_high_speed_data_8_channels.h: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2020 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | #include "types.h" 28 | 29 | void recordHighSpeedData_8_Channels (sumpSetupVariableStruct &sv, 30 | sumpDynamicVariableStruct &dynamic); 31 | -------------------------------------------------------------------------------- /installation.md: -------------------------------------------------------------------------------- 1 | ## To install: 2 | 3 | 1. Download files from github. Download zip. Unzip to any directory - desktop or documents will work. 4 | 2. Get a sump GUI. I use Logic Sniffer (ols.lxtreme.nl). Download the latest zip file and unzip to any directory. Requires Java JRE to be installed. 5 | 3. If using Logic Sniffer, copy the configuration files from TeensyLogicAnalyzer/LogicSniffer to ols-0.9.7.2/plugins 6 | 7 | ## To load Teensy Hex file (no need to compile): 8 | * Run Teensy or TyQt to load desired file from Hex directory 9 | 10 | ## To compile: 11 | 12 | * Teensy 3.1/3.2 - set Tools -> Clock Speed to 96 MHz optimized (overclock) 13 | * Teensy 3.0 - set Tools -> Clock Speed to 96 MHz (overclock) 14 | * Teensy LC - set Tools -> Clock Speed to 48 MHz optimized 15 | 16 | It will get a warning, "Low memory available, stability problems may occur". This is ok - we want to record as much data as possible :) 17 | 18 | If you don't have any signals to connect, you can change the $define CREATE_TEST_FREQUENCIES to 1 to generate test signals. This will output PWM signals on certain pins. No jumpering of pins is necessary to see the data. Output pins are 3,4,5 (and 6,7 on 3.x). 19 | 20 | ## To run: 21 | 22 | 1. Load Teensy with the logic analyzer code and run (LED should be blinking every 2 seconds). 23 | 2. In ols-0.9.7.2 directory, run or click on run.bat. 24 | 3. Select Capture -> Begin Capture 25 | 4. Set Connection type to Serial port, Analyzer port to your Teensy COM port (verify at Teensyduino Tools -> Port), Port Speed 115200bps (may not matter since USB) 26 | 5. Set Device type as follows: (if it's not available, did you copy the .cfg file correctly?) 27 | * Teensy 3.0/3.1/3.2 - Teensy 96 MHz OLS 28 | * Teensy LC - Teensy LC 48 MHz OLS 29 | 6. Select Acquisition tab and set Sampling Rate and Recording Size to desired settings. 30 | 7. If a trigger is desired, select Triggers tab, set Enabled, set Before/After ratio, and select Mask and Value. 31 | 8. Select Capture. Data should show up in window. 32 | 33 | Basic pinout: 34 | 35 | Channel | Pin 36 | :-------:|:---: 37 | 0 | 2 38 | 1 | 14 39 | 2 | 7 40 | 3 | 8 41 | 4 | 6 42 | 5 | 20 43 | 6 | 21 44 | 7 | 5 45 | 46 | Hardware pinout: 47 | 48 | Channel | Pin 49 | :-------:|:---: 50 | 0 | 1 51 | 1 | 8 52 | 53 | See Users_Guide.pdf for more details and how to build/use Hardware configurations. 54 | -------------------------------------------------------------------------------- /interrupts.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2020 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // Routines to turn off interrupts and turn them back on 26 | 27 | bool interrupts_masked = false; 28 | 29 | bool getInterruptsMasked (void) { 30 | return interrupts_masked; 31 | } 32 | 33 | void maskInterrupts (void) { 34 | 35 | // Disable SysTick Exception - delay() will not work while disabled 36 | SYST_CSR &= ~SYST_CSR_TICKINT; 37 | 38 | // Mask USB interrupt 39 | #if Teensy_4_0 40 | NVIC_DISABLE_IRQ (IRQ_USB1); 41 | #else 42 | NVIC_DISABLE_IRQ (IRQ_USBOTG); 43 | #endif 44 | 45 | interrupts_masked = true; 46 | } 47 | 48 | void unmaskInterrupts (void) { 49 | 50 | // Re-enable SysTick Exception 51 | 52 | // Reading CSR register will also clear COUNTFLAG 53 | int flag = SYST_CSR; 54 | 55 | // If an interrupt was missed, cause one to occur 56 | if (flag & SYST_CSR_COUNTFLAG) { 57 | SCB_ICSR |= SCB_ICSR_PENDSTSET; 58 | } 59 | 60 | SYST_CSR |= SYST_CSR_TICKINT; 61 | 62 | // Unmask USB interrupt 63 | #if Teensy_4_0 64 | NVIC_ENABLE_IRQ (IRQ_USB1); 65 | #else 66 | NVIC_ENABLE_IRQ (IRQ_USBOTG); 67 | #endif 68 | 69 | interrupts_masked = false; 70 | } 71 | 72 | // returns true if a USB interrupt is pending (meaning data is available) 73 | inline bool usbInterruptPending (void) { 74 | 75 | #if Teensy_4_0 76 | return (USB1_ENDPTCOMPLETE); 77 | #else 78 | return (USB0_ISTAT & ~USB_ISTAT_SOFTOK); 79 | #endif 80 | 81 | } 82 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2021 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | enum stateType { 28 | Buffering, 29 | LookingForTrigger, 30 | LookingForSimpleTrigger, 31 | TriggerDelay, 32 | Triggered_First_Pass, 33 | Triggered_Second_Pass, 34 | Triggered 35 | }; 36 | 37 | enum TriggerType { 38 | Channel0High, 39 | Channel0Low, 40 | Channel1High, 41 | Channel1Low, 42 | BothChannelsHigh, 43 | BothChannelsLow, 44 | HighLow, 45 | LowHigh 46 | }; 47 | 48 | // data to set up recording 49 | struct sumpSetupVariableStruct { 50 | uint32_t anyDataMask; 51 | uint32_t busClockDivisor; 52 | uint32_t bytesPerElement = 1; 53 | uint32_t cpuClockTicks; 54 | uint32_t clockFrequency; 55 | uint32_t delaySamples = 0; 56 | uint32_t delaySamplesRequested = 0; 57 | uint32_t delaySizeInElements = 0; 58 | uint32_t firstRLEValue = 0; 59 | uint32_t numberOfChannels = 8; 60 | uint32_t sampleMask = 0xFF; 61 | uint32_t sampleShift = 8; 62 | uint32_t samplesPerElement = 4; 63 | uint32_t samplesRequested = 0; 64 | uint32_t samplesToRecord = 0; 65 | uint32_t samplesToSend; 66 | bool rawPortRead = false; // if true, buffer contains raw GPIO values 67 | bool swapChannels = false; 68 | uint32_t logicalToPhysicalChannel[16]; 69 | int lastTriggerLevel; 70 | byte rleCountIndicator = 0; 71 | uint32_t triggerMask[4]; 72 | uint32_t triggerValue[4]; 73 | uint16_t triggerDelay[4]; 74 | uint32_t *startOfBuffer; 75 | uint32_t *endOfBuffer; 76 | uint32_t *endOfMemory; 77 | bool rleSelected = false; 78 | bool rleUsed = false; 79 | }; 80 | 81 | // data that changes while recording 82 | struct sumpDynamicVariableStruct { 83 | bool bufferHasWrapped; 84 | int interruptedIndex; // location where recording was halted early, or -1 if not halted. -2 = no data was recorded 85 | uint32_t triggerSampleIndex; 86 | }; 87 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_lc_48.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = TeensyLC48 6 | # A longer description of the device 7 | device.description = Teensy LC 48 MHz OLS 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 48000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 24000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 8000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 500000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 4096, 8192, 16384, 32768 24 | # Default capture size 25 | device.defaultcapturesize = 4096 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "TeensyLC48" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer.TEENSYLC.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer.TEENSYLC.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_lc_48_demo.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = DemoTeensyLC48 6 | # A longer description of the device 7 | device.description = Teensy LC 48 MHz OLS Demo 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 48000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 24000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 8000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 500000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 4096, 8192, 16384, 32768 24 | # Default capture size 25 | device.defaultcapturesize = 4096 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "DemoTeensyLC48" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzerDemo.TEENSYLC.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzerDemo.TEENSYLC.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_96.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy96 6 | # A longer description of the device 7 | device.description = Teensy 96 MHz OLS 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 96000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 48000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 12000000, 19200000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 2000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 237568, 475136 24 | # Default capture size 25 | device.defaultcapturesize = 59392 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "Teensy96" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_96.TEENSY31.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_96.TEENSY31.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_144.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy144 6 | # A longer description of the device 7 | device.description = Teensy 144 MHz OLS 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 144000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 72000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 15, 30, 75, 150, 300, 750, 1500, 3000, 7500, 15000, 30000, 75000, 150000, 300000, 750000, 1500000, 3000000, 18000000, 28800000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 3000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 237568, 475136 24 | # Default capture size 25 | device.defaultcapturesize = 59392 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "Teensy144", "DemoTeensy144" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_144.TEENSY31.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_144.TEENSY31.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_96_demo.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = DemoTeensy96 6 | # A longer description of the device 7 | device.description = Teensy 96 MHz OLS Demo 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 96000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 48000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 12000000, 19200000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 2000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 237568, 475136 24 | # Default capture size 25 | device.defaultcapturesize = 59392 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "DemoTeensy96" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_96Demo.TEENSY31.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_96Demo.TEENSY31.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy35_120.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy35_120 6 | # A longer description of the device 7 | device.description = Teensy 3.5 120 MHz OLS 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 120000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 60000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2500000, 15000000, 24000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 1000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 190464, 380928, 761856, 1523712 24 | # Default capture size 25 | device.defaultcapturesize = 190464 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "Teensy35_120" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_120.TEENSY35.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_120.TEENSY35.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy36_240.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy36_240 6 | # A longer description of the device 7 | device.description = Teensy 3.6 240 MHz OLS 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 240000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 60000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 30000000, 48000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 2000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 65536, 128000, 256000, 512000, 1024000, 2048000 24 | # Default capture size 25 | device.defaultcapturesize = 256000 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "Teensy36_240" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_240.TEENSY36.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_240.TEENSY36.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_lc_48_hardware.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = TeensyLC48Hardware 6 | # A longer description of the device 7 | device.description = Teensy LC 48 MHz OLS Hardware 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 48000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 24000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 18750, 37500, 75000, 150000, 250000, 500000, 1000000, 2000000, 4000000, 6000000, 12000000, 24000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 12000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 4096, 8192, 16384, 32768 24 | # Default capture size 25 | device.defaultcapturesize = 16384 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 1 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = false 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 2 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "HardwareTeensyLC48", "HW_TeensyLC48", "DemoHW_TeensyLC48" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_Hardware.TEENSYLC.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_Hardware.TEENSYLC.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy35_120_demo.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = DemoTeensy35_120 6 | # A longer description of the device 7 | device.description = Teensy 3.5 120 MHz OLS Demo 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 120000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 60000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2500000, 15000000, 24000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 1000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 190464, 380928, 761856, 1523712 24 | # Default capture size 25 | device.defaultcapturesize = 190464 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "DemoTeensy35_120" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_120Demo.TEENSY35.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_120Demo.TEENSY35.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy35_120_hardware.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy35_120Hardware 6 | # A longer description of the device 7 | device.description = Teensy 3.5 120 MHz OLS Hardware 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 120000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 60000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 56250, 112500, 225000, 450000, 900000, 1500000, 3000000, 10000000, 15000000, 30000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 3000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 190464, 380928, 761856, 1523712 24 | # Default capture size 25 | device.defaultcapturesize = 190464 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 1 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = false 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 2 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "HW_Teensy35_120", "DemoHW_Teensy35_120" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_120_Hardware.TEENSY35.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_120_Hardware.TEENSY35.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_144_hardware.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy144Hardware 6 | # A longer description of the device 7 | device.description = Teensy 144 MHz OLS Hardware 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 144000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 72000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 56250, 112500, 225000, 450000, 900000, 1500000, 300000, 12000000, 18000000, 36000000, 72000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 72000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 237568, 475136 24 | # Default capture size 25 | device.defaultcapturesize = 237568 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 1 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = false 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 2 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "HardwareTeensy144", "HW_Teensy144", "DemoHW_Teensy144" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_144_Hardware.TEENSY31.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_144_Hardware.TEENSY31.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy36_240_demo.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = DemoTeensy36_240 6 | # A longer description of the device 7 | device.description = Teensy 3.6 240 MHz OLS Demo 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 240000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 60000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 30000000, 48000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 2000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 65536, 128000, 256000, 512000, 1024000, 2048000 24 | # Default capture size 25 | device.defaultcapturesize = 256000 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "DemoTeensy36_240" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_240Demo.TEENSY36.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_240Demo.TEENSY36.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy40_600.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy40_600 6 | # A longer description of the device 7 | device.description = Teensy 4.0 600 MHz OLS 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 600000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 600000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 12500000, 25000000, 75000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 5000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 65536, 131072, 251904, 471040, 942080, 1884160, 3768320 24 | # Default capture size 25 | device.defaultcapturesize = 251904 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = true 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 4 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = true 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 8 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "Teensy40_600" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tycmd upload TeensyLogicAnalyzer_600.TEENSY40.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tycmd upload firmware/TeensyLogicAnalyzer_600.TEENSY40.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy36_240_hardware.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy36_240Hardware 6 | # A longer description of the device 7 | device.description = Teensy 3.6 240 MHz OLS Hardware 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 240000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 120000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 56250, 112500, 225000, 450000, 900000, 1500000, 300000, 12000000, 15000000, 20000000, 30000000, 60000000, 120000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 30000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 2048, 16384, 32768, 59392, 118784, 256000, 512000, 1024000, 2048000 24 | # Default capture size 25 | device.defaultcapturesize = 256000 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 1 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = false 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 2 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "HW_Teensy36_240", "DemoHW_Teensy36_240" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_240_Hardware.TEENSY36.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_240_Hardware.TEENSY36.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /LogicSniffer/ols.profile-teensy_96_hardware.cfg: -------------------------------------------------------------------------------- 1 | # Configuration for the Teensy profile 2 | # http://dangerousprototypes.com/docs/Logic_analyzer_mode 3 | 4 | # The short (single word) type of the device described in this profile 5 | device.type = Teensy96Hardware 6 | # A longer description of the device 7 | device.description = Teensy 96 MHz OLS Hardware 8 | # The device interface, SERIAL only 9 | device.interface = SERIAL 10 | # F_CPU - The device's native clockspeed, in Hertz. 11 | device.clockspeed = 96000000 12 | # F_BUS - The clockspeed used in the divider calculation, in Hertz. Defaults to 100MHz as most devices appear to use this. 13 | device.dividerClockspeed = 48000000 14 | # Whether or not double-data-rate is supported by the device (also known as the "demux"-mode). 15 | device.supports_ddr = false 16 | # Supported sample rates in Hertz, separated by comma's 17 | device.samplerates = 9375, 18750, 37500, 75000, 150000, 250000, 500000, 1000000, 2000000, 4000000, 6000000, 8000000, 12000000, 24000000, 48000000 18 | # Default sample rate in Hertz 19 | device.defaultsamplerate = 48000000 20 | # What capture clocks are supported 21 | device.captureclock = INTERNAL 22 | # The supported capture sizes, in bytes 23 | device.capturesizes = 128, 256, 512, 2048, 16384, 32768, 59392, 118784, 237568, 475136 24 | # Default capture size 25 | device.defaultcapturesize = 237568 26 | # Whether or not the noise filter is supported 27 | device.feature.noisefilter = false 28 | # Whether or not Run-Length encoding is supported 29 | device.feature.rle = false 30 | # Whether or not a testing mode is supported 31 | device.feature.testmode = false 32 | # Whether or not triggers are supported 33 | device.feature.triggers = true 34 | # The number of trigger stages 35 | device.trigger.stages = 1 36 | # Whether or not "complex" triggers are supported 37 | device.trigger.complex = false 38 | 39 | # The total number of channels usable for capturing 40 | device.channel.count = 2 41 | # The number of channels groups, together with the channel count determines the channels per group 42 | device.channel.groups = 1 43 | # Whether the capture size is limited by the enabled channel groups 44 | device.capturesize.bound = false 45 | # Whether the capture size is limited by the enabled channels (1,2,4,8) 46 | device.capturesize.channelbound = true 47 | # Which numbering does the device support 48 | device.channel.numberingschemes = DEFAULT 49 | 50 | # Is a delay after opening the port and device detection needed? (0 = no delay, >0 = delay in milliseconds) 51 | device.open.portdelay = 100 52 | # The receive timeout for the device (in milliseconds, 100 = default, <=0 = no timeout) 53 | device.receive.timeout = 1000 54 | # Does the device need a high or low DTR-line to operate correctly? (high = true, low = false) 55 | device.open.portdtr = false 56 | # Which metadata keys correspond to this device profile? Value is a comma-separated list of (double quoted) names... 57 | device.metadata.keys = "HardwareTeensy96", "HW_Teensy96", "DemoHW_Teensy96" 58 | 59 | # In which order are samples sent back from the device? false = last sample first, true = first sample first 60 | device.samples.reverseOrder = false 61 | 62 | # Commands to load firmware 63 | device.firmwareCommand.windows = cmd /c cd firmware & tyc upload TeensyLogicAnalyzer_96_Hardware.TEENSY31.hex 2>&1 64 | device.firmwareCommand.linux = firmware/tyc upload firmware/TeensyLogicAnalyzer_96_Hardware.TEENSY31.hex 2>&1 65 | ###EOF### 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TeensyLogicAnalyzer 2 | Logic Analyzer for the Teensy development boards (pjrc.com/teensy) 3 | 4 | (1/31/21) Released V4.2 - adds 8 channels for Teensy 4.x. Channels 0-7 are pins 14-21, in order 5 | 6 | Two modes: 7 | 8 | * Basic (default) - I just want to record up to 8 signals at up to about 1 MHz. Higher speeds (6 to 48 MHz) are available with some restrictions. 9 | * Hardware - I only need 1 or 2 channels, but I have a need for speed - LC is 24 MHz, 3.2 is 72 MHz, 3.5 is 60 MHz, 3.6 is 120 MHz. Most have full triggering capability. 10 | 11 | See users guide for capabilities and how to use. 12 | 13 | If you are using the Logic Analyzer and have a github account, please leave a comment at https://github.com/LAtimes2/TeensyLogicAnalyzer/issues/1 with your Teensy and OLS versions that you use. 14 | 15 | # New updated OLS GUI is available for Windows and Linux 16 | 17 | Now you can get started on Windows or Linux in 3 easy steps: 18 | 19 | 1a. Windows - Download OLS .zip (https://github.com/LAtimes2/ols/releases/latest), unzip, and run run.bat 20 | 21 | 1b. Linux - Download OLS .tar.gz (https://github.com/LAtimes2/ols/releases/latest), extract, and run run.sh 22 | 23 | 2. Select Capture -> Begin Capture, and set Analyzer port to COM port or /dev/tty for the Teensy 24 | 25 | 3. Select Device type for your Teensy type, then select Load Firmware 26 | 27 | When done, select Capture and you can start looking at data. For a demo on Teensy without external signals, select Teensy Demo when loading firmware and channels 4-8 will have PWM data on them (be sure not to connect anything to the Teensy since these channels are outputs in Demo mode). 28 | 29 | #### Version 4.2 30 | 31 | No changes for Teensy 3.x. 32 | Teensy 4.x has 8 channels - channels 0-7 are pins 14-21, in order. 33 | Teensy 4.x - full capability up to 20 MHz, simpler capability at 40 and 75 MHz. 34 | See User's Guide back pages for table of capabilities by sampling rate. 35 | 36 | #### Version 4.0 37 | 38 | New Run-Length Encoding (RLE) support. This only records data when it changes, so can record up to 100 times longer, depending on how often the data changes. To select it, enable Run Length Encoding on Acquisition tab. 39 | Added pre-built hex files for Teensy 3.0. 40 | 41 | #### Version 3.2 42 | 43 | Add support for Teensy 3.5, 3.6. Add new commands for sample sizes >256k. 44 | 45 | #### Version 3.1 46 | 47 | No new functions. Preparation for new OLS GUI. Better support for sigrok. Eliminated Advanced configuration - Basic now supports it all. 48 | 49 | #### Version 3.0 50 | 51 | Adds Complex Triggers (up to 4 stages). Allows edge triggers or things like 4th clock select pulse. For 3.x, increased maximum hardware speed to 72 MHz. 52 | 53 | #### Version 2.0 54 | 55 | Adds Hardware configurations. These use 2 SPI channels to read at speeds up to 24 MHz. Also allows full triggering capability at higher speeds. Hardware configuration uses different pins than Basic and Advanced. 56 | 57 | #### Version 1.1 58 | 59 | Adds Teensy LC Advanced configuration. 1 MHz, 8 MHz sample rates, up to 32k samples at 1 MHz. Also fixed a problem with 3.1 halt at 19.2 MHz. 60 | 61 | ### Version 1.0 62 | 63 | It now compiles under Arduino 1.6.7, adds Teensy LC 64 | 65 | This version supports Teensy 3.1/3.2 up to 24 MHz and Teensy LC up to 500 kHz. I don't have a Teensy 3.0 to test with, but it should also support it. If anyone can test it, let me know if it works. 66 | 67 | For demonstration purposes, set #define CREATE_TEST_FREQUENCIES 1 in the main file to see 25 kHz signals (varying duty cycles) on channels 4-7 (channels 4-5 on LC). 68 | 69 | User Guide is started. 70 | -------------------------------------------------------------------------------- /t4_overclock.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2021 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // This file contains functions for overclocking the Teensy 4. At speeds above 816 MHz, cooling is required. 26 | // Since no cooling is assumed to be used, it needs to limit how long it runs at these speeds. 27 | // To do so, it limits it to the shorter of: 28 | // 1. Time to record data (typically 10's of milliseconds if no trigger is used) 29 | // 2. Temperature interrupt if temperature rises 10 degrees C (or exceeds 70C, whichever is lower) 30 | // 3. Timer interrupt if time exceeds 7 seconds 31 | 32 | #include "InternalTemperature.h" 33 | 34 | extern "C" uint32_t set_arm_clock(uint32_t frequency); // required prototype 35 | 36 | const uint32_t OverclockSpeed = 960000000; 37 | const uint32_t OverclockTimeoutUsec = 7 * 1000000; 38 | const uint32_t OverclockMaxTemp = 70; 39 | const uint32_t OverclockMaxTempRise = 10; 40 | 41 | float maxMeasuredTemperature; 42 | bool temperatureExceeded = false; 43 | bool overclockTimerExpired = false; 44 | uint32_t originalClockSpeed; 45 | IntervalTimer overclockTimer; 46 | 47 | void startOverclocking_720MHz () 48 | { 49 | // this speed does not require cooling 50 | 51 | originalClockSpeed = F_CPU; 52 | 53 | // convert clock cycles between samples from original speed to new speed 54 | 55 | // round by adding half of original clock speed 56 | cpuClockCycles = cpuClockCycles * 720 + ((originalClockSpeed / 2) / 1000000); 57 | cpuClockCycles /= (originalClockSpeed / 1000000); 58 | 59 | set_arm_clock (720000000); 60 | } 61 | 62 | void startOverclocking_816MHz () 63 | { 64 | // this speed does not require cooling 65 | 66 | originalClockSpeed = F_CPU; 67 | 68 | // convert clock cycles between samples from original speed to new speed 69 | 70 | // round by adding half of original clock speed 71 | cpuClockCycles = cpuClockCycles * 816 + ((originalClockSpeed / 2) / 1000000); 72 | cpuClockCycles /= (originalClockSpeed / 1000000); 73 | 74 | set_arm_clock (816000000); 75 | } 76 | 77 | void startOverclocking_960MHz () 78 | { 79 | float temperature = InternalTemperature.readTemperatureC (); 80 | float maxTemp = temperature + OverclockMaxTempRise; 81 | 82 | maxMeasuredTemperature = 0.0; 83 | 84 | if (maxTemp > OverclockMaxTemp) 85 | { 86 | maxTemp = OverclockMaxTemp; 87 | } 88 | 89 | originalClockSpeed = F_CPU; 90 | 91 | overclockTimerExpired = false; 92 | temperatureExceeded = false; 93 | stopRecording = false; 94 | 95 | DEBUG_SERIAL(print (" Pre-temperature: ")); 96 | DEBUG_SERIAL(println (temperature)); 97 | delay (200); 98 | 99 | // if within 2 degrees of the max temperature, don't go into overclocking 100 | // (caused problems with interrupt going off while still transitioning the clock) 101 | if (temperature > (OverclockMaxTemp - 2)) 102 | { 103 | stopRecording = true; 104 | DEBUG_SERIAL(println ("Temperature too high to overclock, stopping!")); 105 | } 106 | else 107 | { 108 | overclockTimer.begin (overclockTimerInterrupt, OverclockTimeoutUsec); 109 | InternalTemperature.attachHighTempInterruptCelsius (maxTemp, temperatureInterrupt); 110 | 111 | set_arm_clock (OverclockSpeed); 112 | } 113 | //DEBUG_SERIAL(print("F_CPU oc: ")); 114 | //DEBUG_SERIAL(println(F_CPU)); 115 | //DEBUG_SERIAL(print("F_BUS oc: ")); 116 | //DEBUG_SERIAL(println(F_BUS_ACTUAL)); 117 | } 118 | 119 | void stopOverclocking (bool fromISR) 120 | { 121 | maxMeasuredTemperature = InternalTemperature.readTemperatureC (); 122 | 123 | set_arm_clock (originalClockSpeed); 124 | 125 | overclockTimer.end (); 126 | InternalTemperature.detachHighTempInterrupt (); 127 | 128 | digitalWriteFast(LED_PIN, LOW); 129 | // don't print from within an ISR 130 | if (!fromISR) 131 | { 132 | DEBUG_SERIAL(print (" Max temperature: ")); 133 | DEBUG_SERIAL(println (maxMeasuredTemperature)); 134 | 135 | if (temperatureExceeded) 136 | { 137 | DEBUG_SERIAL(println ("Temperature interrupt triggered")); 138 | } 139 | if (overclockTimerExpired) 140 | { 141 | DEBUG_SERIAL(println ("Overclock timer interrupt triggered")); 142 | } 143 | } 144 | } 145 | 146 | void temperatureInterrupt () 147 | { 148 | stopOverclocking (true); 149 | temperatureExceeded = true; 150 | stopRecording = true; 151 | } 152 | 153 | void overclockTimerInterrupt () 154 | { 155 | stopOverclocking (true); 156 | overclockTimerExpired = true; 157 | stopRecording = true; 158 | } 159 | -------------------------------------------------------------------------------- /record_low_speed_data.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2021 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | void recordLowSpeedData (sumpSetupVariableStruct &sv, 26 | sumpDynamicVariableStruct &dynamic) 27 | { 28 | bool bufferHasWrapped = false; 29 | int elementsToRecord = sv.samplesToRecord / sv.samplesPerElement; 30 | register uint32_t *inputPtr = (uint32_t *)sv.startOfBuffer; 31 | uint32_t *endOfBuffer = (uint32_t *)sv.endOfBuffer; 32 | uint32_t *startOfBuffer = (uint32_t *)sv.startOfBuffer; 33 | uint32_t *startPtr = (uint32_t *)sv.startOfBuffer; 34 | uint32_t sample; 35 | byte samplesPerElement = sv.samplesPerElement; 36 | byte samplesPerElementMinusOne = samplesPerElement - 1; 37 | register uint32_t sampleMask = sv.sampleMask; 38 | register uint32_t sampleShift = sv.sampleShift; 39 | int triggerCount = samplesPerElementMinusOne; 40 | register int workingCount = samplesPerElementMinusOne + 1; 41 | register uint32_t workingValue = 0; 42 | 43 | register stateType state = Buffering; 44 | int currentTriggerLevel = 0; 45 | register uint32_t triggerMask = sv.triggerMask[0]; 46 | register uint32_t triggerValue = sv.triggerValue[0]; 47 | uint32_t triggerDelay = sv.triggerDelay[0]; 48 | 49 | // if using a trigger 50 | if (sv.triggerMask[0]) 51 | { 52 | state = Buffering; 53 | 54 | // position to arm the trigger 55 | startPtr = inputPtr + sv.delaySizeInElements; 56 | } 57 | else 58 | { 59 | state = Triggered_Second_Pass; 60 | 61 | startPtr = endOfBuffer; 62 | } 63 | 64 | // 100% causes a problem with circular buffer - never stops 65 | if (startPtr >= endOfBuffer) 66 | { 67 | startPtr = endOfBuffer - 1; 68 | } 69 | 70 | maskInterrupts (); 71 | 72 | // read enough samples prior to arming to meet the pre-trigger request 73 | // (for speed, use while (1) and break instead of while (inputPtr != startPtr)) 74 | while (1) 75 | { 76 | waitForTimeout (); 77 | 78 | sample = readSample (); 79 | 80 | // save a sample 81 | workingValue = (workingValue << sampleShift) + (sample & sampleMask); 82 | --workingCount; 83 | 84 | if (workingCount == 0) 85 | { 86 | *(inputPtr) = workingValue; 87 | ++inputPtr; 88 | 89 | // adjust for circular buffer wraparound at the end 90 | if (inputPtr >= endOfBuffer) 91 | { 92 | #ifdef TIMING_DISCRETES 93 | digitalWriteFast (TIMING_PIN_5, HIGH); 94 | #endif 95 | 96 | inputPtr = startOfBuffer; 97 | 98 | bufferHasWrapped = true; 99 | 100 | // if any data is received from PC, then stop (assume it is a reset) 101 | if (usbInterruptPending ()) 102 | { 103 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 104 | set_led_off (); 105 | SUMPreset(); 106 | break; 107 | } 108 | 109 | #ifdef TIMING_DISCRETES 110 | digitalWriteFast (TIMING_PIN_5, HIGH); 111 | #endif 112 | } 113 | 114 | workingCount = samplesPerElement; 115 | } // if workingCount == 0 116 | 117 | // this state cannot afford enough time for the switch statement 118 | if (state == LookingForTrigger) 119 | { 120 | // if trigger has occurred 121 | if ((workingValue & triggerMask) == triggerValue) { 122 | 123 | if (triggerDelay > 0) { 124 | state = TriggerDelay; 125 | } else { 126 | // if last trigger level 127 | if (currentTriggerLevel >= sv.lastTriggerLevel) { 128 | 129 | triggerCount = workingCount; 130 | 131 | // last location to save 132 | startPtr = inputPtr - sv.delaySizeInElements; 133 | 134 | // move to triggered state 135 | state = Triggered_First_Pass; 136 | 137 | #ifdef TIMING_DISCRETES 138 | digitalWriteFast (TIMING_PIN_1, LOW); 139 | #endif 140 | 141 | } else { 142 | 143 | #ifdef TIMING_DISCRETES 144 | digitalWriteFast (TIMING_PIN_1, LOW); 145 | #endif 146 | 147 | // advance to next trigger level 148 | ++currentTriggerLevel; 149 | triggerMask = sv.triggerMask[currentTriggerLevel]; 150 | triggerValue = sv.triggerValue[currentTriggerLevel]; 151 | 152 | #ifdef TIMING_DISCRETES 153 | digitalWriteFast (TIMING_PIN_1, HIGH); 154 | #endif 155 | } 156 | } 157 | } 158 | } else { 159 | 160 | switch (state) { 161 | case LookingForTrigger : 162 | // already done above 163 | break; 164 | 165 | case LookingForSimpleTrigger : 166 | // not used at low speeds 167 | break; 168 | 169 | case TriggerDelay : 170 | --triggerDelay; 171 | if (triggerDelay == 0) { 172 | // if last trigger level 173 | if (currentTriggerLevel >= sv.lastTriggerLevel) { 174 | triggerCount = workingCount; 175 | 176 | // last location to save 177 | startPtr = inputPtr - sv.delaySizeInElements; 178 | 179 | // move to triggered state 180 | state = Triggered_First_Pass; 181 | 182 | } else { 183 | ++currentTriggerLevel; 184 | triggerMask = sv.triggerMask[currentTriggerLevel]; 185 | triggerValue = sv.triggerValue[currentTriggerLevel]; 186 | triggerDelay = sv.triggerDelay[currentTriggerLevel]; 187 | state = LookingForTrigger; 188 | } 189 | } 190 | break; 191 | 192 | case Triggered: 193 | if (inputPtr == startPtr) { 194 | // done recording 195 | goto DoneRecording; 196 | } 197 | break; 198 | 199 | case Buffering: 200 | // if enough data is buffered 201 | if (inputPtr >= startPtr) 202 | { 203 | // move to armed state 204 | state = LookingForTrigger; 205 | set_led_on (); 206 | 207 | #ifdef TIMING_DISCRETES 208 | digitalWriteFast (TIMING_PIN_1, HIGH); 209 | #endif 210 | } 211 | break; 212 | 213 | case Triggered_Second_Pass: 214 | // adjust for circular buffer wraparound at the end. 215 | if (startPtr < startOfBuffer) 216 | { 217 | startPtr = startPtr + elementsToRecord; 218 | } 219 | 220 | // move to triggered state 221 | state = Triggered; 222 | 223 | #ifdef TIMING_DISCRETES 224 | digitalWriteFast (TIMING_PIN_1, LOW); 225 | #endif 226 | break; 227 | 228 | case Triggered_First_Pass: 229 | // go as fast as possible to try to catch up from Triggered state 230 | state = Triggered_Second_Pass; 231 | set_led_off (); // TRIGGERED, turn off LED 232 | break; 233 | } 234 | } // if state == LookingForTrigger 235 | 236 | } // while (1) 237 | 238 | DoneRecording: 239 | 240 | // cleanup 241 | unmaskInterrupts (); 242 | 243 | #ifdef TIMING_DISCRETES 244 | digitalWriteFast (TIMING_PIN_0, LOW); 245 | #endif 246 | 247 | // adjust trigger count 248 | dynamic.triggerSampleIndex = (startPtr + sv.delaySizeInElements - startOfBuffer) * samplesPerElement + samplesPerElementMinusOne - triggerCount; 249 | 250 | dynamic.bufferHasWrapped = bufferHasWrapped; 251 | 252 | // adjust for circular buffer wraparound at the end. 253 | if (dynamic.triggerSampleIndex >= (uint32_t)sv.samplesToRecord) 254 | { 255 | dynamic.triggerSampleIndex = dynamic.triggerSampleIndex - sv.samplesToRecord; 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /Test/Test_Cases.txt: -------------------------------------------------------------------------------- 1 | Pattern Generator is in GitHub next to the Logic Analyzer: 2 | 3 | Set 1: 4 | Chan(pin) PG Pin - signal 5 | 0 (2) 29 - low-going pulse every 10 seconds 6 | 1 (14) 5 - 1000 Hz, 50 % duty cycle 7 | 2 (7) 4 - user specified, 50 % duty cycle 8 | 3 (8) 6 - user specified/2, 50 % duty cycle 9 | 4 (6) 9 - user specified/2, 12.5 % duty cycle 10 | 5 (20) 10 - user specified/2, 25 % duty cycle 11 | 6 (21) 20 - user specified/2, 37.5 % duty cycle 12 | 7 (5) 23 - user specified/2, 87.5 % duty cycle 13 | 14 | Set 2: 15 | Chan(pin) PG Pin - signal 16 | 0 (2) 32 - Uart "?1fU" at 115200 baud 17 | 1 (14) 30 - PWM 0.1 Hz (10 second period), 1% duty cycle (high-going pulse every 10 seconds) 18 | 2 (7) 25 - software pattern, 4 pulses, each one wider 19 | 3 (8) 33 - Uart "?1fU" at 1,843,200 baud 20 | 4 (6) 1 - Uart "?1fU" at 9600 baud 21 | 5 (20) 16 - PWM 5954 Hz, 50% duty cycle (3.6 only) 22 | 6 (21) 7 - PWM 62,500 Hz, 49% duty cycle 23 | 7 (5) 8 - PWM 62,500 Hz, 50% duty cycle 24 | 25 | 26 | Basic: 27 | 28 | 1. Demo Mode 29 | 1.1 Open OLS GUI 30 | 1.2 Load Firmware 31 | 1.3 Show device metadata 32 | 33 | 2. Normal mode 34 | 2.1 Open OLS GUI 35 | 2.2 Load Firmware 36 | 2.3 Show device metadata 37 | 38 | 2.4 Set 1 of pattern generator 39 | 40 | Foreach of 1st - 4th freqs: 41 | 1. Set pattern generator to 1/4th of freq 42 | 2. Record at default buffer size 43 | 3. Verify 44 | 2.4.1 Set pattern generator user frequency to 5th highest freq 45 | 2.4.2 Record at 3rd highest (highest non-asm) 46 | 2.4.2.1 Verify ch 1 is 1 KHz 47 | 2.4.2.2 Verify ch 2,3,4,5,6,7 are the same pattern throughout 48 | 2.4.3 Record at 4th highest 49 | 2.4.3.1 Verify ch 3,5 are the same pattern throughout 50 | 2.4.3.2 Verify ch 4 is mostly low, ch 7 is mostly high 51 | 2.4.1 Set pattern generator user frequency to 3rd highest freq 52 | 2.4.2 Record at 3rd highest (highest non-asm) 53 | 2.4.2.2 Verify ch 3,4,5,6,7 are the same pattern throughout 54 | 55 | Trigger 56 | 57 | 1,2,4 channels 58 | 59 | RLE 60 | 61 | 62 | 63 | 2.5 Set 2 of pattern generator 64 | 2.5.1 Record at default MHz, no trigger 65 | 2.5.1.1 Verify no unusual data at beginning or end 66 | 2.5.1.2 Verify ch 1 shortest pulses are about 8.68 usec 67 | 68 | 2.5.x Complex trigger: ch 1 high, ch 5 high, ch 0 high, delay 32, ch 7 high. 69 | 70 | 3.1: 71 | 72 | compile with 73 | all defines 0 except CREATE_TEST_FREQUENCIES 74 | no TIMING_DISCRETES 75 | Teensy 96 MHz optimized. 76 | 77 | LC pin 3 set to freq of 62500 78 | connect LC pin 3 to 3.1 pins 2 (ch0) and 8 (ch3). 79 | 80 | OLS: select Device type Teensy 96 MHz OLS 81 | Show device metadata: Teensy96, Firmware 2.0 82 | 83 | 2 MHz, 58kB, no trigger - 84 | Measure ch 0,3 as 62500. 85 | Measure ch 4 as 25000, 25% duty cycle. 86 | Measure ch 5 as 25000, 48.7% 87 | Measure ch 6 as 25000, 50% 88 | Measure ch 7 as 25000, 75% 89 | Look at beginning, end of trace - no garbage. 90 | 91 | 2 MHz, 2kB - same 92 | 1 MHz, 58kB - same 93 | 100kHz, 32kB - similar. Aliasing on ch 0,3 94 | 95 | Trigger: 96 | 2 MHz, 58 kB, 1%, ch 7 low - verify trigger is correct. start, end - no garbage. 97 | 99%, ch 5 low, ch 6 high - verify trigger. 98 | ch 4 high, ch 7 low - never triggers. verify LED is on. verify can halt properly. 99 | Complex - stage 1 ch 5 high, stage 2 ch 4 low - verify trigger is on falling edge ch 4 100 | Complex - stage 1 ch 5 low, stage 2 ch 5 high delay 31, stage 3 ch 3 low - 101 | verify trigger is on second falling edge ch 3 after ch 5 high 102 | 103 | LC: 104 | 105 | compile with 106 | all defines 0 except CREATE_TEST_FREQUENCIES 107 | no TIMING_DISCRETES 108 | Teensy LC 48 MHz optimized. 109 | 110 | 3.1 pin 3 set to freq of 62500 111 | connect 3.1 pin 3 to LC pins 2 (ch0) and 8 (ch3). 112 | 113 | OLS: select Device type Teensy LC 48 MHz OLS 114 | Show device metadata: TeensyLC48, Firmware 2.0 115 | 116 | 500 kHz, 4kB, no trigger - 117 | Measure ch 0,3 as 62500. 118 | Measure ch 4 as 25000, 25% duty cycle. 119 | Measure ch 5 as 25000, 45% 120 | Channels 6 and 7 are not connected - may have noise 121 | Look at beginning, end of trace - no garbage. 122 | 123 | 200 kHz, 2kB - similar. Aliasing on ch 0,3 124 | 125 | Trigger: 126 | 500 kHz, 4 kB, 1%, ch 4 high - verify trigger is correct. start, end - no garbage. 127 | ch 4 high, ch 5 low - never triggers. verify LED is on. verify can halt properly. 128 | 129 | 130 | 131 | Advanced: 132 | 133 | 3.1: 134 | 135 | compile with 136 | all defines 0 except ADVANCED_CONFIGURATION, CREATE_TEST_FREQUENCIES 137 | no TIMING_DISCRETES 138 | Teensy 96 MHz optimized. 139 | 140 | LC pin 3 set to freq of 62500 141 | connect LC pin 3 to 3.1 pins 2 (ch0) and 8 (ch3). 142 | 143 | OLS: select Device type Teensy 96 MHz OLS Advanced 144 | Show device metadata: AdvancedTeensy96, Firmware 2.0 145 | 146 | 19.2 MHz, 58kB, no trigger - 147 | Measure ch 0,3 as 62500 (approx - 62.5 is not an even multiple of 19.2). 148 | Measure ch 4 as 25000, 25% duty cycle. 149 | Measure ch 5 as 25000, 48.5% 150 | Measure ch 6 as 25000, 50% 151 | Measure ch 7 as 25000, 75% 152 | Look at beginning, end of trace - no garbage. 153 | 154 | 12 MHz, 2kB - same 155 | 156 | 19.2, 464 - all 8 channels, only 1/8th of data (3 msec) 157 | 2, 464 - only 1 channel has data 158 | 2, 232 - only 2 channels have data 159 | 2, 116 - only 4 channels have data 160 | 161 | Trigger: 162 | 19.2 MHz, 58 kB, 0% (always at beginning), ch 7 low - verify trigger is correct. start, end - no garbage. 163 | 12 MHz, 58 kB, 10%, ch 5 low, ch 6 high - verify trigger. 164 | 19.2 and 12 MHz, ch 4 high, ch 7 low - never triggers. verify LED is on. verify can halt properly. 165 | 166 | 167 | LC: 168 | 169 | compile with 170 | all defines 0 except ADVANCED_CONFIGURATION, CREATE_TEST_FREQUENCIES 171 | no TIMING_DISCRETES 172 | Teensy LC 48 MHz optimized. 173 | 174 | 3.1 pin 3 set to freq of 62500 175 | connect 3.1 pin 3 to LC pins 2 (ch0) and 8 (ch3). 176 | 177 | OLS: select Device type Teensy LC 48 MHz OLS Advanced 178 | Show device metadata: AdvancedTeensyLC48, Firmware 3.0 179 | 180 | 8 MHz, 4kB, no trigger - 181 | Measure ch 0,3 as 62500. 182 | Measure ch 4 as 25000, 25% duty cycle. 183 | Measure ch 5 as 25000, 48.3% 184 | Channels 6 and 7 are not connected - may have noise 185 | Look at beginning, end of trace - no garbage. 186 | 187 | 1 MHz, 2kB - similar. Aliasing on ch 0,3 188 | 189 | 8, 32 - all 8 channels, only 1/8th of data (0.5 msec) 190 | 1, 32 - only 1 channel has data 191 | 1, 16 - only 2 channels have data 192 | 1, 8 - only 4 channels have data 193 | 194 | Trigger: 195 | 8 MHz, 4 kB, 0% (always at beginning), ch 4 high - verify trigger is correct. start, end - no garbage. 196 | 1 MHz, 4 kB, 10%, ch 4 low, ch 5 high - verify trigger. 197 | 8 and 1 MHz, ch 4 high, ch 5 low - never triggers. verify LED is on. verify can halt properly. 198 | 199 | 200 | Hardware: 201 | 202 | Set 1: 203 | Chan(pin) PG Pin - signal 204 | 0 (1) 3 - user specified, 50 % duty cycle 205 | 1 (8) 6 - user specified/2, 50 % duty cycle 206 | 207 | Set 1: 208 | Chan(pin) PG Pin - signal 209 | 0 (1) 30 - PWM 0.1 Hz (10 second period), 1% duty cycle 210 | 1 (8) 33 - Uart "?1fU" at 1,843,200 baud (543 nsec/bit) 211 | 212 | 3.1: 213 | 214 | compile with 215 | all defines 0 except HARDWARE_CONFIGURATION 216 | no TIMING_DISCRETES 217 | Teensy 96 MHz optimized. 218 | 219 | LC pin 3 set to freq of 62500 220 | connect LC pin 3 to 3.1 pins 1 (ch0) and 8 (ch1). 221 | 222 | OLS: select Device type Teensy 96 MHz OLS Hardware 223 | Show device metadata: HardwareTeensy96, Firmware 3.0 224 | 225 | 24 MHz, 232kB, no trigger - 226 | Verify 2 channels displayed 227 | Measure ch 0,1 as 62500 228 | Verify both channels go high and low at the same clock pulse (zoom in) 229 | Look at beginning, end of trace - no garbage. 230 | 231 | 48 MHz, 232kB - same [Note: freq is 125000 until F_BUS issue is tackled] 232 | 12 MHz, 58kB - same 233 | 8 MHz, 2kB - same 234 | 235 | 24 MHz, 464 kB - 236 | Verify 1 channel has data 237 | Measure ch 0 as 62500 238 | 239 | Trigger: 240 | 24 MHz, 232 kB, 10%, ch 0 high - verify trigger is correct. start, end - no garbage. 241 | 12 MHz, 232 kB, 10%, ch 1 low - verify trigger. 242 | 8 MHz, 464 kB, 10%, ch 0 low - verify trigger. [Note: won't work until a client change for large sample sizes] 243 | 244 | connect serial TX (921600 badu) to 3.1 pin 8 (ch1), LC pin 3 to 3.1 pin 1 (ch0). 245 | 246 | Trigger: 247 | 48 MHz, 232 kB, 90%, ch 1 low - Type 5 on serial terminal, verify trigger is correct. first bit is 1.08 usec. start, end - no garbage. 248 | 249 | 250 | compile at 120 MHz optimized 251 | 252 | LC pin 3 set to freq of 62500 253 | connect LC pin 3 to 3.1 pins 1 (ch0) and 8 (ch1). 254 | 255 | OLS: select Device type Teensy 120 MHz OLS Hardware 256 | Show device metadata: HardwareTeensy120, Firmware 3.0 257 | 258 | 30 MHz, 232kB, no trigger - 259 | 60 MHz, 232kB, no trigger - 260 | Verify 2 channels displayed 261 | Measure ch 0,1 as 62500 [Note: freq is 125000 until F_BUS issue is tackled] 262 | Verify both channels go high and low at the same clock pulse (zoom in) 263 | Look at beginning, end of trace - no garbage. 264 | 265 | compile at 144 MHz optimized 266 | 267 | OLS: select Device type Teensy 144 MHz OLS Hardware 268 | Show device metadata: HardwareTeensy144, Firmware 3.0 269 | 270 | 36 MHz, 232kB, no trigger - 271 | Verify 2 channels displayed 272 | Measure ch 0,1 as 62500 273 | Verify both channels go high and low at the same clock pulse (zoom in) 274 | Look at beginning, end of trace - no garbage. 275 | 72 MHz, 232kB, no trigger - 276 | Verify 2 channels displayed 277 | Measure ch 0,1 as 62500 [Note: freq is 187500 until F_BUS issue is tackled] 278 | Verify both channels go high and low at the same clock pulse (zoom in) 279 | Look at beginning, end of trace - no garbage. 280 | 281 | 282 | LC: 283 | 284 | compile with 285 | all defines 0 except HARDWARE_CONFIGURATION 286 | no TIMING_DISCRETES 287 | Teensy LC 48 MHz optimized. 288 | 289 | 3.1 pin 3 set to freq of 62500 290 | connect 3.1 pin 3 to LC pins 1 (ch0) and 8 (ch1). 291 | 292 | OLS: select Device type Teensy LC 48 MHz OLS Hardware 293 | Show device metadata: HardwareTeensyLC48, Firmware 2.0 294 | 295 | 12 MHz, 16kB, no trigger - 296 | Verify 2 channels displayed 297 | Measure ch 0,1 as 62500 298 | Verify both channels go high and low at the same clock pulse (zoom in) 299 | Look at beginning, end of trace - no garbage. 300 | 301 | 6 MHz, 8kB - same 302 | 4 MHz, 2kB - same 303 | 304 | 24 MHz, 32 kB - 305 | Verify 1 channel has data 306 | Measure ch 0 as 62500 307 | 308 | Trigger: 309 | 24 MHz, 32 kB, 0% (always at beginning), ch 0 high - verify trigger is correct. start, end - no garbage. 310 | 12 MHz, 32 kB, 10%, ch 0 low - verify trigger. 311 | 12 MHz, 16 kB, 0% (always at beginning), ch 1 low - verify trigger 312 | 6 MHz, 16 kB, 10%, ch 1 low - verify trigger 313 | -------------------------------------------------------------------------------- /send_data.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2021 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // 26 | // These functions are used to send the data back to the PC after recording 27 | // 28 | 29 | // Definitions: 30 | // sample - one sample of data. May be 1 bit, 2 bits, 4 bits, or 8 bits, 31 | // as defined by sv.samplesPerElement, sampleMask, and sampleShift 32 | // element - collection of samples stored as one element. Currently 32 bit integer. 33 | // Oldest sample is in msb of element, newest sample is lsb 34 | // data - all the data that was recorded. An array of elements. 35 | // arrayIndex - index of elements in data (0-based) 36 | // elementIndex - index into an element. 0 is newest sample. 37 | // sampleIndex - index of samples in data. 0 is first address. 0 is not 38 | // necessarily the oldest sample, since the buffer can wrap 39 | // while waiting for a trigger. 40 | 41 | byte getSample (sumpSetupVariableStruct setup, int sampleIndex) 42 | { 43 | uint32_t sample; 44 | 45 | int arrayIndex = sampleIndex / setup.samplesPerElement; 46 | int elementIndex = setup.samplesPerElement - (sampleIndex % setup.samplesPerElement) - 1; 47 | 48 | sample = setup.startOfBuffer[arrayIndex]; 49 | 50 | if (setup.rawPortRead) 51 | { 52 | // Fastest Asm stores data as uint32_t, need to shift bits down to bottom 8 53 | sample = shiftSample (sample); 54 | } 55 | 56 | sample = (sample >> (setup.sampleShift * elementIndex)) & setup.sampleMask; 57 | 58 | // if RLE is selected but it wasn't used, clear top channel 59 | if (setup.rleSelected && !setup.rleUsed) 60 | { 61 | // clear rleCountIndicator for this number of channels 62 | sample = sample & ~setup.rleCountIndicator; 63 | } 64 | 65 | if (setup.swapChannels) 66 | { 67 | // if not an rle count value 68 | if (!setup.rleUsed || !(sample & setup.rleCountIndicator)) 69 | { 70 | sample = swapPhysicalChannels (setup, sample); 71 | } 72 | } 73 | 74 | return sample; 75 | } 76 | 77 | // converts a physical sample to logical channel ordering 78 | uint32_t swapPhysicalChannels (sumpSetupVariableStruct setup, uint32_t physicalSample) 79 | { 80 | uint32_t logicalSample = 0; 81 | uint32_t value; 82 | 83 | for (int index = 0; index < setup.numberOfChannels; index++) 84 | { 85 | // get physical value 86 | value = (physicalSample >> setup.logicalToPhysicalChannel[index]) & 0x01; 87 | 88 | // shift to logical location 89 | value = value << index; 90 | 91 | // save value 92 | logicalSample += value; 93 | } 94 | 95 | return logicalSample; 96 | } 97 | 98 | // converts a logical sample to physical channel ordering 99 | uint32_t swapLogicalChannels (sumpSetupVariableStruct setup, uint32_t logicalSample) 100 | { 101 | uint32_t physicalSample = 0; 102 | uint32_t value; 103 | 104 | if (setup.swapChannels) 105 | { 106 | for (int index = 0; index < setup.numberOfChannels; index++) 107 | { 108 | // get logical value 109 | value = (logicalSample >> index) & 0x01; 110 | 111 | // shift to physical location 112 | value = (value << setup.logicalToPhysicalChannel[index]); 113 | 114 | // save value 115 | physicalSample += value; 116 | } 117 | } 118 | else 119 | { 120 | // no swap 121 | physicalSample = logicalSample; 122 | } 123 | 124 | return physicalSample; 125 | } 126 | 127 | bool sampleIsRleCount (sumpSetupVariableStruct setup, int sampleIndex) 128 | { 129 | bool returnValue = false; 130 | uint32_t sample; 131 | 132 | sample = getSample (setup, sampleIndex); 133 | 134 | if ((sample & setup.rleCountIndicator) == setup.rleCountIndicator) 135 | { 136 | returnValue = true; 137 | } 138 | 139 | return (returnValue); 140 | } 141 | 142 | // adjustTrigger 143 | // 144 | // Certain of the fastest modes don't have time to set the trigger precisely. This function 145 | // goes back and looks at the data around the nominal trigger point to find the actual 146 | // trigger location and sets it accordingly. 147 | // 148 | void adjustTrigger ( 149 | sumpSetupVariableStruct setup, 150 | sumpDynamicVariableStruct &dynamic) 151 | { 152 | 153 | #if HARDWARE_CONFIGURATION 154 | // look from 14 before to 16 after the current trigger index for actual trigger change 155 | // (hardware checks 32 bits at a time) 156 | const int startIndex = -14; 157 | const int endIndex = 16; 158 | #else 159 | // assembly checks every 8 samples 160 | const int startIndex = -7; 161 | const int endIndex = 0; 162 | #endif 163 | 164 | int currentStartIndex = startIndex; // can get adjusted at each stage 165 | uint32_t triggerSampleIndex = 0xFFFFFFFF; // set to large invalid value 166 | bool continue_checks = true; 167 | bool skip_sample = false; 168 | 169 | // for each trigger level 170 | for (int level = 0; level <= setup.lastTriggerLevel; ++level) { 171 | 172 | for (int index = currentStartIndex; index <= endIndex; ++index) { 173 | 174 | skip_sample = false; 175 | 176 | // since unsigned, check for wraparound to a very large number (i.e. negative) 177 | if (dynamic.triggerSampleIndex + index > 0xF0000000) { 178 | continue_checks = false; 179 | } 180 | 181 | // if an RLE count, don't use it 182 | if (setup.rleUsed && (sampleIsRleCount (setup, dynamic.triggerSampleIndex + index))) { 183 | skip_sample = true; 184 | } 185 | 186 | if (continue_checks && !skip_sample) { 187 | // if it is the trigger 188 | if ((getSample (setup, dynamic.triggerSampleIndex + index) & setup.triggerMask[level]) == setup.triggerValue[level]) 189 | { 190 | if (level == setup.lastTriggerLevel) { 191 | triggerSampleIndex = dynamic.triggerSampleIndex + index; 192 | 193 | // add trigger delay if not at start of search area (may have been true earlier) 194 | if (index != startIndex && !setup.rleUsed) { 195 | // add any delay after the trigger 196 | triggerSampleIndex += setup.triggerDelay[level]; 197 | } 198 | 199 | // don't know why it needs this, but get it to align 200 | if (setup.numberOfChannels == 2) { 201 | dynamic.triggerSampleIndex -= 1; 202 | } 203 | } else { 204 | // not last level - continue with next stage 205 | currentStartIndex = index; 206 | 207 | // add trigger delay if not at start of search area (may have been true earlier) 208 | if (index != startIndex && !setup.rleUsed) { 209 | currentStartIndex += setup.triggerDelay[level]; 210 | 211 | // if delay goes past end of search area, start searching at beginning again 212 | // (may have been a false trigger at this level, it was really before search area 213 | // due to large delay) 214 | if (currentStartIndex > endIndex) { 215 | currentStartIndex = startIndex; 216 | } 217 | } 218 | } 219 | break; 220 | } // if trigger 221 | } // if continue_checks 222 | 223 | } // for index ... 224 | 225 | } // for level ... 226 | 227 | // do not go outside of search area 228 | if (triggerSampleIndex <= (dynamic.triggerSampleIndex + endIndex) && 229 | (triggerSampleIndex >= 0)) 230 | { 231 | dynamic.triggerSampleIndex = triggerSampleIndex; 232 | } 233 | } 234 | 235 | void sendData ( 236 | sumpSetupVariableStruct sumpSetup, 237 | sumpDynamicVariableStruct dynamic) 238 | { 239 | const byte RLE_MASK = 0x80; 240 | 241 | int firstSampleIndex; 242 | uint32_t lastSampleIndex = 0; 243 | uint32_t triggerSampleIndex; 244 | uint32_t firstRLEValue = 0; 245 | int missingSampleCount = 0; 246 | byte sample; 247 | int samplesSentCount = 0; 248 | int samplesToSend = sumpSetup.samplesToSend; 249 | bool wrappedBuffer = false; 250 | 251 | // if using trigger 252 | if (sumpSetup.triggerMask[0]) 253 | { 254 | adjustTrigger (sumpSetup, dynamic); 255 | } 256 | 257 | triggerSampleIndex = dynamic.triggerSampleIndex; 258 | 259 | // set unused channels to alternating 1's and 0's 260 | byte unusedValue = 0x55; 261 | 262 | // get first sampleIndex to send 263 | firstSampleIndex = triggerSampleIndex - sumpSetup.delaySamples; 264 | 265 | if (firstSampleIndex < 0) 266 | { 267 | firstSampleIndex += sumpSetup.samplesToRecord; 268 | } 269 | 270 | // if using trigger with RLE 271 | if (sumpSetup.triggerMask[0] && sumpSetup.rleUsed) 272 | { 273 | // compute the first sample value in case first sample was an RLE count 274 | for (int32_t elementIndex = sumpSetup.samplesPerElement - 1; elementIndex >= 0; --elementIndex) 275 | { 276 | sample = (sumpSetup.firstRLEValue >> (sumpSetup.sampleShift * elementIndex)) & sumpSetup.sampleMask; 277 | 278 | // if it is not an RLE count 279 | if ((sample & sumpSetup.rleCountIndicator) == 0) 280 | { 281 | // save it as the first value to insert in the data stream 282 | firstRLEValue = sample; 283 | } 284 | } 285 | } 286 | 287 | // if halted before recording all the data 288 | if (dynamic.interruptedIndex >= 0) 289 | { 290 | // get last sample index to send if it hadn't been interrupted 291 | lastSampleIndex = firstSampleIndex + samplesToSend - 1; 292 | 293 | missingSampleCount = lastSampleIndex - dynamic.interruptedIndex; 294 | 295 | // if buffer wrapped around 296 | if (missingSampleCount < 0) 297 | { 298 | missingSampleCount += sumpSetup.samplesToRecord; 299 | } 300 | if ((uint32_t)missingSampleCount >= sumpSetup.samplesToRecord) 301 | { 302 | missingSampleCount -= sumpSetup.samplesToRecord; 303 | } 304 | 305 | // adjust samples available to be sent 306 | samplesToSend -= missingSampleCount; 307 | } 308 | 309 | // special case of no data recorded 310 | if (dynamic.interruptedIndex == -2) 311 | { 312 | samplesToSend = 0; 313 | } 314 | 315 | // get last sampleIndex to send 316 | lastSampleIndex = firstSampleIndex + samplesToSend - 1; 317 | 318 | // if buffer wrapped around 319 | if (lastSampleIndex >= sumpSetup.samplesToRecord) 320 | { 321 | wrappedBuffer = true; 322 | lastSampleIndex = lastSampleIndex - sumpSetup.samplesToRecord; 323 | } 324 | 325 | 326 | // if samples were limited, send bogus data to indicate it is done. 327 | // Send these first since sent backwards in time 328 | for (uint32_t index = samplesToSend; index < sumpSetup.samplesRequested; index = index + 2) 329 | { 330 | // send alternating 1's and 0's 331 | Serial.write(0x55); 332 | 333 | if (sumpSetup.rleSelected) 334 | { 335 | Serial.write(0x2A); // keep MSB off for RLE 336 | } 337 | else 338 | { 339 | Serial.write(0xAA); 340 | } 341 | 342 | samplesSentCount = samplesSentCount + 2; 343 | } 344 | 345 | // send the data up to end of buffer 346 | int finalIndex; 347 | 348 | if (wrappedBuffer) 349 | { 350 | finalIndex = sumpSetup.samplesToRecord - 1; 351 | } 352 | else 353 | { 354 | finalIndex = lastSampleIndex; 355 | } 356 | 357 | // data is sent from last to first 358 | 359 | // if buffer wrapped, send the last part of the data 360 | if (wrappedBuffer) 361 | { 362 | for (int index = lastSampleIndex; index >= 0; --index) 363 | { 364 | sample = getSample (sumpSetup, index); 365 | 366 | // if it is an RLE count, move count indicator to correct bit 367 | if (sumpSetup.rleUsed && (sample & sumpSetup.rleCountIndicator)) 368 | { 369 | // clear rleCountIndicator for this number of channels 370 | sample = sample & ~sumpSetup.rleCountIndicator; 371 | 372 | // set RLE count indicator to GUI 373 | sample = sample | RLE_MASK; 374 | } 375 | else if (!sumpSetup.rleSelected) 376 | { 377 | sample = (unusedValue & ~sumpSetup.sampleMask) + (sample & sumpSetup.sampleMask); 378 | } 379 | 380 | Serial.write(sample); 381 | ++samplesSentCount; 382 | 383 | unusedValue = ~unusedValue; // toggle 1's and 0's 384 | 385 | if (sumpSetup.rleSelected) 386 | { 387 | // RLE must have MSB of unused samples set to 0 388 | unusedValue = unusedValue & 0x7F; 389 | } 390 | } 391 | } 392 | 393 | // send the data (or rest of data after wrapped) 394 | for (int index = finalIndex; index >= firstSampleIndex; --index) 395 | { 396 | sample = getSample (sumpSetup, index); 397 | 398 | // if first sample index is an RLE count, set it to a value 399 | if (sumpSetup.rleUsed && 400 | (index == firstSampleIndex) && 401 | ((sample & RLE_MASK) == RLE_MASK)) 402 | { 403 | sample = firstRLEValue; 404 | } 405 | else 406 | { 407 | sample = getSample (sumpSetup, index); 408 | } 409 | 410 | // if it is an RLE count 411 | if (sumpSetup.rleUsed && (sample & sumpSetup.rleCountIndicator)) 412 | { 413 | // clear rleCountIndicator for this number of channels 414 | sample = sample & ~sumpSetup.rleCountIndicator; 415 | 416 | // set RLE count indicator to GUI 417 | sample = sample | RLE_MASK; 418 | } 419 | else if (!sumpSetup.rleSelected) 420 | { 421 | sample = (unusedValue & ~sumpSetup.sampleMask) + (sample & sumpSetup.sampleMask); 422 | } 423 | 424 | Serial.write(sample); 425 | ++samplesSentCount; 426 | 427 | unusedValue = ~unusedValue; // toggle 1's and 0's 428 | 429 | if (sumpSetup.rleSelected) 430 | { 431 | // RLE must have MSB of unused samples set to 0 432 | unusedValue = unusedValue & 0x7F; 433 | } 434 | } // for index = 435 | } 436 | 437 | 438 | -------------------------------------------------------------------------------- /record_rle_data.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2021 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // 26 | // This function records data using Run Length Encoding (RLE). 27 | // This is useful when the data does not change very often. If the 28 | // value does not change from the previous value, it just increments 29 | // a count. When the value changes again, it stores an extra sample 30 | // with the count, along with the highest channel set to 1 (e.g. 31 | // channel 7 for 8 channel mode, channel 3 for 4 channel mode). This 32 | // means that the highest channel cannot be used for data. 33 | // 34 | void recordRLEData (sumpSetupVariableStruct &sv, 35 | sumpDynamicVariableStruct &dynamic) 36 | { 37 | bool bufferHasWrapped = false; 38 | int elementsToRecord = sv.samplesToRecord / sv.samplesPerElement; 39 | register uint32_t *inputPtr = (uint32_t *)sv.startOfBuffer; 40 | uint32_t *endOfBuffer = (uint32_t *)sv.endOfBuffer; 41 | uint32_t *startOfBuffer = (uint32_t *)sv.startOfBuffer; 42 | uint32_t *startPtr = (uint32_t *)sv.startOfBuffer; 43 | byte samplesPerElement = sv.samplesPerElement; 44 | byte samplesPerElementMinusOne = samplesPerElement - 1; 45 | 46 | // shift right 1 to mask off upper channel, which is used for RLE 47 | uint32_t sampleMask = sv.sampleMask >> 1; 48 | uint32_t anyDataMask = sv.anyDataMask;; 49 | 50 | // this is to set the highest channel for an RLE count 51 | uint32_t rleCountIndicator = sv.rleCountIndicator; 52 | 53 | uint32_t sampleShift = sv.sampleShift; 54 | uint32_t sample; 55 | int sampleValue = -1; 56 | uint32_t previousFirstValue = 0; 57 | bool rleInProgress = false; 58 | int triggerCount = samplesPerElementMinusOne; 59 | register int workingCount = samplesPerElementMinusOne + 1; 60 | register uint32_t workingValue = 0; 61 | 62 | int currentTriggerLevel = 0; 63 | uint32_t triggerMask = sv.triggerMask[0]; 64 | uint32_t triggerValue = sv.triggerValue[0]; 65 | uint32_t triggerDelay = sv.triggerDelay[0]; 66 | 67 | // state is not used except to make a switch context for ptr 68 | stateType state; 69 | // ptr points to a label inside the switch statement to speed it up, 70 | // since it doesn't have to calculate the jump table each time through. 71 | // Label names are the case names with '_Label' added 72 | register void *switch_ptr; 73 | 74 | // if using a trigger 75 | if (sv.triggerMask[0]) 76 | { 77 | switch_ptr = &&Buffering_Label; 78 | 79 | // position to arm the trigger 80 | startPtr = inputPtr + sv.delaySizeInElements; 81 | } 82 | else 83 | { 84 | switch_ptr = &&Triggered_Second_Pass_Label; 85 | 86 | startPtr = endOfBuffer; 87 | } 88 | 89 | // 100% causes a problem with circular buffer - never stops 90 | if (startPtr >= endOfBuffer) 91 | { 92 | startPtr = endOfBuffer - 1; 93 | } 94 | 95 | maskInterrupts (); 96 | 97 | // read enough samples prior to arming to meet the pre-trigger request 98 | // (for speed, use while (1) and break instead of while (inputPtr != startPtr)) 99 | while (1) 100 | { 101 | waitForTimeout (); 102 | 103 | // read a sample 104 | sample = readSample () & sampleMask; 105 | 106 | if (sample != (uint32_t)sampleValue) 107 | { 108 | sampleValue = sample; 109 | 110 | // if previous rle count has not been written 111 | if (workingCount == 0) 112 | { 113 | #ifdef TIMING_DISCRETES_2 114 | digitalWriteFast (TIMING_PIN_3, HIGH); 115 | #endif 116 | 117 | *(inputPtr) = workingValue; 118 | ++inputPtr; 119 | 120 | // adjust for circular buffer wraparound at the end 121 | if (inputPtr >= endOfBuffer) 122 | { 123 | #ifdef TIMING_DISCRETES_2 124 | digitalWriteFast (TIMING_PIN_5, HIGH); 125 | #endif 126 | 127 | inputPtr = startOfBuffer; 128 | 129 | bufferHasWrapped = true; 130 | 131 | // if any data is received from PC, then stop (assume it is a reset) 132 | if (usbInterruptPending ()) 133 | { 134 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 135 | set_led_off (); 136 | SUMPreset(); 137 | break; 138 | } 139 | 140 | #ifdef TIMING_DISCRETES_2 141 | digitalWriteFast (TIMING_PIN_5, LOW); 142 | #endif 143 | } 144 | 145 | // save new value (no need to shift since just saved) 146 | workingValue = sampleValue; 147 | workingCount = samplesPerElementMinusOne; 148 | 149 | #ifdef TIMING_DISCRETES_2 150 | digitalWriteFast (TIMING_PIN_3, LOW); 151 | #endif 152 | } 153 | else 154 | { 155 | // save new value 156 | workingValue = (workingValue << sampleShift) + sampleValue; 157 | --workingCount; 158 | } 159 | 160 | rleInProgress = false; 161 | } 162 | else // same 163 | { 164 | #ifdef TIMING_DISCRETES_2 165 | digitalWriteFast (TIMING_PIN_2, HIGH); 166 | #endif 167 | 168 | if (rleInProgress == false) 169 | { 170 | // save count for previous value 171 | workingValue = (workingValue << sampleShift) + rleCountIndicator; 172 | --workingCount; 173 | 174 | rleInProgress = true; 175 | } 176 | 177 | // number of RLE instances is stored in the working Value 178 | workingValue++; 179 | 180 | // if RLE count is at the maximum value 181 | if ((workingValue & sampleMask) == sampleMask) 182 | { 183 | #ifdef TIMING_DISCRETES_2 184 | digitalWriteFast (TIMING_PIN_4, HIGH); 185 | #endif 186 | 187 | // force current count to be written and new count started 188 | rleInProgress = false; 189 | 190 | // not enough time to check if also going to write working value 191 | if (workingCount != 0) 192 | { 193 | // if any data is received from PC, then stop (assume it is a reset) 194 | if (usbInterruptPending ()) 195 | { 196 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 197 | set_led_off (); 198 | SUMPreset(); 199 | break; 200 | } 201 | } 202 | 203 | #ifdef TIMING_DISCRETES_2 204 | digitalWriteFast (TIMING_PIN_4, LOW); 205 | #endif 206 | } 207 | 208 | #ifdef TIMING_DISCRETES_2 209 | digitalWriteFast (TIMING_PIN_2, LOW); 210 | #endif 211 | } 212 | 213 | // save the working value when it is full 214 | if (workingCount == 0 && rleInProgress == false) 215 | { 216 | #ifdef TIMING_DISCRETES_2 217 | digitalWriteFast (TIMING_PIN_3, HIGH); 218 | #endif 219 | 220 | *(inputPtr) = workingValue; 221 | ++inputPtr; 222 | 223 | // adjust for circular buffer wraparound at the end 224 | if (inputPtr >= endOfBuffer) 225 | { 226 | #ifdef TIMING_DISCRETES_2 227 | digitalWriteFast (TIMING_PIN_5, HIGH); 228 | #endif 229 | 230 | inputPtr = startOfBuffer; 231 | 232 | bufferHasWrapped = true; 233 | 234 | // if any data is received from PC, then stop (assume it is a reset) 235 | if (usbInterruptPending ()) 236 | { 237 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 238 | set_led_off (); 239 | SUMPreset(); 240 | break; 241 | } 242 | 243 | #ifdef TIMING_DISCRETES_2 244 | digitalWriteFast (TIMING_PIN_5, LOW); 245 | #endif 246 | } 247 | 248 | workingCount = samplesPerElement; 249 | workingValue = 0; 250 | 251 | #ifdef TIMING_DISCRETES_2 252 | digitalWriteFast (TIMING_PIN_3, LOW); 253 | #endif 254 | 255 | } // if workingCount == 0 256 | else if (workingCount == 1) 257 | { 258 | // just before overwriting old data, check if it has data in it. 259 | // This is to prevent having just RLE counts at the start of the 260 | // buffer because it wrapped and overwrote the original first value. 261 | 262 | // if any data (i.e. not just RLE counts) in this value, save it 263 | if ((*(inputPtr) & anyDataMask) != anyDataMask) 264 | { 265 | previousFirstValue = *(inputPtr); 266 | } 267 | } 268 | 269 | // 270 | // For speed, perform the switch statement using a pointer to 271 | // the current state and a goto. The switch statement has to 272 | // be in the code so the compiler sets it up properly, but 273 | // the goto uses the duplicate labels for each case. 274 | // 275 | goto *switch_ptr; 276 | 277 | switch (state) { 278 | case LookingForSimpleTrigger : 279 | // not used 280 | break; 281 | 282 | case LookingForTrigger : 283 | LookingForTrigger_Label: 284 | // if trigger has occurred 285 | if ((sampleValue & triggerMask) == triggerValue) 286 | { 287 | if (triggerDelay > 0) { 288 | switch_ptr = &&TriggerDelay_Label; 289 | } else { 290 | // if last trigger level 291 | if (currentTriggerLevel >= sv.lastTriggerLevel) 292 | { 293 | triggerCount = workingCount; 294 | 295 | // last location to save 296 | startPtr = inputPtr - sv.delaySizeInElements; 297 | 298 | // move to triggered state 299 | switch_ptr = &&Triggered_First_Pass_Label; 300 | #ifdef TIMING_DISCRETES 301 | digitalWriteFast (TIMING_PIN_1, LOW); 302 | #endif 303 | 304 | } else { 305 | 306 | #ifdef TIMING_DISCRETES 307 | digitalWriteFast (TIMING_PIN_1, LOW); 308 | #endif 309 | 310 | // advance to next trigger level 311 | ++currentTriggerLevel; 312 | triggerMask = sv.triggerMask[currentTriggerLevel]; 313 | triggerValue = sv.triggerValue[currentTriggerLevel]; 314 | 315 | #ifdef TIMING_DISCRETES 316 | digitalWriteFast (TIMING_PIN_1, HIGH); 317 | #endif 318 | } 319 | } 320 | } 321 | break; 322 | 323 | case TriggerDelay : 324 | TriggerDelay_Label: 325 | --triggerDelay; 326 | if (triggerDelay == 0) { 327 | // if last trigger level 328 | if (currentTriggerLevel >= sv.lastTriggerLevel) { 329 | triggerCount = workingCount; 330 | 331 | // last location to save 332 | startPtr = inputPtr - sv.delaySizeInElements; 333 | 334 | // move to triggered state 335 | switch_ptr = &&Triggered_First_Pass_Label; 336 | 337 | } else { 338 | ++currentTriggerLevel; 339 | triggerMask = sv.triggerMask[currentTriggerLevel]; 340 | triggerValue = sv.triggerValue[currentTriggerLevel]; 341 | triggerDelay = sv.triggerDelay[currentTriggerLevel] - 1; 342 | 343 | switch_ptr = &&LookingForTrigger_Label; 344 | } 345 | } 346 | break; 347 | 348 | case Triggered: 349 | Triggered_Label: 350 | if (inputPtr == startPtr) { 351 | // done recording. Use a goto for speed so that 352 | // no 'if' needed to check for done in the main loop 353 | goto DoneRecording; 354 | } 355 | break; 356 | 357 | case Buffering: 358 | Buffering_Label: 359 | // if enough data is buffered 360 | if (inputPtr >= startPtr) 361 | { 362 | // move to armed state 363 | switch_ptr = &&LookingForTrigger_Label; 364 | set_led_on (); 365 | 366 | #ifdef TIMING_DISCRETES 367 | digitalWriteFast (TIMING_PIN_1, HIGH); 368 | #endif 369 | } 370 | break; 371 | 372 | case Triggered_Second_Pass: 373 | Triggered_Second_Pass_Label: 374 | // adjust for circular buffer wraparound at the end. 375 | if (startPtr < startOfBuffer) 376 | { 377 | startPtr = startPtr + elementsToRecord; 378 | } 379 | 380 | // move to triggered state 381 | switch_ptr = &&Triggered_Label; 382 | 383 | #ifdef TIMING_DISCRETES 384 | digitalWriteFast (TIMING_PIN_1, LOW); 385 | #endif 386 | break; 387 | 388 | case Triggered_First_Pass: 389 | Triggered_First_Pass_Label: 390 | // go as fast as possible to try to catch up from Triggered state 391 | switch_ptr = &&Triggered_Second_Pass_Label; 392 | set_led_off (); // TRIGGERED, turn off LED 393 | break; 394 | } // switch 395 | 396 | } // while (1) 397 | 398 | DoneRecording: 399 | 400 | // cleanup 401 | unmaskInterrupts (); 402 | 403 | #ifdef TIMING_DISCRETES 404 | digitalWriteFast (TIMING_PIN_0, LOW); 405 | #endif 406 | 407 | // save the first value in case it was overwritten due to buffer overflow 408 | // (i.e. first sample is an RLE count, but what was the value it is counting?) 409 | sv.firstRLEValue = previousFirstValue; 410 | 411 | // adjust trigger count 412 | dynamic.triggerSampleIndex = (startPtr + sv.delaySizeInElements - startOfBuffer) * samplesPerElement + samplesPerElementMinusOne - triggerCount; 413 | 414 | dynamic.bufferHasWrapped = bufferHasWrapped; 415 | 416 | // adjust for circular buffer wraparound at the end. 417 | if (dynamic.triggerSampleIndex >= (uint32_t)sv.samplesToRecord) 418 | { 419 | dynamic.triggerSampleIndex = dynamic.triggerSampleIndex - sv.samplesToRecord; 420 | } 421 | 422 | if (inputPtr != startPtr) 423 | { 424 | int deltaElements = inputPtr - startOfBuffer; 425 | 426 | if (deltaElements < 0) 427 | { 428 | deltaElements += elementsToRecord; 429 | } 430 | 431 | dynamic.interruptedIndex = deltaElements * samplesPerElement; 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /record_hardware.h: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2018 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // 26 | // This file is designed to be included multiple times with different 27 | // preprocessor directives. This will provide maximum speed at the 28 | // expense of extra code memory, which is currently not an issue. 29 | // 30 | 31 | bool checkTrigger ( 32 | TriggerType triggerType, 33 | uint16_t sampleChan0, 34 | uint16_t sampleChan1); 35 | 36 | // various function names 37 | #if MULTIPLE_CHANNELS 38 | 39 | #if USE_TRIGGER 40 | void recordSPIData_MultiChannel_Trigger (sumpSetupVariableStruct &sv, 41 | sumpDynamicVariableStruct &dynamic) 42 | #elif USE_PRE_TRIGGER 43 | void recordSPIData_MultiChannel_Pretrigger (sumpSetupVariableStruct &sv, 44 | sumpDynamicVariableStruct &dynamic) 45 | #else 46 | void recordSPIData_MultiChannel (sumpSetupVariableStruct &sv, 47 | sumpDynamicVariableStruct &dynamic) 48 | #endif 49 | 50 | #else 51 | 52 | #if USE_TRIGGER 53 | void recordSPIData_SingleChannel_Trigger (sumpSetupVariableStruct &sv, 54 | sumpDynamicVariableStruct &dynamic) 55 | #elif USE_PRE_TRIGGER 56 | void recordSPIData_SingleChannel_Pretrigger (sumpSetupVariableStruct &sv, 57 | sumpDynamicVariableStruct &dynamic) 58 | #else 59 | void recordSPIData_SingleChannel (sumpSetupVariableStruct &sv, 60 | sumpDynamicVariableStruct &dynamic) 61 | #endif 62 | 63 | #endif 64 | 65 | { 66 | 67 | #define SPI_SR_TXCTR_MASK 0x0000F000 68 | #define SPI_SR_RXCTR_MASK 0x000000F0 69 | 70 | uint32_t *startOfBuffer = (uint32_t *)sv.startOfBuffer; 71 | register uint32_t *inputPtr = (uint32_t *)sv.startOfBuffer; 72 | register uint32_t *endOfBuffer = (uint32_t *)sv.endOfBuffer; 73 | register uint32_t *startPtr = startOfBuffer; 74 | 75 | register uint32_t sampleChan0; 76 | 77 | bool bufferHasWrapped = false; 78 | byte triggerMask = sv.triggerMask[0]; 79 | byte triggerValue = sv.triggerValue[0]; 80 | 81 | #if MULTIPLE_CHANNELS 82 | register uint32_t sampleChan1; 83 | 84 | // ignore unused channels 85 | triggerMask &= 0x03; 86 | triggerValue &= 0x03; 87 | #else 88 | // ignore unused channels 89 | triggerMask &= 0x01; 90 | triggerValue &= 0x01; 91 | #endif 92 | 93 | #if USE_TRIGGER || USE_PRE_TRIGGER 94 | 95 | TriggerType triggerType = Channel0Low; 96 | 97 | #if USE_TRIGGER 98 | int elementsToRecord = sv.samplesToRecord / sv.samplesPerElement; 99 | byte samplesPerElementMinusOne = sv.samplesPerElement - 1; 100 | register stateType state = Buffering; 101 | bool triggered = false; 102 | #endif 103 | 104 | // if using a trigger 105 | if (triggerMask) 106 | { 107 | #if USE_TRIGGER 108 | state = Buffering; 109 | 110 | // position to arm the trigger (add 6 to clear FIFO at start) 111 | startPtr = inputPtr + sv.delaySizeInElements + 6; 112 | #else // pre-trigger 113 | 114 | // set delay to 0 115 | sv.delaySamples = 0; 116 | sv.delaySizeInElements = 0; 117 | 118 | // position to stop recording 119 | // (assumes multiple of 2 ints') 120 | startPtr = endOfBuffer; 121 | #endif 122 | 123 | if (triggerMask == 1 && triggerValue == 0) triggerType = Channel0Low; 124 | if (triggerMask == 1 && triggerValue == 1) triggerType = Channel0High; 125 | if (triggerMask == 2 && triggerValue == 0) triggerType = Channel1Low; 126 | if (triggerMask == 2 && triggerValue == 2) triggerType = Channel1High; 127 | if (triggerMask == 3 && triggerValue == 0) triggerType = BothChannelsLow; 128 | if (triggerMask == 3 && triggerValue == 1) triggerType = HighLow; 129 | if (triggerMask == 3 && triggerValue == 2) triggerType = LowHigh; 130 | if (triggerMask == 3 && triggerValue == 3) triggerType = BothChannelsHigh; 131 | } 132 | else 133 | { 134 | #if USE_TRIGGER 135 | state = Triggered_First_Pass; 136 | #endif 137 | 138 | startPtr = endOfBuffer; 139 | } 140 | 141 | #else // no trigger 142 | startPtr = endOfBuffer; 143 | #endif 144 | 145 | // 100% causes a problem with circular buffer - never stops 146 | if (startPtr >= endOfBuffer) 147 | { 148 | startPtr = endOfBuffer - 2; 149 | } 150 | 151 | spi1Initialize (); 152 | spi1Setup (sv.clockFrequency); 153 | 154 | #if MULTIPLE_CHANNELS 155 | { 156 | spi0Initialize (); 157 | spi0Setup (sv.clockFrequency); 158 | } 159 | #endif 160 | 161 | maskInterrupts (); 162 | 163 | #if USE_PRE_TRIGGER 164 | ////////////////////// 165 | // 166 | // pre-trigger 167 | // 168 | ////////////////////// 169 | uint16_t sample_chan0; 170 | uint16_t sample_chan1; 171 | bool done = false; 172 | 173 | // if using a trigger 174 | if (sv.triggerMask[0]) 175 | { 176 | while (!done) 177 | { 178 | sample_chan0 = (digitalReadFast (CHAN0) ? 0xFFFF : 0); 179 | sample_chan1 = (digitalReadFast (CHAN1) ? 0xFFFF : 0); 180 | 181 | done = checkTrigger ( 182 | triggerType, 183 | sample_chan0, 184 | sample_chan1); 185 | 186 | // if any data is received from PC, then stop (assume it is a reset) 187 | if (usbInterruptPending ()) 188 | { 189 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 190 | set_led_off (); 191 | SUMPreset(); 192 | unmaskInterrupts (); 193 | return; 194 | } 195 | } 196 | } 197 | #endif 198 | 199 | #if MULTIPLE_CHANNELS 200 | startSPIClock (true, sv.cpuClockTicks); 201 | #else 202 | startSPIClock (false, sv.cpuClockTicks); 203 | #endif 204 | 205 | ////////////////////// 206 | // 207 | // main loop 208 | // 209 | ////////////////////// 210 | while (1) 211 | { 212 | 213 | #if Teensy_LC 214 | 215 | // if data is ready to read 216 | if (SPI1_S & SPI_S_SPRF) { 217 | sampleChan0 = SPI1_DL | (SPI1_DH << 8); 218 | 219 | spi1StartTransfer (); 220 | 221 | #if MULTIPLE_CHANNELS 222 | { 223 | // clear status 224 | SPI0_S; 225 | 226 | sampleChan1 = SPI0_DL | (SPI0_DH << 8); 227 | 228 | spi0StartTransfer (); 229 | } 230 | #endif 231 | 232 | // wait until data is ready to read 233 | while (!(SPI1_S & SPI_S_SPRF)); 234 | 235 | *(inputPtr) = sampleChan0 = (sampleChan0 << 16) + SPI1_DL + (SPI1_DH << 8); 236 | ++inputPtr; 237 | 238 | spi1StartTransfer (); 239 | 240 | #if MULTIPLE_CHANNELS 241 | { 242 | // clear status 243 | SPI0_S; 244 | 245 | *(inputPtr) = sampleChan1 = (sampleChan1 << 16) + SPI0_DL + (SPI0_DH << 8); 246 | ++inputPtr; 247 | 248 | spi0StartTransfer (); 249 | } 250 | #endif 251 | 252 | #elif Teensy_3_5 || Teensy_3_6 253 | 254 | // if data is ready to read 255 | if (SPI1_SR & SPI_SR_RXCTR_MASK) 256 | { 257 | sampleChan0 = SPI1_POPR; 258 | 259 | // start next transfer 260 | SPI1_PUSHR = SPI_PUSHR_CONT; 261 | 262 | #if MULTIPLE_CHANNELS 263 | { 264 | sampleChan1 = SPI0_POPR; 265 | 266 | SPI0_PUSHR = SPI_PUSHR_CONT; 267 | } 268 | #endif 269 | 270 | // wait until data is ready to read 271 | while (!(SPI1_SR & SPI_SR_RXCTR_MASK)); 272 | 273 | *(inputPtr) = sampleChan0 = (sampleChan0 << 16) + SPI1_POPR; 274 | ++inputPtr; 275 | 276 | // start next transfer 277 | SPI1_PUSHR = SPI_PUSHR_CONT; 278 | 279 | #if MULTIPLE_CHANNELS 280 | { 281 | *(inputPtr) = sampleChan1 = (sampleChan1 << 16) + SPI0_POPR; 282 | ++inputPtr; 283 | 284 | // start next transfer 285 | SPI0_PUSHR = SPI_PUSHR_CONT; 286 | } 287 | #endif 288 | 289 | #else 290 | 291 | // if at least 2 data values are ready to read 292 | // (for speed, want constant mask of 0E0, so 'and' 0F0 and 1E0) 293 | if (SPI1_SR & SPI_SR_RXCTR_MASK & (SPI_SR_RXCTR_MASK << 1)) 294 | { 295 | *(inputPtr) = sampleChan0 = (SPI1_POPR << 16) + SPI1_POPR; 296 | ++inputPtr; 297 | 298 | // start next transfer 299 | SPI1_PUSHR = SPI_PUSHR_CONT; 300 | SPI1_PUSHR = SPI_PUSHR_CONT; 301 | 302 | #if MULTIPLE_CHANNELS 303 | { 304 | *(inputPtr) = sampleChan1 = (SPI0_POPR << 16) + SPI0_POPR; 305 | ++inputPtr; 306 | 307 | SPI0_PUSHR = SPI_PUSHR_CONT; 308 | SPI0_PUSHR = SPI_PUSHR_CONT; 309 | } 310 | #endif 311 | 312 | #endif 313 | 314 | #if !USE_TRIGGER 315 | if (inputPtr == startPtr) { 316 | // done recording 317 | break; 318 | } 319 | #else 320 | // adjust for circular buffer wraparound at the end 321 | if (inputPtr >= endOfBuffer) 322 | { 323 | inputPtr = sv.startOfBuffer; 324 | 325 | bufferHasWrapped = true; 326 | 327 | // if any data is received from PC, then stop (assume it is a reset) 328 | if (usbInterruptPending ()) 329 | { 330 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 331 | set_led_off (); 332 | SUMPreset(); 333 | break; 334 | } 335 | } 336 | 337 | switch (state) 338 | { 339 | case LookingForTrigger : 340 | // if trigger has occurred 341 | switch (triggerType) 342 | { 343 | case Channel0High: 344 | triggered = (sampleChan0 != 0); 345 | break; 346 | case Channel0Low: 347 | triggered = sampleChan0 != 0xFFFFFFFF; 348 | break; 349 | #if MULTIPLE_CHANNELS 350 | case Channel1High: 351 | triggered = sampleChan1 != 0; 352 | break; 353 | case Channel1Low: 354 | triggered = sampleChan1 != 0xFFFFFFFF; 355 | break; 356 | case BothChannelsHigh: 357 | triggered = (sampleChan0 & sampleChan1) != 0; 358 | break; 359 | case BothChannelsLow: 360 | triggered = (sampleChan0 | sampleChan1) != 0xFFFFFFFF; 361 | break; 362 | case HighLow: 363 | triggered = (sampleChan0 & ~sampleChan1) != 0; 364 | break; 365 | case LowHigh: 366 | triggered = (sampleChan0 | ~sampleChan1) != 0xFFFFFFFF; 367 | break; 368 | #endif 369 | default: 370 | break; 371 | } 372 | if (triggered) 373 | { 374 | // subtract 2 since inputPtr has already been incremented 375 | startPtr = inputPtr - 2 - sv.delaySizeInElements; 376 | 377 | // move to triggered state 378 | state = Triggered_First_Pass; 379 | } 380 | break; 381 | 382 | case TriggerDelay : 383 | break; 384 | 385 | case Triggered : 386 | if (inputPtr == startPtr) 387 | { 388 | // done recording 389 | goto DoneRecording; 390 | } 391 | break; 392 | 393 | case Buffering : 394 | // if enough data is buffered 395 | if (inputPtr >= startPtr) 396 | { 397 | // move to armed state 398 | state = LookingForTrigger; 399 | set_led_on (); 400 | 401 | #ifdef TIMING_DISCRETES 402 | digitalWriteFast (TIMING_PIN_1, HIGH); 403 | #endif 404 | } 405 | break; 406 | 407 | case Triggered_First_Pass : 408 | // go as fast as possible to try to catch up from Triggered state 409 | state = Triggered_Second_Pass; 410 | break; 411 | 412 | case Triggered_Second_Pass : 413 | // adjust for circular buffer wraparound at the end. 414 | if (startPtr < startOfBuffer) 415 | { 416 | startPtr = startPtr + elementsToRecord; 417 | } 418 | 419 | // move to triggered state 420 | state = Triggered; 421 | set_led_off (); // TRIGGERED, turn off LED 422 | 423 | #ifdef TIMING_DISCRETES 424 | digitalWriteFast (TIMING_PIN_1, LOW); 425 | #endif 426 | break; 427 | 428 | default: 429 | break; 430 | } // end switch 431 | #endif 432 | 433 | } // if data is ready to be read 434 | 435 | } // while (1) 436 | 437 | #if USE_TRIGGER 438 | DoneRecording: 439 | #endif 440 | 441 | unmaskInterrupts (); 442 | 443 | #if USE_TRIGGER 444 | // set trigger count 445 | dynamic.triggerSampleIndex = (startPtr + sv.delaySizeInElements - startOfBuffer) * sv.samplesPerElement + samplesPerElementMinusOne; 446 | 447 | // adjust for circular buffer wraparound at the end. 448 | if (dynamic.triggerSampleIndex >= (uint32_t)sv.samplesToRecord) 449 | { 450 | dynamic.triggerSampleIndex = dynamic.triggerSampleIndex - sv.samplesToRecord; 451 | } 452 | 453 | #else 454 | dynamic.triggerSampleIndex = sv.samplesPerElement; 455 | #endif 456 | 457 | dynamic.bufferHasWrapped = bufferHasWrapped; 458 | 459 | // turn off SPI module 460 | #if MULTIPLE_CHANNELS 461 | spiDisable (true); 462 | #else 463 | spiDisable (false); 464 | #endif 465 | } 466 | 467 | // only need to include this routine once 468 | #ifndef CHECKTRIGGER_DEFINED 469 | #define CHECKTRIGGER_DEFINED 470 | 471 | inline bool checkTrigger ( 472 | TriggerType triggerType, 473 | uint16_t sampleChan0, 474 | uint16_t sampleChan1) 475 | { 476 | bool triggered = false; 477 | 478 | // if trigger has occurred 479 | switch (triggerType) 480 | { 481 | case Channel0High: 482 | triggered = sampleChan0 != 0; 483 | break; 484 | case Channel0Low: 485 | triggered = sampleChan0 != 0xFFFF; 486 | break; 487 | case Channel1High: 488 | triggered = sampleChan1 != 0; 489 | break; 490 | case Channel1Low: 491 | triggered = sampleChan1 != 0xFFFF; 492 | break; 493 | case BothChannelsHigh: 494 | triggered = (sampleChan0 & sampleChan1) != 0; 495 | break; 496 | case BothChannelsLow: 497 | triggered = (sampleChan0 | sampleChan1) != 0xFFFF; 498 | break; 499 | case HighLow: 500 | triggered = (sampleChan0 & ~sampleChan1) != 0; 501 | break; 502 | case LowHigh: 503 | triggered = (sampleChan0 | ~sampleChan1) != 0xFFFF; 504 | break; 505 | default: 506 | break; 507 | } 508 | 509 | return triggered; 510 | } 511 | 512 | #endif 513 | -------------------------------------------------------------------------------- /record_high_speed_data_8_channels.cpp: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2018 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIeDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include "types.h" 27 | 28 | // This file is a cpp file so that it can assign registers for just within this scope. 29 | 30 | // Teensy LC 31 | #if defined(__MKL26Z64__) 32 | #define Teensy_LC 1 33 | // Teensy 4_0 34 | #elif defined(__IMXRT1062__) 35 | #define Teensy_4_0 1 36 | #endif 37 | 38 | // LC doesn't have enough registers for high speed 39 | #if not Teensy_LC 40 | 41 | #if Teensy_4_0 42 | // use Port 7 for sampling 43 | #define PORT_DATA_INPUT_REGISTER GPIO7_DR 44 | #else 45 | // use Port D for sampling 46 | #define PORT_DATA_INPUT_REGISTER GPIOD_PDIR 47 | #endif 48 | 49 | #define DEBUG_SERIAL(x) 0 // no debug output 50 | //#define DEBUG_SERIAL(x) Serial2.x // debug output to Serial2 51 | 52 | //#define TIMING_DISCRETES // if uncommented, set pins 0 and 1 for timing 53 | 54 | #define TIMING_PIN_0 15 55 | #define TIMING_PIN_1 16 56 | #define TIMING_PIN_2 17 57 | #define TIMING_PIN_3 18 58 | #define TIMING_PIN_4 19 59 | #define TIMING_PIN_5 22 60 | 61 | // these are from the main sketch 62 | extern int getCurrentFBUS (); 63 | extern void maskInterrupts (void); 64 | extern void set_led_off (); 65 | extern inline void set_led_on (); 66 | extern void SUMPreset(void); 67 | extern void unmaskInterrupts (void); 68 | extern inline bool usbInterruptPending (void); 69 | 70 | // forward declaration 71 | inline void waitForTimeout (void); 72 | 73 | // packed booleans can be written/read as fast as regular booleans 74 | struct Packed_Bools { 75 | bool bufferHasWrapped : 1; 76 | bool doneRecording : 1; 77 | bool simpleTrigger : 1; 78 | bool rleInProgress : 1; 79 | bool skipSomeWaits : 1; 80 | }; 81 | 82 | union Packed_Bools_Union { 83 | Packed_Bools b; 84 | int32_t integer; 85 | }; 86 | 87 | // pack into a single int to fit in 1 register 88 | struct Packed_Type { 89 | uint8_t triggerMask : 8; 90 | uint8_t triggerValue : 8; 91 | uint16_t triggerDelay : 16; 92 | }; 93 | 94 | union Packed_Union { 95 | Packed_Type p; 96 | int32_t integer; 97 | }; 98 | 99 | // save time by having structure pointer in a register 100 | // and using an offset to get each element 101 | struct Struct 102 | { 103 | int currentTriggerLevel; 104 | uint32_t delaySizeInElements; 105 | int elementsToRecord; 106 | uint32_t *startOfBuffer; 107 | uint32_t *startPtr; 108 | uint32_t *triggerPtr; 109 | }; 110 | 111 | Struct variable_struct; 112 | 113 | // define registers for items that are accessed a lot 114 | register Struct *struct_ptr asm("r5"); 115 | register uint32_t *endOfBuffer asm("r12"); 116 | register uint32_t *inputPtr asm("r9"); 117 | register uint32_t workingValue asm("r6"); 118 | 119 | volatile register uint8_t *port_data_register asm("r4"); 120 | volatile register uint32_t *timer_counter_register asm("r7"); 121 | 122 | register void (*stateFunctionPtr)() asm("r8"); 123 | 124 | register Packed_Bools_Union bools asm ("r11"); 125 | register Packed_Union packed asm ("r10"); 126 | Packed_Union triggerArray[4]; 127 | 128 | // forward declarations 129 | void Do_LookingForSimpleTrigger (); 130 | void Do_LookingForTrigger (); 131 | void Do_Triggered_Second_Pass (); 132 | void Do_TriggerFound (); 133 | 134 | 135 | FASTRUN void Do_Buffering () 136 | { 137 | // if enough data is buffered 138 | if (inputPtr >= struct_ptr->startPtr) 139 | { 140 | // move to armed state 141 | if (bools.b.simpleTrigger) 142 | { 143 | stateFunctionPtr = &Do_LookingForSimpleTrigger; 144 | } else { 145 | stateFunctionPtr = &Do_LookingForTrigger; 146 | } 147 | 148 | set_led_on (); 149 | 150 | #ifdef TIMING_DISCRETES 151 | digitalWriteFast (TIMING_PIN_1, HIGH); 152 | #endif 153 | } else { 154 | waitForTimeout (); 155 | } 156 | } 157 | 158 | FASTRUN void Do_Triggered () 159 | { 160 | if (inputPtr == struct_ptr->startPtr) 161 | { 162 | bools.b.doneRecording = true; 163 | } 164 | 165 | waitForTimeout (); 166 | } 167 | 168 | FASTRUN void Do_Triggered_First_Pass () 169 | { 170 | // go as fast as possible to try to catch up from Triggered state 171 | stateFunctionPtr = &Do_Triggered_Second_Pass; 172 | 173 | set_led_off (); // TRIGGERED, turn off LED 174 | 175 | waitForTimeout (); 176 | } 177 | 178 | FASTRUN void Do_Triggered_Second_Pass () 179 | { 180 | // adjust for circular buffer wraparound at the end. 181 | if (struct_ptr->startPtr < struct_ptr->startOfBuffer) 182 | { 183 | struct_ptr->startPtr = struct_ptr->startPtr + struct_ptr->elementsToRecord; 184 | } 185 | 186 | // move to triggered state 187 | stateFunctionPtr = &Do_Triggered; 188 | 189 | #ifdef TIMING_DISCRETES 190 | digitalWriteFast (TIMING_PIN_2, HIGH); 191 | #endif 192 | 193 | if (bools.b.skipSomeWaits) 194 | { 195 | return; 196 | } 197 | else 198 | { 199 | waitForTimeout (); 200 | } 201 | } 202 | 203 | FASTRUN void Do_TriggerDelay () 204 | { 205 | --packed.p.triggerDelay; 206 | if (packed.p.triggerDelay == 0) 207 | { 208 | struct_ptr->triggerPtr = inputPtr; 209 | 210 | // move to trigger found 211 | stateFunctionPtr = &Do_TriggerFound; 212 | } 213 | 214 | waitForTimeout (); 215 | } 216 | 217 | FASTRUN void Do_LookingForSimpleTrigger () 218 | { 219 | // if trigger has occurred 220 | if ((workingValue & packed.p.triggerMask) == packed.p.triggerValue) 221 | { 222 | // last location to save 223 | struct_ptr->startPtr = inputPtr - struct_ptr->delaySizeInElements; 224 | 225 | // move to triggered state 226 | stateFunctionPtr = &Do_Triggered_First_Pass; 227 | 228 | #ifdef TIMING_DISCRETES 229 | digitalWriteFast (TIMING_PIN_1, HIGH); 230 | #endif 231 | 232 | if (bools.b.skipSomeWaits) 233 | { 234 | return; 235 | } 236 | else 237 | { 238 | waitForTimeout (); 239 | } 240 | } else { 241 | waitForTimeout (); 242 | } 243 | } 244 | 245 | FASTRUN void Do_LookingForTrigger () 246 | { 247 | // if trigger has occurred 248 | if ((workingValue & packed.p.triggerMask) == packed.p.triggerValue) 249 | { 250 | if (packed.p.triggerDelay > 0) { 251 | stateFunctionPtr = &Do_TriggerDelay; 252 | } else { 253 | struct_ptr->triggerPtr = inputPtr; 254 | 255 | // move to triggered state 256 | stateFunctionPtr = &Do_TriggerFound; 257 | } 258 | 259 | if (bools.b.skipSomeWaits) 260 | { 261 | return; 262 | } 263 | else 264 | { 265 | waitForTimeout (); 266 | } 267 | } else { 268 | waitForTimeout (); 269 | } 270 | } 271 | 272 | FASTRUN void Do_TriggerFound () 273 | { 274 | if (struct_ptr->currentTriggerLevel == 0) 275 | { 276 | // last location to save 277 | struct_ptr->startPtr = struct_ptr->triggerPtr - struct_ptr->delaySizeInElements; 278 | 279 | // move to triggered state 280 | stateFunctionPtr = &Do_Triggered_First_Pass; 281 | 282 | #ifdef TIMING_DISCRETES 283 | digitalWriteFast (TIMING_PIN_1, HIGH); 284 | #endif 285 | 286 | 287 | if (bools.b.skipSomeWaits) 288 | { 289 | return; 290 | } 291 | else 292 | { 293 | waitForTimeout (); 294 | } 295 | } else { 296 | 297 | #ifdef TIMING_DISCRETES 298 | digitalWriteFast (TIMING_PIN_1, HIGH); 299 | #endif 300 | 301 | // advance to next trigger level 302 | --struct_ptr->currentTriggerLevel; 303 | packed.integer = triggerArray[struct_ptr->currentTriggerLevel].integer; 304 | 305 | // move to looking for next trigger level 306 | stateFunctionPtr = &Do_LookingForTrigger; 307 | 308 | #ifdef TIMING_DISCRETES 309 | digitalWriteFast (TIMING_PIN_1, LOW); 310 | #endif 311 | 312 | if (bools.b.skipSomeWaits) 313 | { 314 | return; 315 | } 316 | else 317 | { 318 | waitForTimeout (); 319 | } 320 | } 321 | } 322 | 323 | 324 | void recordHighSpeedData_8_Channels ( 325 | sumpSetupVariableStruct &sv, 326 | sumpDynamicVariableStruct &dynamic) 327 | { 328 | // backup the register values before using 329 | Packed_Bools_Union save_bools; 330 | save_bools.integer = bools.integer; 331 | Packed_Union save_packed; 332 | save_packed.integer = packed.integer; 333 | uint32_t *save_endOfBuffer = endOfBuffer; 334 | uint32_t *save_inputPtr = inputPtr; 335 | volatile uint8_t *save_port_data_register = port_data_register; 336 | Struct *save_struct_ptr = struct_ptr; 337 | void (*save_stateFunctionPtr)() = stateFunctionPtr; 338 | volatile uint32_t *save_timer_counter_register = timer_counter_register; 339 | uint32_t save_workingValue = workingValue; 340 | 341 | port_data_register = (volatile uint8_t *)&PORT_DATA_INPUT_REGISTER; 342 | timer_counter_register = &PIT_CVAL0; 343 | 344 | struct_ptr = &variable_struct; 345 | bools.b.bufferHasWrapped = false; 346 | struct_ptr->elementsToRecord = sv.samplesToRecord / sv.samplesPerElement; 347 | inputPtr = sv.startOfBuffer; 348 | endOfBuffer = sv.endOfBuffer; 349 | struct_ptr->startOfBuffer = sv.startOfBuffer; 350 | struct_ptr->delaySizeInElements = sv.delaySizeInElements; 351 | struct_ptr->startPtr = sv.startOfBuffer; 352 | 353 | // hard-code values since always 8 channels 354 | const byte samplesPerElement = 4; 355 | const byte samplesPerElementMinusOne = 3; 356 | const uint32_t sampleMask = 0xFF; 357 | const uint32_t sampleShift = 8; 358 | 359 | workingValue = 0; 360 | 361 | struct_ptr->currentTriggerLevel = 0; 362 | 363 | packed.p.triggerValue = sv.triggerValue[0]; 364 | packed.p.triggerMask += sv.triggerMask[0]; 365 | packed.p.triggerDelay += sv.triggerDelay[0]; 366 | 367 | // at the fastest speed, skip some WaitForTimeouts 368 | if (sv.cpuClockTicks <= 24) 369 | { 370 | bools.b.skipSomeWaits = true; 371 | } 372 | else 373 | { 374 | bools.b.skipSomeWaits = false; 375 | } 376 | 377 | bools.b.simpleTrigger = false; 378 | 379 | // set up trigger array 380 | if (sv.lastTriggerLevel == 0) 381 | { 382 | struct_ptr->currentTriggerLevel = 0; 383 | 384 | if (sv.triggerDelay[0] == 0) 385 | { 386 | bools.b.simpleTrigger = true; 387 | } 388 | } 389 | else if (sv.lastTriggerLevel == 1) 390 | { 391 | struct_ptr->currentTriggerLevel = 1; 392 | triggerArray[0].integer = sv.triggerMask[1] + (sv.triggerValue[1] << 8) + (sv.triggerDelay[1] << 16); 393 | } 394 | else if (sv.lastTriggerLevel == 2) 395 | { 396 | struct_ptr->currentTriggerLevel = 2; 397 | triggerArray[1].integer = sv.triggerMask[1] + (sv.triggerValue[1] << 8) + (sv.triggerDelay[1] << 16); 398 | triggerArray[0].integer = sv.triggerMask[2] + (sv.triggerValue[2] << 8) + (sv.triggerDelay[2] << 16); 399 | } 400 | else if (sv.lastTriggerLevel == 3) 401 | { 402 | struct_ptr->currentTriggerLevel = 3; 403 | triggerArray[2].integer = sv.triggerMask[1] + (sv.triggerValue[1] << 8) + (sv.triggerDelay[1] << 16); 404 | triggerArray[1].integer = sv.triggerMask[2] + (sv.triggerValue[2] << 8) + (sv.triggerDelay[2] << 16); 405 | triggerArray[0].integer = sv.triggerMask[3] + (sv.triggerValue[3] << 8) + (sv.triggerDelay[3] << 16); 406 | } 407 | 408 | // trigger delay is only checked 3 out of 4 passes, so adjust the end value accordingly 409 | for (int index = 0; index < sv.lastTriggerLevel; index++) 410 | { 411 | triggerArray[index].p.triggerDelay = (triggerArray[index].p.triggerDelay * 3) / 4; 412 | } 413 | packed.p.triggerDelay = (packed.p.triggerDelay * 3) / 4; 414 | 415 | // if using a trigger 416 | if (sv.triggerMask[0]) 417 | { 418 | stateFunctionPtr = &Do_Buffering; 419 | 420 | // position to arm the trigger 421 | struct_ptr->startPtr = inputPtr + struct_ptr->delaySizeInElements; 422 | } 423 | else 424 | { 425 | stateFunctionPtr = &Do_Triggered_Second_Pass; 426 | 427 | struct_ptr->startPtr = endOfBuffer; 428 | } 429 | 430 | // 100% causes a problem with circular buffer - never stops 431 | if (struct_ptr->startPtr >= endOfBuffer) 432 | { 433 | struct_ptr->startPtr = endOfBuffer - 1; 434 | } 435 | 436 | bools.b.doneRecording = false; 437 | maskInterrupts (); 438 | 439 | // read the first value 440 | workingValue = *port_data_register; 441 | 442 | // read enough samples prior to arming to meet the pre-trigger request 443 | // (for speed, use while (1) and break instead of while (inputPtr != startPtr)) 444 | while (1) 445 | { 446 | // workingCount = 3 447 | workingValue = (workingValue << sampleShift) + (*port_data_register & sampleMask); 448 | 449 | // perform current action for this state 450 | stateFunctionPtr (); 451 | 452 | // workingCount = 2 453 | workingValue = (workingValue << sampleShift) + (*port_data_register & sampleMask); 454 | 455 | stateFunctionPtr (); 456 | 457 | // workingCount = 1 458 | workingValue = (workingValue << sampleShift) + (*port_data_register & sampleMask); 459 | 460 | #ifdef TIMING_DISCRETES 461 | digitalWriteFast (TIMING_PIN_1, HIGH); 462 | #endif 463 | 464 | *inputPtr = workingValue; 465 | ++inputPtr; 466 | 467 | // adjust for circular buffer wraparound at the end 468 | if (inputPtr >= endOfBuffer) 469 | { 470 | #ifdef TIMING_DISCRETES_2 471 | digitalWriteFast (TIMING_PIN_5, HIGH); 472 | #endif 473 | 474 | inputPtr = (uint32_t *)struct_ptr->startOfBuffer; 475 | 476 | bools.b.bufferHasWrapped = true; 477 | 478 | // if any data is received from PC, then stop (assume it is a reset) 479 | if (usbInterruptPending ()) 480 | { 481 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 482 | set_led_off (); 483 | SUMPreset(); 484 | goto DoneRecording; 485 | break; 486 | } 487 | 488 | #ifdef TIMING_DISCRETES_2 489 | digitalWriteFast (TIMING_PIN_5, LOW); 490 | #endif 491 | } 492 | 493 | waitForTimeout (); 494 | 495 | // delay to match other waits 496 | asm volatile ("nop\n\t"); 497 | asm volatile ("nop\n\t"); 498 | 499 | // first value after saving workingValue doesn't need to 500 | // shift previous value nor mask off extra bits. This saves 501 | // time that was used saving workingValue above. 502 | workingValue = *port_data_register; 503 | 504 | #ifdef TIMING_DISCRETES 505 | digitalWriteFast (TIMING_PIN_1, LOW); 506 | #endif 507 | 508 | stateFunctionPtr (); 509 | 510 | if (bools.b.doneRecording) 511 | { 512 | goto DoneRecording; 513 | } 514 | } // while (1) 515 | 516 | DoneRecording: 517 | 518 | // cleanup 519 | unmaskInterrupts (); 520 | 521 | #ifdef TIMING_DISCRETES 522 | digitalWriteFast (TIMING_PIN_0, LOW); 523 | #endif 524 | 525 | // adjust trigger count 526 | dynamic.triggerSampleIndex = (struct_ptr->startPtr + sv.delaySizeInElements - struct_ptr->startOfBuffer) * samplesPerElement + samplesPerElementMinusOne; 527 | 528 | dynamic.bufferHasWrapped = bools.b.bufferHasWrapped; 529 | 530 | // adjust for circular buffer wraparound at the end. 531 | if (dynamic.triggerSampleIndex >= (uint32_t)sv.samplesToRecord) 532 | { 533 | dynamic.triggerSampleIndex = dynamic.triggerSampleIndex - sv.samplesToRecord; 534 | } 535 | 536 | // restore the register values 537 | bools.integer = save_bools.integer; 538 | packed.integer = save_packed.integer; 539 | endOfBuffer = save_endOfBuffer; 540 | inputPtr = save_inputPtr; 541 | port_data_register = save_port_data_register; 542 | struct_ptr = save_struct_ptr; 543 | stateFunctionPtr = save_stateFunctionPtr; 544 | timer_counter_register = save_timer_counter_register; 545 | workingValue = save_workingValue; 546 | } 547 | 548 | #define LED_PIN 13 549 | 550 | inline void set_led_on () { 551 | digitalWriteFast (LED_PIN, HIGH); 552 | } 553 | 554 | inline void set_led_off () { 555 | digitalWriteFast (LED_PIN, LOW); 556 | } 557 | 558 | inline void toggleTimingPin0 () { 559 | *portToggleRegister (TIMING_PIN_0) = (uint32_t)stateFunctionPtr; 560 | } 561 | 562 | // returns true if a USB interrupt is pending (meaning data is available) 563 | inline bool usbInterruptPending (void) { 564 | 565 | #if Teensy_4_0 566 | return (USB1_ENDPTCOMPLETE); 567 | #else 568 | return (USB0_ISTAT & ~USB_ISTAT_SOFTOK); 569 | #endif 570 | 571 | } 572 | 573 | inline void waitForTimeout (void) 574 | { 575 | #ifdef TIMING_DISCRETES 576 | toggleTimingPin0 (); 577 | #endif 578 | 579 | //if (*timer_counter_register < 14) 580 | 581 | // WaitCount has to be less than cpu cycles in the shortest 582 | // loop (Do_Triggered?), so that it doesn't start too early 583 | #if F_CPU >= 144000000 584 | const int WaitCount = 14 / 2; 585 | #else 586 | const int WaitCount = 14 / (F_CPU / F_BUS); 587 | #endif 588 | 589 | asm volatile ("wait_loop_%=:\n\t" 590 | "ldr r2, [r7]\n\t" 591 | "cmp r2, %[WaitCount]\n\t" 592 | "bhi wait_loop_%=\n\t" 593 | 594 | :: [WaitCount] "i" (WaitCount) 595 | : "cc", "r2"); 596 | 597 | #ifdef TIMING_DISCRETES 598 | toggleTimingPin0 (); 599 | #endif 600 | } 601 | 602 | #endif // if not Teensy_LC 603 | 604 | 605 | 606 | 607 | -------------------------------------------------------------------------------- /record_high_speed_rle_data.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2018 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // 26 | // This function records data using Run Length Encoding (RLE). 27 | // This is useful when the data does not change very often. If the 28 | // value does not change from the previous value, it just increments 29 | // a count. When the value changes again, it stores an extra sample 30 | // with the count, along with the highest channel set to 1 (e.g. 31 | // channel 7 for 8 channel mode, channel 3 for 4 channel mode). This 32 | // means that the highest channel cannot be used for data. 33 | // 34 | void recordHighSpeedRLEData (sumpSetupVariableStruct &sv, 35 | sumpDynamicVariableStruct &dynamic) 36 | { 37 | // hard-code values since always 8 channels 38 | const byte samplesPerElement = 4; 39 | const byte samplesPerElementMinusOne = 3; 40 | const uint32_t sampleMask = 0x7F; 41 | const uint32_t sampleShift = 8; 42 | const uint32_t rleCountIndicator = 0x80; 43 | const uint32_t anyDataMask = 0x80808080; 44 | 45 | // packed booleans can be written/read as fast as regular booleans 46 | struct Packed_Bools { 47 | bool bufferHasWrapped : 1; 48 | bool doneRecording : 1; 49 | bool rleInProgress : 1; 50 | bool simpleTrigger : 1; 51 | bool skipWait : 1; 52 | }; 53 | 54 | union Packed_Bools_Union { 55 | Packed_Bools b; 56 | int32_t integer; 57 | }; 58 | 59 | // pack into a single int to fit in 1 register 60 | struct Packed_Type { 61 | uint8_t triggerMask : 8; 62 | uint8_t triggerValue : 8; 63 | uint16_t triggerDelay : 16; 64 | }; 65 | 66 | union Packed_Union { 67 | Packed_Type p; 68 | int32_t integer; 69 | }; 70 | 71 | Packed_Bools_Union bools; 72 | Packed_Union packed; 73 | Packed_Union triggerArray[4]; 74 | 75 | 76 | //// bool bufferHasWrapped = false; 77 | bools.b.bufferHasWrapped = false; 78 | int elementsToRecord = sv.samplesToRecord / sv.samplesPerElement; 79 | register uint32_t *inputPtr = (uint32_t *)sv.startOfBuffer; 80 | uint32_t *endOfBuffer = (uint32_t *)sv.endOfBuffer; 81 | uint32_t *startOfBuffer = (uint32_t *)sv.startOfBuffer; 82 | uint32_t *startPtr = (uint32_t *)sv.startOfBuffer; 83 | //// byte samplesPerElement = sv.samplesPerElement; 84 | //// byte samplesPerElementMinusOne = samplesPerElement - 1; 85 | 86 | // shift right 1 to mask off upper channel, which is used for RLE 87 | //// uint32_t sampleMask = sv.sampleMask >> 1; 88 | //// uint32_t anyDataMask = sv.anyDataMask;; 89 | 90 | // this is to set the highest channel for an RLE count 91 | //// uint32_t rleCountIndicator = sv.rleCountIndicator; 92 | 93 | //// uint32_t sampleShift = sv.sampleShift; 94 | int sampleValue = -1; 95 | // int previousSampleValue = -1; 96 | uint32_t previousFirstValue = 0; 97 | //// bool rleInProgress = false; 98 | bools.b.rleInProgress = false; 99 | // int triggerCount = samplesPerElementMinusOne; 100 | register int workingCount = samplesPerElementMinusOne + 1; 101 | register uint32_t workingValue = 0; 102 | 103 | bools.b.simpleTrigger = false; 104 | bools.b.skipWait = false; 105 | 106 | int currentTriggerLevel = 0; 107 | //// uint32_t triggerMask = sv.triggerMask[0]; 108 | //// uint32_t triggerValue = sv.triggerValue[0]; 109 | //// uint32_t triggerDelay = sv.triggerDelay[0]; 110 | packed.p.triggerMask = sv.triggerMask[0]; 111 | packed.p.triggerValue = sv.triggerValue[0]; 112 | packed.p.triggerDelay = sv.triggerDelay[0]; 113 | 114 | // state is not used except to make a switch context for ptr 115 | stateType state; 116 | // ptr points to a label inside the switch statement to speed it up, 117 | // since it doesn't have to calculate the jump table each time through. 118 | // Label names are the case names with '_Label' added 119 | register void *switch_ptr; 120 | 121 | // set up trigger array 122 | if (sv.lastTriggerLevel == 0) 123 | { 124 | currentTriggerLevel = 0; 125 | 126 | if (sv.triggerDelay[0] == 0) 127 | { 128 | bools.b.simpleTrigger = true; 129 | } 130 | DEBUG_SERIAL (print("Packed int:")); 131 | DEBUG_SERIAL (println(packed.integer, HEX)); 132 | } 133 | else if (sv.lastTriggerLevel == 1) 134 | { 135 | currentTriggerLevel = 1; 136 | triggerArray[0].integer = sv.triggerMask[1] + (sv.triggerValue[1] << 8) + (sv.triggerDelay[1] << 16); 137 | } 138 | else if (sv.lastTriggerLevel == 2) 139 | { 140 | currentTriggerLevel = 2; 141 | triggerArray[1].integer = sv.triggerMask[1] + (sv.triggerValue[1] << 8) + (sv.triggerDelay[1] << 16); 142 | triggerArray[0].integer = sv.triggerMask[2] + (sv.triggerValue[2] << 8) + (sv.triggerDelay[2] << 16); 143 | } 144 | else if (sv.lastTriggerLevel == 3) 145 | { 146 | currentTriggerLevel = 3; 147 | triggerArray[2].integer = sv.triggerMask[1] + (sv.triggerValue[1] << 8) + (sv.triggerDelay[1] << 16); 148 | triggerArray[1].integer = sv.triggerMask[2] + (sv.triggerValue[2] << 8) + (sv.triggerDelay[2] << 16); 149 | triggerArray[0].integer = sv.triggerMask[3] + (sv.triggerValue[3] << 8) + (sv.triggerDelay[3] << 16); 150 | } 151 | 152 | // if using a trigger 153 | if (sv.triggerMask[0]) 154 | { 155 | switch_ptr = &&Buffering_Label; 156 | 157 | // position to arm the trigger 158 | startPtr = inputPtr + sv.delaySizeInElements; 159 | } 160 | else 161 | { 162 | switch_ptr = &&Triggered_Second_Pass_Label; 163 | 164 | startPtr = endOfBuffer; 165 | } 166 | 167 | // 100% causes a problem with circular buffer - never stops 168 | if (startPtr >= endOfBuffer) 169 | { 170 | startPtr = endOfBuffer - 1; 171 | } 172 | 173 | maskInterrupts (); 174 | 175 | // read enough samples prior to arming to meet the pre-trigger request 176 | // (for speed, use while (1) and break instead of while (inputPtr != startPtr)) 177 | while (1) 178 | { 179 | /* 180 | if (bools.b.skipWait) 181 | { 182 | bools.b.skipWait = false; 183 | clearTimerFlag (); 184 | } 185 | else 186 | */ 187 | { 188 | //// waitForTimeout (); 189 | waitForTimeout3 (); 190 | } 191 | 192 | ////Skip_Wait_Label: 193 | //// digitalWriteFast (TIMING_PIN_1, LOW); 194 | // read a sample 195 | // sampleValue = PORT_DATA_INPUT_REGISTER & sampleMask; 196 | 197 | if ((PORT_DATA_INPUT_REGISTER & sampleMask) != (uint32_t)sampleValue) 198 | ////if ((temp = (PORT_DATA_INPUT_REGISTER & sampleMask)) != sampleValue) 199 | // if (sampleValue != previousSampleValue) 200 | { 201 | sampleValue = PORT_DATA_INPUT_REGISTER & sampleMask; 202 | ////sampleValue = temp; 203 | 204 | #ifdef TIMING_DISCRETES 205 | toggleTimingPin1 (); 206 | #endif 207 | // bools.b.skipWait = true; 208 | 209 | // if previous rle count has not been written 210 | if (workingCount == 0) 211 | { 212 | #ifdef TIMING_DISCRETES_2 213 | digitalWriteFast (TIMING_PIN_3, HIGH); 214 | #endif 215 | 216 | *inputPtr = workingValue; 217 | ++inputPtr; 218 | 219 | // adjust for circular buffer wraparound at the end 220 | if (inputPtr >= endOfBuffer) 221 | { 222 | #ifdef TIMING_DISCRETES_2 223 | digitalWriteFast (TIMING_PIN_5, HIGH); 224 | #endif 225 | 226 | inputPtr = startOfBuffer; 227 | 228 | bools.b.bufferHasWrapped = true; 229 | 230 | // if any data is received from PC, then stop (assume it is a reset) 231 | if (usbInterruptPending ()) 232 | { 233 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 234 | set_led_off (); 235 | SUMPreset(); 236 | break; 237 | } 238 | 239 | #ifdef TIMING_DISCRETES_2 240 | digitalWriteFast (TIMING_PIN_5, LOW); 241 | #endif 242 | } 243 | 244 | // save new value (no need to shift since just saved) 245 | workingValue = sampleValue; 246 | workingCount = samplesPerElementMinusOne; 247 | 248 | #ifdef TIMING_DISCRETES_2 249 | digitalWriteFast (TIMING_PIN_3, LOW); 250 | #endif 251 | } 252 | else 253 | { 254 | // save new value 255 | workingValue = (workingValue << sampleShift) + sampleValue; 256 | --workingCount; 257 | } 258 | 259 | // previousSampleValue = sampleValue; 260 | bools.b.rleInProgress = false; 261 | } 262 | else // same 263 | { 264 | #ifdef TIMING_DISCRETES_2 265 | digitalWriteFast (TIMING_PIN_2, HIGH); 266 | #endif 267 | 268 | if (bools.b.rleInProgress == false) 269 | { 270 | // save count for previous value 271 | workingValue = (workingValue << sampleShift) + rleCountIndicator + 1; 272 | --workingCount; 273 | 274 | bools.b.rleInProgress = true; 275 | } 276 | else 277 | { 278 | // number of RLE instances is stored in the working Value 279 | workingValue++; 280 | 281 | // if RLE count is at the maximum value 282 | if ((workingValue & sampleMask) == sampleMask) 283 | { 284 | #ifdef TIMING_DISCRETES_2 285 | digitalWriteFast (TIMING_PIN_4, HIGH); 286 | #endif 287 | 288 | // force current count to be written and new count started 289 | bools.b.rleInProgress = false; 290 | 291 | // not enough time to check if also going to write working value 292 | if (workingCount != 0) 293 | { 294 | // if any data is received from PC, then stop (assume it is a reset) 295 | if (usbInterruptPending ()) 296 | { 297 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 298 | set_led_off (); 299 | SUMPreset(); 300 | break; 301 | } 302 | } 303 | 304 | #ifdef TIMING_DISCRETES_2 305 | digitalWriteFast (TIMING_PIN_4, LOW); 306 | digitalWriteFast (TIMING_PIN_4, HIGH); 307 | digitalWriteFast (TIMING_PIN_4, LOW); 308 | #endif 309 | } 310 | } 311 | 312 | #ifdef TIMING_DISCRETES_2 313 | digitalWriteFast (TIMING_PIN_2, LOW); 314 | #endif 315 | } 316 | 317 | // save the working value when it is full 318 | if (workingCount == 0 && bools.b.rleInProgress == false) 319 | { 320 | #ifdef TIMING_DISCRETES_2 321 | digitalWriteFast (TIMING_PIN_3, HIGH); 322 | #endif 323 | 324 | *inputPtr = workingValue; 325 | ++inputPtr; 326 | 327 | // adjust for circular buffer wraparound at the end 328 | if (inputPtr >= endOfBuffer) 329 | { 330 | #ifdef TIMING_DISCRETES_2 331 | digitalWriteFast (TIMING_PIN_5, HIGH); 332 | #endif 333 | 334 | inputPtr = startOfBuffer; 335 | 336 | bools.b.bufferHasWrapped = true; 337 | 338 | // if any data is received from PC, then stop (assume it is a reset) 339 | if (usbInterruptPending ()) 340 | { 341 | DEBUG_SERIAL(print(" Halt due to USB interrupt")); 342 | set_led_off (); 343 | SUMPreset(); 344 | break; 345 | } 346 | 347 | #ifdef TIMING_DISCRETES_2 348 | digitalWriteFast (TIMING_PIN_5, LOW); 349 | #endif 350 | } 351 | 352 | workingCount = samplesPerElement; 353 | workingValue = 0; 354 | 355 | #ifdef TIMING_DISCRETES_2 356 | digitalWriteFast (TIMING_PIN_3, LOW); 357 | #endif 358 | 359 | } // if workingCount == 0 360 | else if (workingCount == 1) 361 | { 362 | // just before overwriting old data, check if it has data in it. 363 | // This is to prevent having just RLE counts at the start of the 364 | // buffer because it wrapped arn overwrote the original first value. 365 | 366 | // if any data (i.e. not just RLE counts) in this value, save it 367 | if ((*inputPtr & anyDataMask) != anyDataMask) 368 | { 369 | previousFirstValue = *inputPtr; 370 | } 371 | } 372 | 373 | // 374 | // For speed, perform the switch statement using a pointer to 375 | // the current state and a goto. The switch statement has to 376 | // be in the code so the compiler sets it up properly, but 377 | // the goto uses the duplicate labels for each case. 378 | // 379 | goto *switch_ptr; 380 | 381 | switch (state) { 382 | case LookingForSimpleTrigger : 383 | LookingForSimpleTrigger_Label: 384 | // if trigger has occurred 385 | if ((sampleValue & packed.p.triggerMask) == packed.p.triggerValue) 386 | { 387 | // last location to save 388 | startPtr = inputPtr - sv.delaySizeInElements; 389 | 390 | // move to triggered state 391 | // state = Triggered_First_Pass; 392 | switch_ptr = &&Triggered_First_Pass_Label; 393 | #ifdef TIMING_DISCRETES 394 | digitalWriteFast (TIMING_PIN_1, LOW); 395 | #endif 396 | 397 | } 398 | break; 399 | 400 | case LookingForTrigger : 401 | LookingForTrigger_Label: 402 | // if trigger has occurred 403 | if ((sampleValue & packed.p.triggerMask) == packed.p.triggerValue) 404 | { 405 | if (packed.p.triggerDelay > 0) { 406 | // state = TriggerDelay; 407 | switch_ptr = &&TriggerDelay_Label; 408 | } else { 409 | // if last trigger level 410 | if (currentTriggerLevel == 0) 411 | { 412 | // last location to save 413 | startPtr = inputPtr - sv.delaySizeInElements; 414 | 415 | // move to triggered state 416 | // state = Triggered_First_Pass; 417 | switch_ptr = &&Triggered_First_Pass_Label; 418 | #ifdef TIMING_DISCRETES 419 | digitalWriteFast (TIMING_PIN_1, LOW); 420 | #endif 421 | 422 | } else { 423 | 424 | #ifdef TIMING_DISCRETES 425 | digitalWriteFast (TIMING_PIN_1, LOW); 426 | #endif 427 | 428 | // advance to next trigger level 429 | --currentTriggerLevel; 430 | packed.integer = triggerArray[currentTriggerLevel].integer; 431 | 432 | #ifdef TIMING_DISCRETES 433 | digitalWriteFast (TIMING_PIN_1, HIGH); 434 | #endif 435 | } 436 | } 437 | } 438 | break; 439 | 440 | case TriggerDelay : 441 | TriggerDelay_Label: 442 | --packed.p.triggerDelay; 443 | if (packed.p.triggerDelay == 0) { 444 | // if last trigger level 445 | if (currentTriggerLevel == 0) { 446 | // last location to save 447 | startPtr = inputPtr - sv.delaySizeInElements; 448 | 449 | // move to triggered state 450 | // state = Triggered_First_Pass; 451 | switch_ptr = &&Triggered_First_Pass_Label; 452 | 453 | } else { 454 | --currentTriggerLevel; 455 | packed.integer = triggerArray[currentTriggerLevel].integer; 456 | // state = LookingForTrigger; 457 | switch_ptr = &&LookingForTrigger_Label; 458 | } 459 | } 460 | break; 461 | 462 | case Triggered: 463 | Triggered_Label: 464 | if (inputPtr == startPtr) { 465 | // done recording. Use a goto for speed so that 466 | // no 'if' needed to check for done in the main loop 467 | goto DoneRecording; 468 | } 469 | break; 470 | 471 | case Buffering: 472 | Buffering_Label: 473 | // if enough data is buffered 474 | if (inputPtr >= startPtr) 475 | { 476 | // move to armed state 477 | // state = LookingForTrigger; 478 | switch_ptr = &&LookingForTrigger_Label; 479 | set_led_on (); 480 | 481 | #ifdef TIMING_DISCRETES 482 | digitalWriteFast (TIMING_PIN_1, HIGH); 483 | #endif 484 | } 485 | break; 486 | 487 | case Triggered_Second_Pass: 488 | Triggered_Second_Pass_Label: 489 | // adjust for circular buffer wraparound at the end. 490 | if (startPtr < startOfBuffer) 491 | { 492 | startPtr = startPtr + elementsToRecord; 493 | } 494 | 495 | // move to triggered state 496 | // state = Triggered; 497 | switch_ptr = &&Triggered_Label; 498 | 499 | #ifdef TIMING_DISCRETES 500 | digitalWriteFast (TIMING_PIN_1, LOW); 501 | #endif 502 | break; 503 | 504 | case Triggered_First_Pass: 505 | Triggered_First_Pass_Label: 506 | // go as fast as possible to try to catch up from Triggered state 507 | // state = Triggered_Second_Pass; 508 | switch_ptr = &&Triggered_Second_Pass_Label; 509 | set_led_off (); // TRIGGERED, turn off LED 510 | break; 511 | } 512 | // } // if state == LookingForTrigger 513 | 514 | } // while (1) 515 | 516 | DoneRecording: 517 | 518 | // cleanup 519 | unmaskInterrupts (); 520 | 521 | #ifdef TIMING_DISCRETES 522 | digitalWriteFast (TIMING_PIN_0, LOW); 523 | #endif 524 | 525 | // save the first value in case it was overwritten due to buffer overflow 526 | // (i.e. first sample is an RLE count, but what was the value it is counting?) 527 | sv.firstRLEValue = previousFirstValue; 528 | 529 | // adjust trigger count 530 | dynamic.triggerSampleIndex = (startPtr + sv.delaySizeInElements - startOfBuffer) * samplesPerElement + samplesPerElementMinusOne; 531 | 532 | dynamic.bufferHasWrapped = bools.b.bufferHasWrapped; 533 | 534 | // adjust for circular buffer wraparound at the end. 535 | if (dynamic.triggerSampleIndex >= (uint32_t)sv.samplesToRecord) 536 | { 537 | dynamic.triggerSampleIndex = dynamic.triggerSampleIndex - sv.samplesToRecord; 538 | } 539 | 540 | if (inputPtr != startPtr) 541 | { 542 | int deltaElements = inputPtr - startOfBuffer; 543 | 544 | if (deltaElements < 0) 545 | { 546 | deltaElements += elementsToRecord; 547 | } 548 | 549 | dynamic.interruptedIndex = deltaElements * samplesPerElement; 550 | } 551 | } 552 | 553 | inline void toggleTimingPin0 () { 554 | *portToggleRegister (TIMING_PIN_0) = 1; 555 | } 556 | inline void toggleTimingPin1 () { 557 | //// *portToggleRegister (TIMING_PIN_1) = 1; 558 | *portToggleRegister (TIMING_PIN_3) = 1; 559 | } 560 | 561 | inline void waitForTimeout2 (void) 562 | { 563 | #ifdef TIMING_DISCRETES 564 | toggleTimingPin0 (); 565 | #endif 566 | 567 | 568 | // for speed, to reduce jitter 569 | if (TIMER_FLAG_REGISTER) 570 | { 571 | clearTimerFlag (); 572 | } else if (TIMER_FLAG_REGISTER) { 573 | clearTimerFlag (); 574 | } else { 575 | // digitalWriteFast (TIMING_PIN_0, LOW); 576 | // while (!TIMER_FLAG_REGISTER); 577 | // digitalWriteFast (TIMING_PIN_0, HIGH); 578 | // clearTimerFlag (); 579 | // } 580 | 581 | 582 | waitStart: 583 | if (TIMER_FLAG_REGISTER) goto waitEnd; 584 | if (TIMER_FLAG_REGISTER) goto waitEnd; 585 | if (TIMER_FLAG_REGISTER) goto waitEnd; 586 | if (TIMER_FLAG_REGISTER) goto waitEnd; 587 | if (TIMER_FLAG_REGISTER) goto waitEnd; 588 | if (TIMER_FLAG_REGISTER) goto waitEnd; 589 | if (TIMER_FLAG_REGISTER) goto waitEnd; 590 | if (!TIMER_FLAG_REGISTER) goto waitStart; 591 | 592 | waitEnd: 593 | clearTimerFlag (); 594 | } 595 | 596 | #ifdef TIMING_DISCRETES 597 | toggleTimingPin0 (); 598 | #endif 599 | } 600 | 601 | inline void waitForTimeout3 (void) 602 | { 603 | #ifdef TIMING_DISCRETES 604 | // digitalWriteFast (TIMING_PIN_0, HIGH); 605 | toggleTimingPin0 (); 606 | #endif 607 | 608 | // WaitCount has to be less than cpu cycles in the shortest 609 | // loop (Do_Triggered?), so that it doesn't start too early 610 | const int WaitCount = 24 / (F_CPU / F_BUS); 611 | 612 | while (PIT_CVAL0 > WaitCount); 613 | 614 | #ifdef TIMING_DISCRETES 615 | // digitalWriteFast (TIMING_PIN_0, LOW); 616 | toggleTimingPin0 (); 617 | #endif 618 | } 619 | 620 | 621 | -------------------------------------------------------------------------------- /record_hardware.ino: -------------------------------------------------------------------------------- 1 | /* Teensy Logic Analyzer 2 | * Copyright (c) 2020 LAtimes2 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining 5 | * a copy of this software and associated documentation files (the 6 | * "Software"), to deal in the Software without restriction, including 7 | * without limitation the rights to use, copy, modify, merge, publish, 8 | * distribute, sublicense, and/or sell copies of the Software, and to 9 | * permit persons to whom the Software is furnished to do so, subject to 10 | * the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be 13 | * included in all copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 19 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 20 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 21 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | // skip 4.0 for now 26 | #if not Teensy_4_0 27 | 28 | // Teensy 3.0/3.1/3.2 29 | #if defined(KINETISK) 30 | 31 | // 32 | // Teensy 3.1 MCU has a partial implementation of SPI1 in the hardware - there is no clock signal available. 33 | // Luckily, the logic analyzer doesn't need the clock signal, only the MISO signal, so it can use SPI1. 34 | // 35 | 36 | #define KINETISK_SPI1 (*(KINETISK_SPI_t *)0x4002D000) 37 | #define SPI1_MCR (KINETISK_SPI1.MCR) // DSPI Module Configuration Register 38 | #define SPI1_TCR (KINETISK_SPI1.TCR) // DSPI Transfer Count Register 39 | #define SPI1_CTAR0 (KINETISK_SPI1.CTAR0) // DSPI Clock and Transfer Attributes Register, In Master Mode 40 | #define SPI1_SR (KINETISK_SPI1.SR) // DSPI Status Register 41 | #define SPI1_PUSHR (KINETISK_SPI1.PUSHR) // DSPI PUSH TX FIFO Register In Master Mode 42 | #define SPI1_POPR (KINETISK_SPI1.POPR) // DSPI POP RX FIFO Register 43 | 44 | #endif 45 | 46 | // Forward declarations 47 | void rearrangeBufferValues (sumpSetupVariableStruct &sv, 48 | sumpDynamicVariableStruct &dynamic, 49 | bool adjustChan1Left); 50 | inline void startSPIClock (bool multipleChannels, 51 | uint32_t cpuClockTicks); 52 | 53 | 54 | // 55 | // Include record_hadware.h multiple times with different defines 56 | // 57 | 58 | // single channel 59 | #include "record_hardware.h" 60 | 61 | // single channel with trigger 62 | #define USE_TRIGGER 1 63 | #include "record_hardware.h" 64 | #undef USE_TRIGGER 65 | 66 | // single channel with pre-trigger 67 | #define USE_PRE_TRIGGER 1 68 | #include "record_hardware.h" 69 | #undef USE_PRE_TRIGGER 70 | 71 | // multiple channels 72 | #define MULTIPLE_CHANNELS 1 73 | #include "record_hardware.h" 74 | 75 | // multiple channels with trigger 76 | #define USE_TRIGGER 1 77 | #include "record_hardware.h" 78 | #undef USE_TRIGGER 79 | 80 | // multiple channels with pre-trigger 81 | #define USE_PRE_TRIGGER 1 82 | #include "record_hardware.h" 83 | #undef USE_PRE_TRIGGER 84 | 85 | #undef MULTIPLE_CHANNELS 86 | 87 | 88 | void recordSPIData (sumpSetupVariableStruct &sv, 89 | sumpDynamicVariableStruct &dynamic) 90 | { 91 | bool adjustChan1Left = false; 92 | bool multipleChannels = (sv.numberOfChannels > 1); 93 | 94 | if (multipleChannels) 95 | { 96 | if (sv.triggerMask[0]) 97 | { 98 | #if Teensy_LC 99 | // 12 MHz - can't support full triggering 100 | if (sv.cpuClockTicks <= 4) 101 | { 102 | recordSPIData_MultiChannel_Pretrigger (sv, dynamic); 103 | } 104 | else 105 | #endif 106 | { 107 | recordSPIData_MultiChannel_Trigger (sv, dynamic); 108 | } 109 | } 110 | else 111 | { 112 | recordSPIData_MultiChannel (sv, dynamic); 113 | } 114 | } 115 | else 116 | { 117 | if (sv.triggerMask[0]) 118 | { 119 | #if Teensy_LC 120 | // 24 MHz - can't support full triggering 121 | if (sv.cpuClockTicks <= 2) 122 | { 123 | recordSPIData_SingleChannel_Pretrigger (sv, dynamic); 124 | } 125 | else 126 | #endif 127 | { 128 | recordSPIData_SingleChannel_Trigger (sv, dynamic); 129 | } 130 | } 131 | else 132 | { 133 | recordSPIData_SingleChannel (sv, dynamic); 134 | } 135 | } 136 | 137 | if (multipleChannels) 138 | { 139 | // offset between channels is most likely to occur at higher speeds. An 140 | // attempt is made to compensate in startSPIClock and this then 141 | // removes that compensation 142 | if (sv.cpuClockTicks <= 16) 143 | { 144 | adjustChan1Left = true; 145 | } 146 | 147 | // adjust data from how SPI stores it to how send_data expects it 148 | rearrangeBufferValues (sv, dynamic, adjustChan1Left); 149 | } 150 | } 151 | 152 | void rearrangeBufferValues (sumpSetupVariableStruct &sv, 153 | sumpDynamicVariableStruct &dynamic, 154 | bool adjustChan1Left) 155 | { 156 | if (sv.numberOfChannels > 1) 157 | { 158 | uint32_t *inputPtr = sv.startOfBuffer; 159 | uint32_t *outputPtr = sv.startOfBuffer; 160 | uint32_t chan0Values; 161 | uint32_t chan1Values; 162 | uint32_t firstMSB; 163 | uint32_t newValues = 0; 164 | uint32_t newValuesPart1 = 0; 165 | uint32_t mask; 166 | int shiftIndex; 167 | 168 | firstMSB = *(inputPtr + 1) >> 31; 169 | 170 | while (outputPtr < sv.endOfBuffer) 171 | { 172 | // fix the data to alternate each sample 173 | chan0Values = *inputPtr; 174 | ++inputPtr; 175 | chan1Values = *inputPtr; 176 | ++inputPtr; 177 | 178 | newValues = 0; 179 | mask = 0x80000000; 180 | shiftIndex = 30; 181 | 182 | for (int index = 31; index >= 0; --index) 183 | { 184 | newValues += ((chan0Values & mask) >> index) << (shiftIndex); 185 | 186 | // if recorded at same time 187 | if (!adjustChan1Left) 188 | { 189 | newValues += ((chan1Values & mask) >> index) << (shiftIndex + 1); 190 | } 191 | else 192 | { 193 | // if not first bit 194 | if (index > 0) 195 | { 196 | // shift channel 1 to the left 1 bit 197 | newValues += (((chan1Values << 1) & mask) >> index ) << (shiftIndex + 1); 198 | } 199 | else 200 | { 201 | // if not last value in buffer 202 | if (inputPtr < sv.endOfBuffer) 203 | { 204 | // LSB is MSB of next value 205 | chan1Values = *(inputPtr + 1); 206 | newValues += ((chan1Values & 0x80000000) >> 31) << 1; 207 | 208 | } 209 | else // last value in buffer - use MSB of first value 210 | { 211 | newValues += firstMSB << 1; 212 | } 213 | } 214 | } 215 | 216 | mask = mask >> 1; 217 | shiftIndex = shiftIndex - 2; 218 | 219 | // if end of part 1 220 | if (index == 16) { 221 | // save value and restart for next 32 bits of output 222 | newValuesPart1 = newValues; 223 | newValues = 0; 224 | shiftIndex = 30; 225 | } 226 | } 227 | 228 | *outputPtr = newValuesPart1; 229 | ++outputPtr; 230 | *outputPtr = newValues; 231 | ++outputPtr; 232 | } 233 | } 234 | } 235 | 236 | 237 | #if Teensy_LC 238 | 239 | // SPI register values 240 | // SPI master, system enable, clock phase (to get continuous clock) 241 | const uint8_t SPI_C1 = SPI_C1_MSTR | SPI_C1_SPE | SPI_C1_CPHA; 242 | // 16 bit mode 243 | const uint8_t SPI_C2 = SPI_C2_SPIMODE; 244 | // FIFO mode 245 | const uint8_t SPI_C3 = SPI_C3_FIFOMODE; 246 | 247 | 248 | void spi0Initialize() 249 | { 250 | // turn on SPI module clock 251 | SIM_SCGC4 |= SIM_SCGC4_SPI0; 252 | SPI0_C1 = SPI_C1; 253 | SPI0_C2 = SPI_C2; 254 | 255 | // MISO is only SPI signal needed. 256 | if (CHAN1 == 12) { 257 | CORE_PIN12_CONFIG = PORT_PCR_MUX(2); // MISO0 = 12 (PTC7) 258 | } else { 259 | CORE_PIN8_CONFIG = PORT_PCR_MUX(2); // MISO0 = 8 (PTD3) 260 | } 261 | 262 | #if SPI_DEBUG 263 | if (1) { 264 | CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); // MOSI0 = 11 (PTC6) 265 | } else { 266 | CORE_PIN7_CONFIG = PORT_PCR_MUX(2); // MOSI0 = 7 (PTD2) 267 | } 268 | 269 | if (0) { 270 | CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); // SCK0 = 13 (PTC5) 271 | } else { 272 | CORE_PIN14_CONFIG = PORT_PCR_MUX(2); // SCK0 = 14 (PTD1) 273 | } 274 | #endif 275 | 276 | } 277 | 278 | void spi1Initialize() 279 | { 280 | // turn on SPI module clock 281 | SIM_SCGC4 |= SIM_SCGC4_SPI1; 282 | SPI1_C1 = SPI_C1; 283 | SPI1_C2 = SPI_C2; 284 | 285 | // MISO is only SPI signal needed. 286 | if (CHAN0 == 1) { 287 | CORE_PIN1_CONFIG = PORT_PCR_MUX(2); // MISO1 = 1 (PTB17) 288 | } else { 289 | CORE_PIN5_CONFIG = PORT_PCR_MUX(2); // MISO1 = 5 (PTD7) 290 | } 291 | 292 | #if SPI_DEBUG 293 | if (0) { 294 | CORE_PIN0_CONFIG = PORT_PCR_MUX(2); // MOSI1 = 0 (PTB16) 295 | } else { 296 | CORE_PIN21_CONFIG = PORT_PCR_MUX(2); // MOSI1 = 21 (PTD6) 297 | } 298 | 299 | CORE_PIN20_CONFIG = PORT_PCR_MUX(2); // SCK1 = 20 (PTD5) 300 | #endif 301 | 302 | } 303 | 304 | static uint32_t spiGetClockSetting (uint32_t clock, uint32_t baseFrequency) { 305 | 306 | uint32_t br; 307 | 308 | if (clock >= baseFrequency / 2) { br = SPI_BR_SPPR(0) | SPI_BR_SPR(0); 309 | } else if (clock >= baseFrequency / 4) { br = SPI_BR_SPPR(1) | SPI_BR_SPR(0); 310 | } else if (clock >= baseFrequency / 6) { br = SPI_BR_SPPR(2) | SPI_BR_SPR(0); 311 | } else if (clock >= baseFrequency / 8) { br = SPI_BR_SPPR(3) | SPI_BR_SPR(0); 312 | } else if (clock >= baseFrequency / 10) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(0); 313 | } else if (clock >= baseFrequency / 12) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(0); 314 | } else if (clock >= baseFrequency / 14) { br = SPI_BR_SPPR(6) | SPI_BR_SPR(0); 315 | } else if (clock >= baseFrequency / 16) { br = SPI_BR_SPPR(7) | SPI_BR_SPR(0); 316 | } else if (clock >= baseFrequency / 20) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(1); 317 | } else if (clock >= baseFrequency / 24) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(1); 318 | } else if (clock >= baseFrequency / 28) { br = SPI_BR_SPPR(6) | SPI_BR_SPR(1); 319 | } else if (clock >= baseFrequency / 32) { br = SPI_BR_SPPR(7) | SPI_BR_SPR(1); 320 | } else if (clock >= baseFrequency / 40) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(2); 321 | } else if (clock >= baseFrequency / 48) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(2); 322 | } else if (clock >= baseFrequency / 56) { br = SPI_BR_SPPR(6) | SPI_BR_SPR(2); 323 | } else if (clock >= baseFrequency / 64) { br = SPI_BR_SPPR(7) | SPI_BR_SPR(2); 324 | } else if (clock >= baseFrequency / 80) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(3); 325 | } else if (clock >= baseFrequency / 96) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(3); 326 | } else if (clock >= baseFrequency / 112) { br = SPI_BR_SPPR(6) | SPI_BR_SPR(3); 327 | } else if (clock >= baseFrequency / 128) { br = SPI_BR_SPPR(7) | SPI_BR_SPR(3); 328 | } else if (clock >= baseFrequency / 160) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(4); 329 | } else if (clock >= baseFrequency / 192) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(4); 330 | } else if (clock >= baseFrequency / 224) { br = SPI_BR_SPPR(6) | SPI_BR_SPR(4); 331 | } else if (clock >= baseFrequency / 256) { br = SPI_BR_SPPR(7) | SPI_BR_SPR(4); 332 | } else if (clock >= baseFrequency / 320) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(5); 333 | } else if (clock >= baseFrequency / 384) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(5); 334 | } else if (clock >= baseFrequency / 448) { br = SPI_BR_SPPR(6) | SPI_BR_SPR(5); 335 | } else if (clock >= baseFrequency / 512) { br = SPI_BR_SPPR(7) | SPI_BR_SPR(5); 336 | } else if (clock >= baseFrequency / 640) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(6); 337 | } else if (clock >= baseFrequency / 768) { br = SPI_BR_SPPR(5) | SPI_BR_SPR(6); 338 | } else if (clock >= baseFrequency /1280) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(7); 339 | } else if (clock >= baseFrequency /2560) { br = SPI_BR_SPPR(4) | SPI_BR_SPR(8); 340 | } else /* baseFrequency / 4096 */ { br = SPI_BR_SPPR(7) | SPI_BR_SPR(8); 341 | } 342 | 343 | return br; 344 | } 345 | 346 | void spiDisable (bool multipleChannels) 347 | { 348 | SPI1_C1 = 0; 349 | 350 | if (multipleChannels) 351 | { 352 | SPI0_C1 = 0; 353 | } 354 | } 355 | 356 | void spi0Setup(uint32_t clock) 357 | { 358 | // set baud rate 359 | SPI0_BR = spiGetClockSetting (clock, F_BUS); 360 | 361 | // read status register to clear flags 362 | SPI0_S; 363 | } 364 | 365 | void spi1Setup(uint32_t clock) 366 | { 367 | // set baud rate 368 | SPI1_BR = spiGetClockSetting (clock, F_PLL/2); 369 | 370 | // read status register to clear flags 371 | SPI1_S; 372 | } 373 | 374 | inline void spi0StartTransfer () 375 | { 376 | SPI0_DL = 0; 377 | SPI0_DH = 0; 378 | } 379 | 380 | inline void spi1StartTransfer () 381 | { 382 | SPI1_DL = 0; 383 | SPI1_DH = 0; 384 | } 385 | 386 | inline void startSPIClock (bool multipleChannels, 387 | uint32_t cpuClockTicks) 388 | { 389 | volatile uint8_t *SPI0_DH_reg = &SPI0_DH; 390 | volatile uint8_t *SPI1_DH_reg = &SPI1_DH; 391 | uint32_t SPI_DH_value = 0; 392 | 393 | //digitalWriteFast (TIMING_PIN_0, LOW); 394 | if (multipleChannels) 395 | { 396 | // start channel 1 (SPI0) before channel 0 (SPI1) since channel 0 is 397 | // used to tell if done (0 will finish first if it is started first) 398 | SPI1_DL = 0; 399 | SPI0_DL = 0; 400 | 401 | if (cpuClockTicks <= 4) 402 | { 403 | // start the SPI channels 4 ticks apart, so they are exactly 404 | // 1 sample apart. This will be corrected in rearrangeBufferValues 405 | // after recording is complete. 406 | asm volatile (".align 2\n\t" 407 | "strb %[SPI_DH_value], [%[SPI0_DH_reg]]\n\t" 408 | "nop\n\t" 409 | "nop\n\t" 410 | "strb %[SPI_DH_value], [%[SPI1_DH_reg]]\n\t" 411 | : [SPI0_DH_reg] "+r" (SPI0_DH_reg), 412 | [SPI1_DH_reg] "+r" (SPI1_DH_reg), 413 | [SPI_DH_value] "+r" (SPI_DH_value) 414 | :: "cc"); 415 | } 416 | else if (cpuClockTicks <= 8) 417 | { 418 | // start the SPI channels 8 ticks apart, so they are exactly 419 | // 1 sample apart. This will be corrected in rearrangeBufferValues 420 | // after recording is complete. 421 | asm volatile (".align 2\n\t" 422 | "strb %[SPI_DH_value], [%[SPI0_DH_reg]]\n\t" 423 | "nop\n\t" 424 | "nop\n\t" 425 | "nop\n\t" 426 | "nop\n\t" 427 | "nop\n\t" 428 | "nop\n\t" 429 | "strb %[SPI_DH_value], [%[SPI1_DH_reg]]\n\t" 430 | : [SPI0_DH_reg] "+r" (SPI0_DH_reg), 431 | [SPI1_DH_reg] "+r" (SPI1_DH_reg), 432 | [SPI_DH_value] "+r" (SPI_DH_value) 433 | :: "cc"); 434 | } 435 | else if (cpuClockTicks <= 16) 436 | { 437 | // start the SPI channels 16 ticks apart, so they are exactly 438 | // 1 sample apart. This will be corrected in rearrangeBufferValues 439 | // after recording is complete. 440 | asm volatile (".align 2\n\t" 441 | "strb %[SPI_DH_value], [%[SPI0_DH_reg]]\n\t" 442 | "nop\n\t" 443 | "nop\n\t" 444 | "nop\n\t" 445 | "nop\n\t" 446 | "nop\n\t" 447 | "nop\n\t" 448 | "nop\n\t" 449 | "nop\n\t" 450 | "nop\n\t" 451 | "nop\n\t" 452 | "nop\n\t" 453 | "nop\n\t" 454 | "nop\n\t" 455 | "nop\n\t" 456 | "strb %[SPI_DH_value], [%[SPI1_DH_reg]]\n\t" 457 | : [SPI0_DH_reg] "+r" (SPI0_DH_reg), 458 | [SPI1_DH_reg] "+r" (SPI1_DH_reg), 459 | [SPI_DH_value] "+r" (SPI_DH_value) 460 | :: "cc"); 461 | } 462 | else 463 | { 464 | SPI0_DH = 0; 465 | SPI1_DH = 0; 466 | } 467 | } 468 | else 469 | { 470 | spi1StartTransfer (); 471 | } 472 | 473 | //digitalWriteFast (TIMING_PIN_0, HIGH); 474 | // wait for transmit buffer to be ready for next transfer 475 | while (!(SPI1_S & SPI_S_SPTEF)) ; // wait 476 | //digitalWriteFast (TIMING_PIN_0, LOW); 477 | // No FIFO: SPTEF, FIFO: not needed. don't start next transfer yet 478 | 479 | // start a 2nd transfer while waiting for first to complete 480 | spi1StartTransfer (); 481 | 482 | if (multipleChannels) 483 | { 484 | // clear status so more data can be sent 485 | SPI0_S; 486 | 487 | // start a 2nd transfer while waiting for first to complete 488 | spi0StartTransfer (); 489 | } 490 | } 491 | 492 | 493 | #else // 3.x 494 | 495 | uint32_t prev_SIM_CLKDIV1; 496 | int prevFBUS; 497 | 498 | void spi0Initialize() 499 | { 500 | // turn on SPI module clocks 501 | SIM_SCGC6 |= SIM_SCGC6_SPI0 + SIM_SCGC6_SPI1; 502 | 503 | // MISO can be pin 12 or pin 8 504 | if (CHAN1 == 12) { 505 | CORE_PIN12_CONFIG = PORT_PCR_MUX(2); // DIN/MISO = 12 (PTC7) 506 | } else { 507 | CORE_PIN8_CONFIG = PORT_PCR_MUX(2); // DIN/MISO = 8 (PTD3) 508 | } 509 | 510 | #if SPI_DEBUG 511 | CORE_PIN11_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); // DOUT/MOSI = 11 (PTC6) 512 | 513 | if (0) { 514 | CORE_PIN13_CONFIG = PORT_PCR_DSE | PORT_PCR_MUX(2); // SCK = 13 (PTC5) 515 | } else { 516 | CORE_PIN14_CONFIG = PORT_PCR_MUX(2); // SCK = 14 (PTD1) 517 | } 518 | #endif 519 | 520 | } 521 | 522 | void spi1Initialize() 523 | { 524 | // turn on SPI module clocks 525 | SIM_SCGC6 |= SIM_SCGC6_SPI0 + SIM_SCGC6_SPI1; 526 | 527 | // MISO can be pin 1 or pin 26 528 | if (CHAN0 == 1) { 529 | CORE_PIN1_CONFIG = PORT_PCR_MUX(2); // DIN/MISO = 40 () 530 | 531 | // move UART TX to pin 5 532 | CORE_PIN5_CONFIG = PORT_PCR_MUX(3); // UART0_TX = 64 () 533 | 534 | } else { 535 | CORE_PIN26_CONFIG = PORT_PCR_MUX(2); // DIN/MISO = 2 () 536 | } 537 | 538 | #if SPI_DEBUG 539 | CORE_PIN0_CONFIG = PORT_PCR_MUX(2); // MOSI 540 | #endif 541 | 542 | } 543 | 544 | void spi0Setup(uint32_t clock) 545 | { 546 | SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; 547 | 548 | // set control register for correct clock setting and 16 bit transfers 549 | SPI0_CTAR0 = spiGetClockSetting (clock) | SPI_CTAR_FMSZ(15); //// | SPI_CTAR_LSBFE; 550 | 551 | // clear Rx FIFO not empty flag, Tx FIFO Underflow, RxFIFO Overflow 552 | SPI0_SR = SPI_SR_RFDF | SPI_SR_TFUF | SPI_SR_RFOF; 553 | } 554 | 555 | void spi1Setup(uint32_t clock) 556 | { 557 | // if F_BUS is adjusted, need to adjust this also to compensate 558 | uint32_t adjustedClock = clock; 559 | int newFBUS = getCurrentFBUS (); 560 | 561 | // save original value 562 | prev_SIM_CLKDIV1 = SIM_CLKDIV1; 563 | prevFBUS = getCurrentFBUS (); 564 | 565 | // check for need for F_BUS speed increase 566 | if (F_CPU < 150000000) 567 | { 568 | if (clock >= F_BUS) 569 | { 570 | // set F_BUS equal to F_CPU 571 | SIM_CLKDIV1 = (SIM_CLKDIV1 & ~SIM_CLKDIV1_OUTDIV2(0x0F)) | SIM_CLKDIV1_OUTDIV2(0); 572 | 573 | newFBUS = F_CPU; 574 | 575 | // set F_MEM to F_CPU / 4 (only affects 120 and 144 MHz F_CPU) 576 | SIM_CLKDIV1 = (SIM_CLKDIV1 & ~SIM_CLKDIV1_OUTDIV4(0x0F)) | SIM_CLKDIV1_OUTDIV4(3); 577 | } else { 578 | // set F_BUS equal to F_CPU / 2 (only affects 144 MHz F_CPU) 579 | SIM_CLKDIV1 = (SIM_CLKDIV1 & ~SIM_CLKDIV1_OUTDIV2(0x0F)) | SIM_CLKDIV1_OUTDIV2(1); 580 | 581 | newFBUS = F_CPU / 2; 582 | 583 | if (F_CPU == 144000000) { 584 | adjustedClock = adjustedClock * 2 / 3; 585 | } 586 | } 587 | } 588 | // F_CPU > 150 MHz 589 | else if (clock >= F_BUS * 2) 590 | { 591 | // set F_BUS equal to F_CPU 592 | SIM_CLKDIV1 = (SIM_CLKDIV1 & ~SIM_CLKDIV1_OUTDIV2(0x0F)) | SIM_CLKDIV1_OUTDIV2(0); 593 | 594 | newFBUS = F_CPU; 595 | } 596 | // F_CPU > 150 MHz 597 | else 598 | { 599 | // set F_BUS equal to F_CPU / 2 600 | SIM_CLKDIV1 = (SIM_CLKDIV1 & ~SIM_CLKDIV1_OUTDIV2(0x0F)) | SIM_CLKDIV1_OUTDIV2(1); 601 | 602 | newFBUS = F_CPU / 2; 603 | adjustedClock = adjustedClock / 2; 604 | } 605 | 606 | SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; 607 | 608 | // set control register for correct clock setting and 16 bit transfers 609 | SPI1_CTAR0 = spiGetClockSetting (adjustedClock) | SPI_CTAR_FMSZ(15); //// | SPI_CTAR_LSBFE; 610 | 611 | // clear Rx FIFO not empty flag, Tx FIFO Underflow, RxFIFO Overflow 612 | SPI1_SR = SPI_SR_RFDF | SPI_SR_TFUF | SPI_SR_RFOF; 613 | 614 | setupTestFrequencies (newFBUS); 615 | } 616 | 617 | static uint32_t spiGetClockSetting (uint32_t clock) { 618 | 619 | uint32_t t; 620 | 621 | if (clock >= F_BUS / 2) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0); 622 | } else if (clock >= F_BUS / 3) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0); 623 | } else if (clock >= F_BUS / 4) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); 624 | } else if (clock >= F_BUS / 5) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_DBR | SPI_CTAR_CSSCK(0); 625 | } else if (clock >= F_BUS / 6) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); 626 | } else if (clock >= F_BUS / 8) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); 627 | } else if (clock >= F_BUS / 10) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0); 628 | } else if (clock >= F_BUS / 12) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1); 629 | } else if (clock >= F_BUS / 16) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); 630 | } else if (clock >= F_BUS / 20) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(0); 631 | } else if (clock >= F_BUS / 24) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); 632 | } else if (clock >= F_BUS / 32) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3); 633 | } else if (clock >= F_BUS / 40) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); 634 | } else if (clock >= F_BUS / 48) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3); 635 | } else if (clock >= F_BUS / 56) { t = SPI_CTAR_PBR(3) | SPI_CTAR_BR(3) | SPI_CTAR_CSSCK(2); 636 | } else if (clock >= F_BUS / 64) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4); 637 | } else if (clock >= F_BUS / 80) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(4) | SPI_CTAR_CSSCK(3); 638 | } else if (clock >= F_BUS / 96) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4); 639 | } else if (clock >= F_BUS / 128) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5); 640 | } else if (clock >= F_BUS / 160) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(5) | SPI_CTAR_CSSCK(4); 641 | } else if (clock >= F_BUS / 192) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5); 642 | } else if (clock >= F_BUS / 256) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6); 643 | } else if (clock >= F_BUS / 320) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(6) | SPI_CTAR_CSSCK(5); 644 | } else if (clock >= F_BUS / 384) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6); 645 | } else if (clock >= F_BUS / 512) { t = SPI_CTAR_PBR(0) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7); 646 | } else if (clock >= F_BUS / 640) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(7) | SPI_CTAR_CSSCK(6); 647 | } else if (clock >= F_BUS / 768) { t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(7); 648 | } else if (clock >= F_BUS /1280) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(8) | SPI_CTAR_CSSCK(6); 649 | } else if (clock >= F_BUS /2560) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(9) | SPI_CTAR_CSSCK(8); 650 | } else if (clock >= F_BUS /5120) { t = SPI_CTAR_PBR(2) | SPI_CTAR_BR(10)| SPI_CTAR_CSSCK(9); 651 | } else { /* F_BUS / 10240 */ t = SPI_CTAR_PBR(1) | SPI_CTAR_BR(11)| SPI_CTAR_CSSCK(10); 652 | } 653 | 654 | return t; 655 | } 656 | 657 | void spiDisable (bool multipleChannels) 658 | { 659 | // wait for data to finish transmitting 660 | delay (10); 661 | 662 | SPI1_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); 663 | 664 | if (multipleChannels) { 665 | SPI0_MCR = SPI_MCR_MDIS | SPI_MCR_HALT | SPI_MCR_PCSIS(0x1F); 666 | } 667 | 668 | // turn off SPI module clocks 669 | SIM_SCGC6 &= ~(SIM_SCGC6_SPI0 + SIM_SCGC6_SPI1); 670 | 671 | // restore original value 672 | SIM_CLKDIV1 = prev_SIM_CLKDIV1; 673 | 674 | setupTestFrequencies (prevFBUS); 675 | } 676 | 677 | inline void startSPIClock (bool multipleChannels, 678 | uint32_t cpuClockTicks) 679 | { 680 | // ensure that FIFO's are empty (had a problem with this) 681 | SPI1_POPR; 682 | SPI1_POPR; 683 | SPI1_POPR; 684 | SPI1_POPR; 685 | SPI1_POPR; 686 | 687 | if (multipleChannels) { 688 | SPI0_POPR; 689 | SPI0_POPR; 690 | SPI0_POPR; 691 | SPI0_POPR; 692 | SPI0_POPR; 693 | 694 | // start channel 1 (SPI0) before channel 0 (SPI1) since channel 0 is 695 | // used to tell if done (0 will finish first if it is started first) 696 | 697 | volatile uint32_t *SPI0_PUSHR_reg = &SPI0_PUSHR; 698 | volatile uint32_t *SPI1_PUSHR_reg = &SPI1_PUSHR; 699 | uint32_t SPI_PUSHR_value = SPI_PUSHR_CONT; 700 | 701 | volatile uint32_t *SPI0_MCR_reg = &SPI0_MCR; 702 | volatile uint32_t *SPI1_MCR_reg = &SPI1_MCR; 703 | uint32_t SPI_MCR_value = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CONT_SCKE | SPI_MCR_DIS_TXF | SPI_MCR_DIS_RXF | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; 704 | 705 | if (cpuClockTicks <= 2) { 706 | // start the SPI channels 2 ticks apart, so they are exactly 707 | // 1 sample apart. This will be corrected in rearrangeBufferValues 708 | // after recording is complete. 709 | asm volatile (".align 2\n\t" 710 | "str %[SPI_MCR_value], [%[SPI0_MCR_reg]]\n\t" 711 | "str %[SPI_MCR_value], [%[SPI1_MCR_reg]]\n\t" 712 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 713 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 714 | 715 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 716 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 717 | : [SPI0_PUSHR_reg] "+r" (SPI0_PUSHR_reg), 718 | [SPI1_PUSHR_reg] "+r" (SPI1_PUSHR_reg), 719 | [SPI_PUSHR_value] "+r" (SPI_PUSHR_value), 720 | [SPI0_MCR_reg] "+r" (SPI0_MCR_reg), 721 | [SPI1_MCR_reg] "+r" (SPI1_MCR_reg), 722 | [SPI_MCR_value] "+r" (SPI_MCR_value) 723 | :: "cc"); 724 | } else if (cpuClockTicks <= 4) { 725 | // start the SPI channels 4 ticks apart, so they are exactly 726 | // 1 sample apart. This will be corrected in rearrangeBufferValues 727 | // after recording is complete. 728 | asm volatile (".align 2\n\t" 729 | "str %[SPI_MCR_value], [%[SPI0_MCR_reg]]\n\t" 730 | "nop\n\t" 731 | "nop\n\t" 732 | "str %[SPI_MCR_value], [%[SPI1_MCR_reg]]\n\t" 733 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 734 | "nop\n\t" 735 | "nop\n\t" 736 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 737 | 738 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 739 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 740 | : [SPI0_PUSHR_reg] "+r" (SPI0_PUSHR_reg), 741 | [SPI1_PUSHR_reg] "+r" (SPI1_PUSHR_reg), 742 | [SPI_PUSHR_value] "+r" (SPI_PUSHR_value), 743 | [SPI0_MCR_reg] "+r" (SPI0_MCR_reg), 744 | [SPI1_MCR_reg] "+r" (SPI1_MCR_reg), 745 | [SPI_MCR_value] "+r" (SPI_MCR_value) 746 | :: "cc"); 747 | } else if (cpuClockTicks <= 8) { 748 | // start the SPI channels 8 ticks apart, so they are exactly 749 | // 1 sample apart. This will be corrected in rearrangeBufferValues 750 | // after recording is complete. 751 | asm volatile (".align 2\n\t" 752 | "str %[SPI_MCR_value], [%[SPI0_MCR_reg]]\n\t" 753 | "nop\n\t" 754 | "nop\n\t" 755 | "nop\n\t" 756 | "nop\n\t" 757 | "nop\n\t" 758 | "nop\n\t" 759 | "str %[SPI_MCR_value], [%[SPI1_MCR_reg]]\n\t" 760 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 761 | "nop\n\t" 762 | "nop\n\t" 763 | "nop\n\t" 764 | "nop\n\t" 765 | "nop\n\t" 766 | "nop\n\t" 767 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 768 | 769 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 770 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 771 | : [SPI0_PUSHR_reg] "+r" (SPI0_PUSHR_reg), 772 | [SPI1_PUSHR_reg] "+r" (SPI1_PUSHR_reg), 773 | [SPI_PUSHR_value] "+r" (SPI_PUSHR_value), 774 | [SPI0_MCR_reg] "+r" (SPI0_MCR_reg), 775 | [SPI1_MCR_reg] "+r" (SPI1_MCR_reg), 776 | [SPI_MCR_value] "+r" (SPI_MCR_value) 777 | :: "cc"); 778 | } else if (cpuClockTicks <= 12) { 779 | // start the SPI channels 12 ticks apart, so they are exactly 780 | // 1 sample apart. This will be corrected in rearrangeBufferValues 781 | // after recording is complete. 782 | asm volatile (".align 2\n\t" 783 | "str %[SPI_MCR_value], [%[SPI0_MCR_reg]]\n\t" 784 | "nop\n\t" 785 | "nop\n\t" 786 | "nop\n\t" 787 | "nop\n\t" 788 | "nop\n\t" 789 | "nop\n\t" 790 | "nop\n\t" 791 | "nop\n\t" 792 | "nop\n\t" 793 | "nop\n\t" 794 | "str %[SPI_MCR_value], [%[SPI1_MCR_reg]]\n\t" 795 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 796 | "nop\n\t" 797 | "nop\n\t" 798 | "nop\n\t" 799 | "nop\n\t" 800 | "nop\n\t" 801 | "nop\n\t" 802 | "nop\n\t" 803 | "nop\n\t" 804 | "nop\n\t" 805 | "nop\n\t" 806 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 807 | 808 | "str %[SPI_PUSHR_value], [%[SPI0_PUSHR_reg]]\n\t" 809 | "str %[SPI_PUSHR_value], [%[SPI1_PUSHR_reg]]\n\t" 810 | : [SPI0_PUSHR_reg] "+r" (SPI0_PUSHR_reg), 811 | [SPI1_PUSHR_reg] "+r" (SPI1_PUSHR_reg), 812 | [SPI_PUSHR_value] "+r" (SPI_PUSHR_value), 813 | [SPI0_MCR_reg] "+r" (SPI0_MCR_reg), 814 | [SPI1_MCR_reg] "+r" (SPI1_MCR_reg), 815 | [SPI_MCR_value] "+r" (SPI_MCR_value) 816 | :: "cc"); 817 | } else { 818 | SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; 819 | SPI1_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; 820 | 821 | // clear FIFO 822 | SPI1_POPR; 823 | SPI1_POPR; 824 | SPI1_POPR; 825 | SPI1_POPR; 826 | SPI1_POPR; 827 | SPI0_POPR; 828 | SPI0_POPR; 829 | SPI0_POPR; 830 | SPI0_POPR; 831 | SPI0_POPR; 832 | 833 | SPI0_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CONT_SCKE; 834 | SPI1_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CONT_SCKE; 835 | 836 | SPI0_PUSHR = SPI_PUSHR_CONT; 837 | SPI0_PUSHR = SPI_PUSHR_CONT; 838 | SPI1_PUSHR = SPI_PUSHR_CONT; 839 | SPI1_PUSHR = SPI_PUSHR_CONT; 840 | } 841 | 842 | // continue to fill FIFO's 843 | SPI0_PUSHR = SPI_PUSHR_CONT; 844 | SPI0_PUSHR = SPI_PUSHR_CONT; 845 | } else { 846 | SPI1_MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CONT_SCKE; 847 | 848 | // start 849 | SPI1_PUSHR = SPI_PUSHR_CONT; 850 | SPI1_PUSHR = SPI_PUSHR_CONT; 851 | } 852 | 853 | // continue to fill FIFO's 854 | SPI1_PUSHR = SPI_PUSHR_CONT; 855 | SPI1_PUSHR = SPI_PUSHR_CONT; 856 | } 857 | 858 | #endif 859 | 860 | #endif 861 | --------------------------------------------------------------------------------