diff --git a/Function Generator/FunctionGenerator.cpp b/Function Generator/FunctionGenerator.cpp index 3d9572f..4688d24 100644 --- a/Function Generator/FunctionGenerator.cpp +++ b/Function Generator/FunctionGenerator.cpp @@ -9,91 +9,110 @@ #include "blink.pio.h" #include "DAC.pio.h" -///////////////////////////// -// Define GPIO connections... -///////////////////////////// +////////////////////////////////////// +// Define GPIO connections for Pico... +////////////////////////////////////// // Note: The SPI Port only works through specific pins, so this port is defined first. -// SPI Port connections... // ┌──────────┬───────────────┬─────────────┐──────────────┐ - // │ PGA2040 │ Connection │ MCP41010 │ Nixie module │ - // ├──────────┼───────────────┼─────────────┤──────────────┤ -#define PIN_RX 16 // │ GPIO 16 │ RX/spi1_rx │ │ - │ -//#define PIN_CS 17 // │ GPIO 17 │ CS/spi1_cs │ │ │ can this be re-defined ? -#define PIN_CLK 18 // │ GPIO 18 │ CLK/spi1_clk │ │ SCK (blue) │ -#define PIN_TX 19 // │ GPIO 19 │ TX/spi1_tx │ │ SDI (green) │ -#define Nixie_CS 21 // │ GPIO 21 │ Chip select │ │ SS1 (white) │ - // └──────────┴───────────────┴─────────────┘──────────────┘ +// SPI Port connections... // ┌──────────┬───────────────┬─────────────┐────────────────┐ + // │ PGA2040 │ Connection │ MCP41010 │ Display module │ + // ├──────────┼───────────────┼─────────────┤────────────────┤ +/* #define PIN_RX 16 // │ GPIO 16 │ RX/spi1_rx │ │ - │ +//#define PIN_CS 17 // │ GPIO 17 │ CS/spi1_cs │ │ │ can this be re-defined ? +#define PIN_CLK 18 // │ GPIO 18 │ CLK/spi1_clk │ │ SCK (blue) │ +#define PIN_TX 19 // │ GPIO 19 │ TX/spi1_tx │ │ SDI (green) │ +#define Display_CS 21 // │ GPIO 21 │ Chip select │ │ SS1 (white) │ */ + +#define PIN_RX 4 // │ GPIO 4 │ RX/spi1_rx │ │ - │ +//#define PIN_CS 17 // │ GPIO 17 │ CS/spi1_cs │ │ │ can this be re-defined ? +#define PIN_CLK 2 // │ GPIO 2 │ CLK/spi1_clk │ │ SCK (blue) │ +#define PIN_TX 3 // │ GPIO 3 │ TX/spi1_tx │ │ SDI (green) │ +#define Display_CS 6 // │ GPIO 6 │ Chip select │ │ SS1 (white) │ + // └──────────┴───────────────┴─────────────┘────────────────┘ #define SPI_PORT spi0 // These SPI connections require the use of RP2040 SPI port 0 #define _A 0 // DAC channel alias #define _B 1 #define _Up 1 #define _Down -1 -#define LED 20 // GPIO connected to LED -#define BitMapSize 256 // Match X to Y resolution #define _Sine_ 0 // Permited values for variable WaveForm_Type #define _Square_ 1 #define _Triangle_ 2 -#define _GPIO_ 0 -#define _PIO_ 1 -#define _BM_start_ 2 -#define _SM_ 3 -#define _SM_codeBot_ 4 -#define _SM_codeTop_ 5 #define _DMA_ctrl_ 6 #define _DMA_data_ 7 #define _Funct_ 8 #define _Phase_ 9 #define _Freq_ 10 -#define _Range_ 11 -//#define _DutyC_ 12 -#define _DAC_div_ 13 +#define _Duty_ 11 +#define _Range_ 12 #define eof 255 // EOF in stdio.h -is -1, but getchar returns int 255 to avoid blocking +#define CR 13 +#define BitMapSize 256 // Match X to Y resolution //#define BitMapSize 360 // won't work - DMA needs to operate as a power of 2 unsigned short DAC_channel_mask = 0 ; // Binary mask to simultaneously start all DMA channels const uint32_t transfer_count = BitMapSize ; // Number of DMA transfers per event -int WaveForm_Type; -const uint startLineLength = 8; // the linebuffer will automatically grow for longer lines -int ParmCnt = 0, Parm[4] ; // Storage for 4 command line parameters -int SelectedChan, c, i = 0, dirn = 1 ; -char LastCmd[30]; // TBD - check required size +int ParmCnt = 0, Parm[4], WaveForm_Type ; // Storage for 4 command line parameters +int SelectedChan, c, i = 0, dirn = 1, result ; +char inStr[30], outStr[2048], LastCmd[30] ; // outStr large enough to contain the HelpText string const char * HelpText = "\tUsage...\n" "\t ? - Usage\n" "\t S - Status\n" -"\t I - System info\n" -"\t f+ - Frequency + 1\n" -"\t f- - Frequency - 1\n" -"\t fnnn - Frequency ( 0->999 )\n" -"\t p+ - Phase + 1\n" -"\t p- - Phase - 1\n" -"\t pnnn - Phase ( 0->360 degrees )\n" +"\t R - Resource Allocation\n" "\t h - Frequency multiplier Hz\n" "\t k - Frequency multiplier KHz\n" -"\t snnn - Sine wave + harmonic ( 0->9 )\n" -"\t q+ - Duty Cycle + 1\n" -"\t q- - Duty Cycle - 1\n" -"\t qnnn - Square wave + duty cycle ( 0->100%% )\n" -"\t t+ - Rise time + 1\n" -"\t t- - Rise time - 1\n" -"\t tnnn - Triangle wave + Rise time ( 0->100%% )\n" -"\t w - Sweep frequency\n" -"\t - DAC channel A,B or Both\n" -"\t nnn - Three digit numeric value\n"; - -class DACchannel { - unsigned short DAC_data[BitMapSize] __attribute__ ((aligned(2048))) ; // Align DAC data (2048d = 0800h) - int Funct, Freq, Range, Phase, DutyC ; - uint StateMachine, ctrl_chan, data_chan, GPIO, SM_WrapBot, SM_WrapTop ; // Variabes used by the getter function... - float DAC_div ; - PIO pio; // Class wide var to share value with setter function +"\t si - Sine wave\n" +"\t sq - Square wave\n" +"\t tr - Triangle wave\n" +"\t sw - Sweep frequency (Low, High, Speed, Pause)\n" +"\t frnnn - Frequency = nnn ( 0->999 )\n" +"\t fr+ - Frequency + 1\n" +"\t fr- - Frequency - 1\n" +"\t phnnn - Phase = nnn ( 0->359 degrees )\n" +"\t ph+ - Phase + 1\n" +"\t ph- - Phase - 1\n" +"\t dunnn - Duty Cycle = nnn ( 0->100%% )\n" +"\t du+ - Duty Cycle + 1\n" +"\t du- - Duty Cycle - 1\n" +"\twhere...\n" +"\t = DAC channel A,B or Both\n" +"\t nnn = Three digit numeric value\n"; +class DAC { public: -// Setter functions... + PIO pio; // Class wide var to share value with setter function + unsigned short DAC_data[BitMapSize] __attribute__ ((aligned(2048))) ; // Align DAC data (2048d = 0800h) + int Phase, Funct, Freq, Range, DutyC, PIOnum ; + uint StateMachine, ctrl_chan, data_chan, GPIO, SM_WrapBot, SM_WrapTop ; // Variabes used by the getter function... + char name ; // Name of this instance + float DAC_div ; + + void StatusString () { + // Print the status line for the current DAC object. + char Str1[4], Str2[50] ; // ! Max line length = 50 chars ! + Range == 1 ? strcpy(Str1,"Hz") : strcpy(Str1,"KHz") ; // Asign multiplier suffix + switch ( Funct ) { // Calculate status sting... + case _Sine_: + sprintf(Str2,"\tChannel %c: Freq:%03d%s Phase:%03d Wave:Sine\n", name, Freq, Str1, Phase) ; + break; + case _Triangle_: + if ((DutyC == 0) || (DutyC == 100)) { + sprintf(Str2,"\tChannel %c: Freq:%03d%s Phase:%03d Wave:Sawtooth\n", name, Freq, Str1, Phase) ; + } else { + sprintf(Str2,"\tChannel %c: Freq:%03d%s Phase:%03d Wave:Triangle Rise time:%d%%\n", name, Freq, Str1, Phase, DutyC) ; + } + break; + case _Square_: + sprintf(Str2,"\tChannel %c: Freq:%03d%s Phase:%03d Wave:Square Duty cycle:%d%%\n", name, Freq, Str1, Phase, DutyC) ; + } + strcat(outStr,Str2) ; + } + + // Setter functions... void ReInit () { // Re-initialises DMA channels to their initial state. - // Note: 1) DMA channels are not restarted, allowing an atomic (simultaneous) restart of both DAC channels later. + // Note: 1) DMA channels are not restarted, allowing for atomic (simultaneous) restart of both DAC channels later. // 2) Cannot use dma_hw->abort on chained DMA channels, so using disable and re-enable instead. // 3) This needs to be performed across both DAC channels to ensure phase sync is maintained. // Disable both DMA channels associated with this DAC... @@ -105,43 +124,68 @@ public: hw_set_bits(&dma_hw->ch[data_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); hw_set_bits(&dma_hw->ch[ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); } - void SetFunct (int _value) { Funct = _value ; } // Function (Sine/Triangl/Square) - void SetDutyC (int _value) { DutyC = _value ; } // Duty cycle (0->100%) - void SetRange (int _value) { Range = _value ; // Range (Hz/KHz) - DACspeed(Freq * Range) ; } // Update State Machine run speed - void SetFreq (int _value) { Freq = _value ; // Frequency (numeric) - DACspeed(Freq * Range) ; } // Update State machine run speed - void SetPhase (int _value) { Phase = _value ; // Phase shift (0->360 degrees) - DataCalc() ; } // Recalc Bitmap using new phase value - int BumpFreq (int _value) { Freq += _value ; - if (Freq >= 1000) { Freq = 0 ; } // Endwrap - if (Freq < 0) { Freq = 999 ; } // Endwrap - DACspeed(Freq * Range) ; - return (Freq) ; } - int BumpPhase (int _value) { Phase += _value ; - if (Phase == 360) { Phase = 0 ; } // Endwrap - if (Phase < 0 ) { Phase = 360 ; } // Endwrap - DataCalc(); // Update Bitmap data to include new DAC phase - return (Phase) ; } - int BumpDuty (int _value) { DutyC += _value ; - if (DutyC == 100) { DutyC = 0 ; } // Endwrap - if (DutyC < 0 ) { DutyC = 100 ; } // Endwrap - DataCalc(); - return (DutyC) ; } // Update Bitmap with new Duty Cycle value + + int Set(int _type, int _val) { + // _type = Frequency / Phase / Duty, _dirn = Up / Down (_Up = 1, _Down = -1) + if (_type == _Freq_) { + Freq = _val ; // Frequency (numeric) + ReInit() ; // Stop and reset the DAC channel (no restart) + DACspeed(Freq * Range) ; } // Update State machine run speed + if (_type == _Range_) { + Range = _val ; // Frequency (multiplier) + ReInit() ; // Stop and reset the DAC channel (no restart) + DACspeed(Freq * Range) ; } // Update State machine run speed + if (_type == _Phase_) { + Phase = _val ; // Phase shift (0->355 degrees) + ReInit() ; // Stop and reset the DAC channel (no restart) + DataCalc() ; } // Recalc Bitmap and apply new phase value + if (_type == _Duty_) { + DutyC = _val ; // Duty cycle (0->100%) + DataCalc() ; } // Recalc Bitmap and apply new Duty Cycle value + if (_type == _Funct_) { // Function (Sine/Triangl/Square) + Funct = _val ; + DataCalc() ; // Recalc Bitmap and apply new Function value + } + return (_val) ; + } + + int Bump(int _type, int _dirn) { + // _type = Frequency / Phase / Duty, _dirn = Up / Down (_Up = 1, _Down = -1) + int val = 0 ; + if (_type == _Freq_) { + Freq += _dirn ; + if (Freq >= 1000) Freq = 0 ; // Endwrap + if (Freq < 0) Freq = 999 ; // Endwrap + val = Freq ; + DACspeed(Freq * Range) ; } + if (_type == _Phase_) { + Phase += _dirn ; + if (Phase == 360) Phase = 0 ; // Endwrap + if (Phase < 0 ) Phase = 359 ; // Endwrap + val = Phase ; + DataCalc(); } // Update Bitmap data to include new DAC phase + if (_type == _Duty_) { + DutyC += _dirn ; + if (DutyC > 100) { DutyC = 0 ; } // Top endwrap + if (DutyC < 0 ) { DutyC = 100 ; } // Bottom endwrap + val = DutyC ; + DataCalc(); } // Update Bitmap with new Duty Cycle value + return (val) ; + } void DACspeed (int _frequency) { - // If DAC_div exceeds 2^16 (65,536), the registers wrap around, and the State Machine clock will be incorrect. - // A slow version of the DAC State Machine is used for frequencies below 17Hz, allowing the value of DAC_div to - // be kept within range. + // If DAC_div exceeds 2^16 (65,536), the registers wrap around, and the State Machine clock will be incorrect. + // A slow version of the DAC State Machine is used for frequencies below 17Hz, allowing the value of DAC_div to + // be kept within range. float DAC_freq = _frequency * BitMapSize; // Target frequency... DAC_div = 2 * (float)clock_get_hz(clk_sys) / DAC_freq; // ...calculate the PIO clock divider required for the given Target frequency float Fout = 2 * (float)clock_get_hz(clk_sys) / (BitMapSize * DAC_div); // Actual output frequency if (_frequency >= 34) { // Fast DAC ( Frequency range from 34Hz to 999Khz ) SM_WrapTop = SM_WrapBot ; // SM program memory = 1 op-code pio_sm_set_wrap (pio, StateMachine, SM_WrapBot, SM_WrapTop) ; // Fast loop (1 clock cycle) - // If the previous frequency was < 33Hz, we will have just shrunk the assembler from 4 op-codes down to 1. - // This leaves the State Machine PC pointing outside of the new WRAP statement, which crashes the SM. - // To avoid this, we need to also reset the State Machine program counter... + // If the previous frequency was < 33Hz, we will have just shrunk the assembler from 4 op-codes down to 1. + // This leaves the State Machine program counter pointing outside of the new WRAP statement, which crashes the SM. + // To avoid this, we need to also reset the State Machine program counter... pio->sm[StateMachine].instr = SM_WrapBot ; // Reset State Machine PC to start of code pio_sm_set_clkdiv(pio, StateMachine, DAC_div); // Set the State Machine clock } else { // Slow DAC ( 1Hz=>33Hz ) @@ -149,10 +193,11 @@ public: DAC_freq = DAC_freq * 64; SM_WrapTop = SM_WrapBot + 3 ; // SM program memory = 4 op-codes pio_sm_set_wrap (pio, StateMachine, SM_WrapBot, SM_WrapTop) ; // slow loop (64 clock cycles) - // If the previous frequency was >= 34Hz, we will have just expanded the assembler code from 1 op-code up to 4. - // The State Machine PC will still be pointing to an op-code within the new WRAP statement, so will not crash. + // If the previous frequency was >= 34Hz, we will have just expanded the assembler code from 1 op-code up to 4. + // The State Machine program counter will still be pointing to an op-code within the new WRAP statement, so will not crash. pio_sm_set_clkdiv(pio, StateMachine, DAC_div); // Set the State Machine clock speed } + StatusString () ; // Update the terminal session } void DataCalc () { @@ -169,7 +214,7 @@ public: case _Sine_: DutyC = DutyC % 10; // Sine value cycles after 7 for (i=0; i= 1) { a += v_offset/3 * sin((float)_2Pi*3*i / (float)BitMapSize); } // Add 3rd harmonic @@ -203,42 +248,27 @@ public: if (i > x1) { DAC_data[j] = (BitMapSize - 1) - ((i - x1) * g2); } // Falling section of waveform } } - } - // Getter functions... - int Get_Resource (int _index) { - int result; - switch (_index) { - case _GPIO_: result = GPIO; break; - case _PIO_: result = pio_get_index(pio); break; - case _BM_start_: result = (int)&DAC_data[0]; break; - case _SM_: result = StateMachine; break; - case _SM_codeBot_: result = SM_WrapBot; break; - case _SM_codeTop_: result = SM_WrapTop; break; - case _DMA_ctrl_: result = ctrl_chan; break; - case _DMA_data_: result = data_chan; break; - case _Funct_: result = Funct; break; - case _Phase_: result = Phase; break; - case _Freq_: result = Freq; break; - case _Range_: result = Range; break; - case _DAC_div_: result = DAC_div; break; - } - return (result); + StatusString () ; // Update the terminal session } public: // Each DAC channel consists of... - // DMA => FIFO => State Machine => GPIO pins => R2R module + // BitMap data => DMA => FIFO => State Machine => GPIO pins => R-2-R module // Note: The PIO clock dividers are 16-bit integer, 8-bit fractional, with first-order delta-sigma for the fractional divider. // This means the clock divisor can vary between 1 and 65536, in increments of 1/256. // If DAC_div exceeds 2^16 (65,536), the registers will wrap around, and the State Machine clock will be incorrect. // For frequencies below 34Hz, an additional 63 op-code delay is inserted into the State Machine assembler code. This slows // down the State Machine operation by a factor of 64, keeping the value of DAC_div within range. // Parameters... - // _pio = the required PIO channel - // _GPIO = the port connecting to the MSB of the R-2-R resistor network. + // _name = Name of this DAC channel instance + // _pio = Required PIO channel + // _GPIO = Port connecting to the MSB of the R-2-R resistor network. // Constructor - int DAC_chan(PIO _pio, uint _GPIO) { - pio = _pio, GPIO = _GPIO; // copy parameters to class vars + int DAC_chan(char _name, PIO _pio, uint _GPIO) { + pio = _pio, GPIO = _GPIO, name = _name ; // Copy parameters to class vars + PIOnum = pio_get_index(pio) ; // Print friendly value + Funct = _Sine_, Freq = 100, Range = 1, DutyC = 50 ; // Assign start-up default values. + name == 'A' ? Phase = 0 : Phase = 180 ; // Phase difference between channels int _offset; StateMachine = pio_claim_unused_sm(_pio, true); // Find a free state machine on the specified PIO - error if there are none. ctrl_chan = dma_claim_unused_channel(true); // Find 2 x free DMA channels for the DAC (12 available) @@ -284,6 +314,8 @@ public: ); DAC_channel_mask += (1u << ctrl_chan) ; // Save details of DMA control channel to global variable. This facilitates // atomic restarts of both channels, and ensures phase lock between channels. + DataCalc() ; // Populate bitmap data. + DACspeed(Freq * Range) ; // Initialise State MAchine clock speed. return(StateMachine); } @@ -291,14 +323,14 @@ public: class blink_forever { // Class to initialise a state machine to blink a GPIO pin PIO pio ; // Class wide variables to share value with setter function -uint pioNum, StateMachine, Freq, _offset ; public: +uint pioNum, StateMachine, Freq, _offset ; blink_forever(PIO _pio) { pio = _pio; // transfer parameter to class wide var pioNum = pio_get_index(_pio); StateMachine = pio_claim_unused_sm(_pio, true); // Find a free state machine on the specified PIO - error if there are none. _offset = pio_add_program(_pio, &pio_blink_program); - blink_program_init(_pio, StateMachine, _offset, LED ); + blink_program_init(_pio, StateMachine, _offset, PICO_DEFAULT_LED_PIN ); pio_sm_set_enabled(_pio, StateMachine, true); } @@ -309,94 +341,66 @@ public: float DAC_div = (float)clock_get_hz(clk_sys) /((float)_frequency*2000); pio_sm_set_clkdiv(pio, StateMachine, DAC_div); // Set the State Machine clock speed } - - // Getter function... - int Get_Resource (int _index) { - int result; - switch (_index) { - case _GPIO_: result = LED; break; - case _SM_: result = StateMachine; break; - case _PIO_: result = pioNum; break; - case _Freq_: result = Freq; break; - } - return (result); - } }; -void ChanInfo ( DACchannel DACchannel[], int _chanNum) { -// Print current channel parameters to the console... - char Chan, WaveStr[9], MultStr[4]; - int value = DACchannel[_chanNum].Get_Resource(_Funct_); - - int test = DACchannel[_chanNum].Get_Resource(_Phase_); - - switch ( value ) { - case _Sine_: strcpy(WaveStr, "Sine"); break; - case _Triangle_: strcpy(WaveStr, "Triangle"); break; - case _Square_: strcpy(WaveStr,"Square"); - } - _chanNum == 0 ? Chan = 'A' : Chan = 'B'; - DACchannel[_chanNum].Get_Resource(_Range_) == 1 ? strcpy(MultStr,"Hz ") : strcpy(MultStr,"KHz"); - printf("\tChannel %c: Freq:%03d%s Phase:%03d Wave:%s\n", Chan, DACchannel[_chanNum].Get_Resource(_Freq_), - MultStr, DACchannel[_chanNum].Get_Resource(_Phase_), WaveStr); -} - -void SysInfo ( DACchannel DACchannel[], blink_forever LED_blinky) { +void SysInfo ( DAC DAC[], blink_forever LED_blinky) { // Print system and resource allocation details... - int a,b,c,d ; - a = LED_blinky.Get_Resource(_PIO_); - b = LED_blinky.Get_Resource(_SM_); - c = LED_blinky.Get_Resource(_GPIO_); - d = LED_blinky.Get_Resource(_Freq_); - printf("\n|-----------------------------------------------------------|\n"); - printf("| Waveform Generator Ver: 0.0.1 Date: 21/03/2013 |\n"); - printf("|-----------------------------|-----------------------------|\n"); - printf("| LED blinker | |\n"); - printf("|-----------------------------| |\n"); - printf("| PIO: %2d | Key: |\n",a); - printf("| SM: %2d | SM = State machine |\n",b); - printf("| GPIO: %2d | BM = Bitmap |\n",c); - printf("| Frequency: %2dHz | |\n",d); - printf("|-----------------------------|-----------------------------|\n"); - printf("| DAC channel A | DAC channel B |\n"); - a = DACchannel[_A].Get_Resource(_Freq_), b = DACchannel[_B].Get_Resource(_Freq_); - printf("| Frequency: %3d | Frequency: %3d |\n",a,b); - a = DACchannel[_A].Get_Resource(_DAC_div_), b = DACchannel[_B].Get_Resource(_DAC_div_); - printf("| Divider: %05x | Divider: %05x |\n",a,b); - printf("|-----------------------------|-----------------------------|\n"); - a = DACchannel[_A].Get_Resource(_PIO_), b = DACchannel[_B].Get_Resource(_PIO_); - printf("| PIO: %d | PIO: %d |\n",a,b); - a = DACchannel[_A].Get_Resource(_GPIO_), b = DACchannel[_B].Get_Resource(_GPIO_); - printf("| GPIO: %d-%d | GPIO: %d-%d |\n",a,a+7,b,b+7); - printf("| BM size: %8d | BM size: %8d |\n", BitMapSize, BitMapSize); - a = DACchannel[_A].Get_Resource(_BM_start_), b = DACchannel[_B].Get_Resource(_BM_start_); - printf("| BM start: %x | BM start: %x |\n",a,b); - a = DACchannel[_A].Get_Resource(_SM_), b = DACchannel[_B].Get_Resource(_SM_); - printf("| SM: %d | SM: %d |\n",a,b); - a = DACchannel[_A].Get_Resource(_SM_codeBot_), b = DACchannel[_B].Get_Resource(_SM_codeBot_); - printf("| Wrap Bottom: %2x | Wrap Bottom: %2x |\n",a,b); - a = DACchannel[_A].Get_Resource(_SM_codeTop_), b = DACchannel[_B].Get_Resource(_SM_codeTop_); - printf("| Wrap Top: %2x | Wrap Top: %2x |\n",a,b); - a = DACchannel[_A].Get_Resource(_DMA_ctrl_), b = DACchannel[_B].Get_Resource(_DMA_ctrl_); - printf("| DMA ctrl: %2d | DMA ctrl: %2d |\n",a,b); - a = DACchannel[_A].Get_Resource(_DMA_data_), b = DACchannel[_B].Get_Resource(_DMA_data_); - printf("| DMA data: %2d | DMA data: %2d |\n",a,b); - printf("|--------------|--------------|--------------|--------------|\n"); + sprintf(outStr,"\t|-----------------------------------------------------------|\n" + "\t| Resource allocation |\n" + "\t|-----------------------------|-----------------------------|\n" + "\t| LED blinker | |\n" + "\t|-----------------------------| |\n" + "\t| PIO: %2d | Key: |\n" + "\t| SM: %2d | SM = State machine |\n" + "\t| GPIO: %2d | BM = Bitmap |\n" + "\t| Frequency: %2dHz | |\n" + "\t|-----------------------------|-----------------------------|\n" + "\t| DAC Channel A | DAC Channel B |\n" + "\t|-----------------------------|-----------------------------|\n" + "\t| Frequency: %3d | Frequency: %3d |\n" + "\t| Phase: %3d | Phase: %3d |\n" + "\t| Duty cycle: %3d | Duty cycle: %3d |\n" + "\t| Divider: %05d | Divider: %05d |\n" + "\t|-----------------------------|-----------------------------|\n" + "\t| PIO: %d | PIO: %d |\n" + "\t| GPIO: %d-%d | GPIO: %d-%d |\n" + "\t| BM size: %8d | BM size: %8d |\n" + "\t| BM start: %x | BM start: %x |\n" + "\t| SM: %d | SM: %d |\n" + "\t| Wrap Bottom: %2x | Wrap Bottom: %2x |\n" + "\t| Wrap Top: %2x | Wrap Top: %2x |\n" + "\t| DMA ctrl: %2d | DMA ctrl: %2d |\n" + "\t| DMA data: %2d | DMA data: %2d |\n" + "\t|-----------------------------|-----------------------------|\n", + LED_blinky.pioNum, LED_blinky.StateMachine, PICO_DEFAULT_LED_PIN, LED_blinky.Freq, + DAC[_A].Freq, DAC[_B].Freq, + DAC[_A].Phase, DAC[_B].Phase, + DAC[_A].DutyC, DAC[_B].DutyC, + (int)DAC[_A].DAC_div, (int)DAC[_B].DAC_div, + DAC[_A].PIOnum, DAC[_B].PIOnum, + DAC[_A].GPIO, DAC[_A].GPIO+7, DAC[_B].GPIO, DAC[_B].GPIO+7, + BitMapSize, BitMapSize, + (int)&DAC[_A].DAC_data[0], (int)&DAC[_B].DAC_data[0], + DAC[_A].StateMachine, DAC[_B].StateMachine, + DAC[_A].SM_WrapBot, DAC[_B].SM_WrapBot, + DAC[_A].SM_WrapTop, DAC[_B].SM_WrapTop, + DAC[_A].ctrl_chan, DAC[_B].ctrl_chan, + DAC[_A].data_chan, DAC[_B].data_chan ); } static inline void cs_select() { asm volatile("nop \n nop \n nop"); - gpio_put(Nixie_CS, 0); // Active low + gpio_put(Display_CS, 0); // Active low asm volatile("nop \n nop \n nop"); } static inline void cs_deselect() { asm volatile("nop \n nop \n nop"); - gpio_put(Nixie_CS, 1); + gpio_put(Display_CS, 1); asm volatile("nop \n nop \n nop"); } -static void SPI_Nixie_Write(int _data) { +static void SPI_Display_Write(int _data) { uint8_t buff[2]; buff[0] = _data / 256; // MSB data buff[1] = _data % 256; // LSB data @@ -405,53 +409,16 @@ static void SPI_Nixie_Write(int _data) { cs_deselect(); } -static char * getLine(bool fullDuplex = false, char lineBreak = '\n') { -/* - * read a line of any length from stdio (grows) - * - * @param fullDuplex input will echo on entry (terminal mode) when false - * @param linebreak defaults to "\n", but "\r" may be needed for terminals - * @return entered line on heap - don't forget calling free() to get memory back - */ - // th line buffer - // will allocated by pico_malloc module if gets included - char * pStart = (char*)malloc(startLineLength); - char * pPos = pStart; // next character position - size_t maxLen = startLineLength; // current max buffer size - size_t len = maxLen; // current max length - int c; - - if(!pStart) { - return NULL; // out of memory or dysfunctional heap - } - +static void getLine() { + char *pPos = (char *)inStr ; // Pointer to start of Global input string while(1) { - c = getchar(); // expect next character entry - if(c == eof || c == lineBreak) { - break; // non blocking exit - } - if (fullDuplex) { - putchar(c); // echo for fullDuplex terminals - } - if(--len == 0) { // allow larger buffer - len = maxLen; - // double the current line buffer size - char *pNew = (char*)realloc(pStart, maxLen *= 2); - if(!pNew) { - free(pStart); - return NULL; // out of memory abort - } - // fix pointer for new buffer - pPos = pNew + (pPos - pStart); - pStart = pNew; - } - // stop reading if lineBreak character entered - if((*pPos++ = c) == lineBreak) { - break; - } + c = getchar(); + if (c == eof || c == '\n' || c == '\r') break ; // Non blocking exit + putchar(c); // FullDuplex echo + *pPos++ = c ; // Bump pointer, store character } - *pPos = '\0'; // set string end mark - return pStart; + *pPos = '\0'; // Set string end mark + return ; } int main() { @@ -463,229 +430,169 @@ int main() { gpio_set_function(PIN_TX, GPIO_FUNC_SPI); // Chip select is active-low, so initialise to a driven-high state... - gpio_init(Nixie_CS); - gpio_set_dir(Nixie_CS, GPIO_OUT); - gpio_put(Nixie_CS, 1); + gpio_init(Display_CS); + gpio_set_dir(Display_CS, GPIO_OUT); + gpio_put(Display_CS, 1); // Initialise remaining SPI connections... gpio_set_dir(PIN_CLK, GPIO_OUT); gpio_set_dir(PIN_TX, GPIO_OUT); - DACchannel DACchannel[2]; // Array to hold the two DAC channel objects + DAC DAC[2]; // Array to hold the two DAC channel objects -// Set up the objects controlling the various State Machines... -// Note: Both DAC channels need to be on the same PIO to acheive accurate phase sync. - DACchannel[_A].DAC_chan(pio1,0); // First DAC channel object in array - resistor network connected to GPIO0->7 - DACchannel[_B].DAC_chan(pio1,8); // Second DAC channel object in array - resistor network connected to GPIO8->15 - blink_forever LED_blinky(pio0); // Onboard LED blinky object +// Instantiate objects to control the various State Machines... +// Note: Both DAC channels need to be on the same PIO to achieve +// Atomic restarts for accurate phase sync. + DAC[_A].DAC_chan('A',pio1,0); // First DAC channel object in array - resistor network connected to GPIO0->8 + DAC[_B].DAC_chan('B',pio1,8); // Second DAC channel object in array - resistor network connected to GPIO8->16 + blink_forever LED_blinky(pio0); // Onboard LED blinky object -// Set default run time settings... - DACchannel[_A].SetRange(1), DACchannel[_B].SetRange(1) ; // Hz - DACchannel[_A].SetFunct(_Sine_), DACchannel[_B].SetFunct(_Sine_) ; // Sine wave, no harmonics - DACchannel[_A].SetDutyC(50), DACchannel[_B].SetDutyC(50) ; // 50% Duty cycle - DACchannel[_A].SetFreq(100), DACchannel[_B].SetFreq(100) ; // 100 - DACchannel[_A].SetPhase(0), DACchannel[_B].SetPhase(180) ; // 180 phase diff + generate the two Bitmaps - strcpy(LastCmd,"?") ; // Hitting return will give 'Help' + strcpy(LastCmd,"?") ; // Hitting return will give 'Help' - SPI_Nixie_Write(DACchannel[_A].Get_Resource(_Freq_)); // Frequency => Nixie display + SPI_Display_Write(0) ; // Zero => SPI display -// Set LED to slow flash indicates waiting for USB connection... - LED_blinky.Set_Frequency(1); // 1Hz + LED_blinky.Set_Frequency(1); // Flash LED at 1Hz- waiting for USB connection -// Wait for USB connection... - while (!stdio_usb_connected()) { sleep_ms(100); } + while (!stdio_usb_connected()) { sleep_ms(100); } // Wait for USB connection... -// USB connection established, set LED to rapid flash... - LED_blinky.Set_Frequency(10); // 10Hz + LED_blinky.Set_Frequency(10); // Flash LED at 10Hz - USB connected. + SPI_Display_Write(DAC[_A].Freq) ; // Frequency => SPI display - SysInfo(DACchannel, LED_blinky); // Show configuration (optional) -// printf(HelpText); // Show instructions (optional) +// Send start-up text to terminal... + printf("==============================\n" // Version details to terminal (optional) + "WaveForm Generator Version 1.0\n" + "==============================\n") ; -// Starting all 4 DMA channels simultaneously ensures phase sync across all State Machines... +// Atomic Restart - starting all 4 DMA channels simultaneously ensures phase sync between both DAC channels dma_start_channel_mask(DAC_channel_mask); while(1) { - ParmCnt=0, Parm[0]=0, Parm[1]=0, Parm[2]=0, Parm[3]=0; - printf(">") ; // Command prompt + ParmCnt=0, Parm[0]=0, Parm[1]=0, Parm[2]=0, Parm[3]=0; // Reset all command line parameters + printf(">") ; // Command prompt - char *inString = getLine(true, '\r') ; + getLine() ; // Zero length string = 'CR' pressed... - if (strlen(inString) == 0) { strcpy(inString,LastCmd) ; // Repeat last command - printf("%s", inString) ; } + if (strlen(inStr) == 0) { strcpy(inStr,LastCmd) ; // Repeat last command + printf("%s", inStr) ; } - // Check for single character instructions... - if (inString[0] == '?') { printf(HelpText); } // Help text - if (inString[0] == 'S') { ChanInfo(DACchannel, _A); // Status info - ChanInfo(DACchannel, _B); } - if (inString[0] == 'I') { SysInfo(DACchannel, LED_blinky); } + // One character commands... + if (strlen(inStr) == 1) { + if (inStr[0] == '?') sprintf(outStr,HelpText); // Help text + if (inStr[0] == 'S') { DAC[_A].StatusString() ; DAC[_B].StatusString() ; } + if (inStr[0] == 'R') SysInfo(DAC, LED_blinky); + } - // Select DAC channel A or B... - if (inString[0] == 'A') { SelectedChan = 0b0001; } // Channel A - if (inString[0] == 'B') { SelectedChan = 0b0010; } // Channel B - if (inString[0] == 'C') { SelectedChan = 0b0011; } // Channel A & B + // For all remaining commands, the first character selects DAC channel A or B... + if (inStr[0] == 'A') { SelectedChan = 0b0001; } // Channel A only + if (inStr[0] == 'B') { SelectedChan = 0b0010; } // Channel B only + if (inStr[0] == 'C') { SelectedChan = 0b0011; } // Channel A & B - if ((inString[2] != '+') && (inString[2] != '-')) { - // Not bumping a value, so extract the value of Parm[0]... - i = 1 ; // Skip chars 0 & 1 - while (i++ < strlen(inString)-1 ) { // Start at char 2 - if ( inString[i] == ',' ) { ParmCnt++ ; } // Next parameter - else { Parm[ParmCnt] *= 10; // Next digit. Bump the existing decimal digits - Parm[ParmCnt] += inString[i] - '0'; } // Convert character to integer and add + // ...and if we aren't bumping a value, there will be one or more parameters... + if ((inStr[2] != '+') && (inStr[2] != '-')) { + i = 2 ; // Skip chars 0, 1 and 2 + while (i++ < strlen(inStr)-1 ) { // Starts at char 3 + if ( inStr[i] == ',' ) { ParmCnt++ ; } // Next parameter + else { Parm[ParmCnt] *= 10; // Next digit. Bump the existing decimal digits + Parm[ParmCnt] += inStr[i] - '0'; } // Convert character to integer and add } } - // Perform the selected command... - switch ( inString[1] ) { - case 'w': // Frequency sweep - i = Parm[0]; - for (;;) { - DACchannel[_A].ReInit(); // Stop DAC channel A and re-initialise DMA to start of Bitmap data - DACchannel[_B].ReInit(); // Stop DAC channel B and re-initialise DMA to start of Bitmap data - if (SelectedChan & 0b01) { - DACchannel[_A].SetFreq(i); - ChanInfo(DACchannel, _A); // Update the terminal - } - if (SelectedChan & 0b10) { - DACchannel[_B].SetFreq(i); - ChanInfo(DACchannel, _B); // Update the terminal - } - dma_start_channel_mask(DAC_channel_mask); // Atomically Restart all 4 DMA channels... - SPI_Nixie_Write(i); // Update Nixie display - if (i==Parm[0]) { dirn = 1; - sleep_ms(Parm[3]); } // Count up from zero, pause at end - if (i>=Parm[1]) { dirn =-1; - sleep_ms(Parm[3]); } // Count down from 100, pause at start - i = i + dirn; - c = getchar_timeout_us (0); // Non-blocking char input - if ((c>=32) & (c<=126)) { break; } // exit on keypress - sleep_ms(Parm[2]); // Speed of scan - } - break; - case 's': // Sine wave - if (SelectedChan & 0b01) { - DACchannel[_A].SetFunct(_Sine_); - DACchannel[_A].SetDutyC(Parm[0]); - DACchannel[_A].DataCalc(); - } - if (SelectedChan & 0b10) { - DACchannel[_B].SetFunct(_Sine_); - DACchannel[_B].SetDutyC(Parm[0]); - DACchannel[_B].DataCalc(); - } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - case 't': // Triangle wave - if (inString[2] == '+') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpDuty(_Up); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpDuty(_Up); } // Bump + grab new value for SPI - } - else if (inString[2] == '-') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpDuty(_Down); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpDuty(_Down); } // Bump + grab new value for SPI - } - else { - // Not bumping the value, so set the absolute value from Parm[0]... - if ( Parm[0] > 100 ) { Parm[0] = 100; } // Hard limit @ 100% - if (SelectedChan & 0b01) { - DACchannel[_A].SetFunct(_Triangle_); - DACchannel[_A].SetDutyC(Parm[0]); - DACchannel[_A].DataCalc(); - } - if (SelectedChan & 0b10) { - DACchannel[_B].SetFunct(_Triangle_); - DACchannel[_B].SetDutyC(Parm[0]); - DACchannel[_B].DataCalc(); - } - } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - case 'q': // sQuare wave - if (inString[2] == '+') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpDuty(_Up); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpDuty(_Up); } // Bump + grab new value for SPI - } - else if (inString[2] == '-') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpDuty(_Down); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpDuty(_Down); } // Bump + grab new value for SPI - } - else { - // Not bumping the value, so set the absolute value from Parm[0]... - if ( Parm[0] > 100 ) { Parm[0] = 100; } // Hard limit @ 100% - if (SelectedChan & 0b01) { - DACchannel[_A].SetFunct(_Square_); - DACchannel[_A].SetDutyC(Parm[0]); - DACchannel[_A].DataCalc(); - } - if (SelectedChan & 0b10) { - DACchannel[_B].SetFunct(_Square_); - DACchannel[_B].SetDutyC(Parm[0]); - DACchannel[_B].DataCalc(); - } - } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - case 'h': // Set Hz - if (SelectedChan & 0b01) { DACchannel[_A].SetRange(1); } - if (SelectedChan & 0b10) { DACchannel[_B].SetRange(1); } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - case 'k': // Set KHz - if (SelectedChan & 0b01) { DACchannel[_A].SetRange(1000); } - if (SelectedChan & 0b10) { DACchannel[_B].SetRange(1000); } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - case 'f': // Frequency setting... - if (inString[2] == '+') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpFreq(_Up); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpFreq(_Up); } // Bump + grab new value for SPI - } - else if (inString[2] == '-') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpFreq(_Down); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpFreq(_Down); } // Bump + grab new value for SPI - } - else { - // Not bumping the value, so set the absolute value from Parm[0]... - DACchannel[_A].ReInit(); // Stop DAC channel A and re-initialise DMA to start of Bitmap data - DACchannel[_B].ReInit(); // Stop DAC channel B and re-initialise DMA to start of Bitmap data - if (SelectedChan & 0b01) { DACchannel[_A].SetFreq(Parm[0]); } // Update State Machine clock speed - if (SelectedChan & 0b10) { DACchannel[_B].SetFreq(Parm[0]); } // Update State Machine clock speed - dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels - } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - case 'p': // Phase settings... - if (inString[2] == '+') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpPhase(_Up); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpPhase(_Up); } // Bump + grab new value for SPI - } - else if (inString[2] == '-') { - if (SelectedChan & 0b01) { Parm[0] = DACchannel[_A].BumpPhase(_Down); } // Bump + grab new value for SPI - if (SelectedChan & 0b10) { Parm[0] = DACchannel[_B].BumpPhase(_Down); } // Bump + grab new value for SPI - } - else { - // Not bumping the value, so set the absolute value from Parm[0]... - DACchannel[_A].ReInit(); // Stop DAC channel A and re-initialise DMA to start of Bitmap data - DACchannel[_B].ReInit(); // Stop DAC channel B and re-initialise DMA to start of Bitmap data - if (SelectedChan & 0b01) { DACchannel[_A].SetPhase(Parm[0]); } // Update DAC phase - if (SelectedChan & 0b10) { DACchannel[_B].SetPhase(Parm[0]); } // Update DAC phase. - dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels - } - if (SelectedChan & 0b01) { ChanInfo(DACchannel, _A); } // Update the terminal - if (SelectedChan & 0b10) { ChanInfo(DACchannel, _B); } - break; - default: - if ((inString[0] != 'S') && (inString[0] != 'I') && (inString[0] != '?')) { - printf("\tUnknown command\n"); } + + if (strlen(inStr) == 2) { + if (inStr[1] == 'h') { // Set Hz + if (SelectedChan & 0b01) DAC[_A].Set(_Range_,1) ; + if (SelectedChan & 0b10) DAC[_B].Set(_Range_,1) ; + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + } + if (inStr[1] == 'k') { // Set KHz + if (SelectedChan & 0b01) DAC[_A].Set(_Range_,1000) ; + if (SelectedChan & 0b10) DAC[_B].Set(_Range_,1000) ; + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + } } - SPI_Nixie_Write(Parm[0]); // Update Nixie display - strcpy(LastCmd, inString) ; // Preserve last command - free(inString); // free buffer + + if ((inStr[1] == 'f') & (inStr[2] == 'r')) { // Set Frequency + if (inStr[3] == '+') { // Bump up and grab result for SPI display... + if (SelectedChan & 0b01) result = DAC[_A].Bump(_Freq_,_Up) ; + if (SelectedChan & 0b10) result = DAC[_B].Bump(_Freq_,_Up) ; + } else if (inStr[3] == '-') { // Bump down and grab result for SPI display... + if (SelectedChan & 0b01) result = DAC[_A].Bump(_Freq_,_Down) ; + if (SelectedChan & 0b10) result = DAC[_B].Bump(_Freq_,_Down) ; + } else { // Not a bump, so set the absolute value from Parm[0]... + if (SelectedChan & 0b01) result = DAC[_A].Set(_Freq_,Parm[0]) ; + if (SelectedChan & 0b10) result = DAC[_B].Set(_Freq_,Parm[0]) ; + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + } + } + if ((inStr[1] == 'p') & (inStr[2] == 'h')) { // Set Phase + if (inStr[3] == '+') { // Bump up and grab result for SPI display... + if (SelectedChan & 0b01) result = DAC[_A].Bump(_Phase_,_Up) ; + if (SelectedChan & 0b10) result = DAC[_B].Bump(_Phase_,_Up) ; + } else if (inStr[3] == '-') { // Bump down and grab result for SPI display... + if (SelectedChan & 0b01) result = DAC[_A].Bump(_Phase_,_Down) ; + if (SelectedChan & 0b10) result = DAC[_B].Bump(_Phase_,_Down) ; + } else { // Not a bump, so set the absolute value from Parm[0]... + if (SelectedChan & 0b01) result = DAC[_A].Set(_Phase_,Parm[0]) ; + if (SelectedChan & 0b10) result = DAC[_B].Set(_Phase_,Parm[0]) ; + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + } + } + + if ((inStr[1] == 'd') & (inStr[2] == 'u')) { // Set Duty cycle + if (inStr[3] == '+') { // Bump up and grab result for SPI display... + if (SelectedChan & 0b01) result = DAC[_A].Bump(_Duty_,_Up) ; + if (SelectedChan & 0b10) result = DAC[_B].Bump(_Duty_,_Up) ; + } else if (inStr[3] == '-') { // Bump down and grab result for SPI display... + if (SelectedChan & 0b01) result = DAC[_A].Bump(_Duty_,_Down) ; + if (SelectedChan & 0b10) result = DAC[_B].Bump(_Duty_,_Down) ; + } else { // Not a bump, so set the absolute value from Parm[0]... + if ( Parm[0] > 100 ) Parm[0] = 100; // Hard limit @ 100% + if (SelectedChan & 0b01) DAC[_A].Set(_Duty_,Parm[0]) ; + if (SelectedChan & 0b10) DAC[_B].Set(_Duty_,Parm[0]) ; + } + } + + // Next two characters select the command... + if (strlen(inStr) == 3) { // TBD - is this needed ??? + if ((inStr[1] == 's') & (inStr[2] == 'i')) { // Set Sine + if (SelectedChan & 0b01) DAC[_A].Set(_Funct_,_Sine_) ; + if (SelectedChan & 0b10) DAC[_B].Set(_Funct_,_Sine_) ; + } else if ((inStr[1] == 's') & (inStr[2] == 'q')) { // Set Square + if (SelectedChan & 0b01) DAC[_A].Set(_Funct_,_Square_) ; + if (SelectedChan & 0b10) DAC[_B].Set(_Funct_,_Square_) ; + } else if ((inStr[1] == 't') & (inStr[2] == 'r')) { // Set Triangle + if (SelectedChan & 0b01) DAC[_A].Set(_Funct_,_Triangle_) ; + if (SelectedChan & 0b10) DAC[_B].Set(_Funct_,_Triangle_) ; + } + } + + if ((inStr[1] == 's') & (inStr[2] == 'w')) { // Sweep + // Parm[0]=Low frequency, Parm[1]=High frequency, Parm[2]=Scan speed, Parm[3]=Low/High pause + i = Parm[0]; + for (;;) { + outStr[0] = '\0' ; // Reset the string variable + if (SelectedChan & 0b01) result = DAC[_A].Set(_Freq_,i) ; // Set frequency, display status + if (SelectedChan & 0b10) result = DAC[_B].Set(_Freq_,i) ; // Set frequency, display status + dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels... + printf(outStr) ; // Update terminal + SPI_Display_Write(i); // Update SPI display + if (i==Parm[0]) { dirn = 1; + sleep_ms(Parm[3]); } + if (i>=Parm[1]) { dirn =-1; + sleep_ms(Parm[3]); } + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + i = i + dirn; + c = getchar_timeout_us (0); // Non-blocking char input + if ((c>=32) & (c<=126)) { break; } // exit on keypress + sleep_ms(Parm[2]); // Speed of scan + } + } + + if (strlen(outStr) == 0) strcpy(outStr,"\t?\n") ; // Unknown command + printf(outStr) ; // Update terminal + outStr[0] = '\0' ; // Clear (reset) the string variable + SPI_Display_Write(result) ; // Update SPI display + strcpy(LastCmd, inStr) ; // Preserve last command } return 0; }