From 17c262526c85c0697956fac60240d91d4515ac52 Mon Sep 17 00:00:00 2001 From: Tony Date: Tue, 19 Sep 2023 13:08:59 +0100 Subject: [PATCH] Improved Phase Lock --- Function Generator/DAC.pio | 1 + Function Generator/FunctionGenerator.cpp | 411 ++++++++++++----------- Function Generator/FunctionGenerator.uf2 | Bin 0 -> 123392 bytes Function Generator/blink.pio | 35 -- 4 files changed, 207 insertions(+), 240 deletions(-) create mode 100644 Function Generator/FunctionGenerator.uf2 delete mode 100644 Function Generator/blink.pio diff --git a/Function Generator/DAC.pio b/Function Generator/DAC.pio index efd05b7..af3b658 100644 --- a/Function Generator/DAC.pio +++ b/Function Generator/DAC.pio @@ -31,6 +31,7 @@ void pio_DAC_program_init(PIO pio, uint sm, uint offset, uint start_pin) { pio_sm_config c = pio_DAC_program_get_default_config(offset); // Define PIO Configuration structure sm_config_set_out_pins(&c, start_pin, 8); // Configure pins to be targeted by the OUT (and MOV) commands sm_config_set_out_shift(&c, true, true, 8); // Shift right, Autopull enabled, 6/8 bits + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); // Set TX_FIFO to 8. Improves stability at high frequencies pio_sm_init(pio, sm, offset, &c); // Load configuration and jump to start of the program pio_sm_set_enabled(pio, sm, true); } diff --git a/Function Generator/FunctionGenerator.cpp b/Function Generator/FunctionGenerator.cpp index 6c9475c..09c063a 100644 --- a/Function Generator/FunctionGenerator.cpp +++ b/Function Generator/FunctionGenerator.cpp @@ -1,3 +1,7 @@ +// TBD: 1) SPI read connecton +// 2) Capacitors on op-amps +// 3) Issue with phase lock - red/writes to serial port affecting phase lock at high frequencies + #include #include #include @@ -9,7 +13,6 @@ #include "hardware/dma.h" #include "blink.pio.h" #include "DAC.pio.h" - #include "hardware/gpio.h" // Required for manually toggling GPIO pins (clock) ////////////////////////////////////// @@ -53,17 +56,25 @@ //#define SysClock 250 // System clock x 2 for 0.977 MHz DAC output #define SysClock 280 // Overclock for 1.000 MHz DAC output -// Data for clock face generated by Excel spreadsheet... -uint8_t FaceX[] = {235,239,243,247,251,255,255,254,254,254,254,254,254,254,253,253,253,252,252,251,251,250,250,249,248,248,247,246,245,244,244,243,242,241,240,239,221,224,227,231,234,238,236,235,234,233,232,230,229,228,226,225,224,222,221,219,218,216,214,213,211,209,208,206,204,203,201,199,197,195,193,182,184,186,188,190,191,190,188,186,184,182,180,178,176,174,172,170,167,165,163,161,159,157,155,153,150,148,146,144,142,139,137,135,133,131,128,128,128,128,128,128,126,124,122,120,117,115,113,111,109,107,104,102,100,98,96,94,92,90,87,85,83,81,79,77,75,73,71,69,67,75,73,71,69,67,65,64,62,60,58,56,54,53,51,49,47,46,44,43,41,39,38,36,35,33,32,31,29,28,27,25,24,23,22,20,36,33,30,26,23,19,18,17,16,15,14,13,12,12,11,10,9,9,8,7,7,6,6,5,5,4,4,4,3,3,3,3,3,3,2,22,18,14,10,6,2,2,3,3,3,3,3,3,4,4,4,5,5,6,6,7,7,8,9,9,10,11,12,12,13,14,15,16,17,18,36,33,30,26,23,19,20,22,23,24,25,27,28,29,31,32,33,35,36,38,39,41,43,44,46,47,49,51,53,54,56,58,60,62,64,75,73,71,69,67,65,67,69,71,73,75,77,79,81,83,85,87,90,92,94,96,98,100,102, -104,107,109,111,113,115,117,120,122,124,126,128,128,128,128,128,128,131,133,135,137,139,142,144,146,148,150,153,155,157,159,161,163,165,167,170,172,174,176,178,180,182,184,186,188,190,182,184,186,188,190,191,193,195,197,199,201,203,204,206,208,209,211,213,214,216,218,219,221,222,224,225,226,228,229,230,232,233,234,235,236,221,224,227,231,234,238,239,240,241,242,243,244,244,245,246,247,248,248,249,250,250,251,251,252,252,253,253,253,254,254,254,254,254,254,254} ; -uint8_t FaceY[] = {128,128,128,128,128,128,128,126,124,122,120,117,115,113,111,109,107,104,102,100,98,96,94,92,90,87,85,83,81,79,77,75,73,71,69,67,75,73,71,69,67,65,64,62,60,58,56,54,53,51,49,47,46,44,43,41,39,38,36,35,33,32,31,29,28,27,25,24,23,22,20,36,33,30,26,23,19,18,17,16,15,14,13,12,12,11,10,9,9,8,7,7,6,6,5,5,4,4,4,3,3,3,3,3,3,2,22,18,14,10,6,2,2,3,3,3,3,3,3,4,4,4,5,5,6,6,7,7,8,9,9,10,11,12,12,13,14,15,16,17,18,36,33,30,26,23,19,20,22,23,24,25,27,28,29,31,32,33,35,36,38,39,41,43,44,46,47,49,51,53,54,56,58,60,62,64,75,73,71,69,67,65,67,69,71,73,75,77,79,81,83,85,87,90,92,94,96,98,100,102,104,107,109,111,113,115,117,120,122,124,126,128,128,128,128,128,128,131,133,135,137,139,142,144,146,148,150,153,155,157,159,161,163,165,167,170,172,174,176,178,180,182,184,186,188,190,182,184,186,188,190,191,193,195,197,199,201,203,204,206,208,209,211,213,214,216,218,219,221,222,224,225,226,228,229,230,232,233,234,235,236,221,224,227,231,234,238,239,240,241,242,243,244,244,245,246,247,248,248,249,250,250,251,251,252, -252,253,253,253,254,254,254,254,254,254,254,235,239,243,247,251,255,254,254,254,254,254,254,254,253,253,253,252,252,251,251,250,250,249,248,248,247,246,245,244,244,243,242,241,240,239,221,224,227,231,234,238,236,235,234,233,232,230,229,228,226,225,224,222,221,219,218,216,214,213,211,209,208,206,204,203,201,199,197,195,193,182,184,186,188,190,191,190,188,186,184,182,180,178,176,174,172,170,167,165,163,161,159,157,155,153,150,148,146,144,142,139,137,135,133,131} ; -// (Number of pixels: 421) +// Data for the clock face is generated externally using an Excel spreadsheet... +uint8_t FaceX[] = { +0xfa,0xfb,0xfc,0xfd,0xfe,0xff,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfd,0xfd,0xfd,0xfc,0xfc,0xfb,0xfb,0xfa,0xfa,0xf9,0xf8,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf3,0xf2,0xf1,0xf0,0xef,0xe9,0xea,0xeb,0xec,0xed,0xed,0xec,0xeb,0xea,0xe9,0xe7,0xe6,0xe5,0xe3,0xe2,0xe1,0xdf,0xde,0xdc,0xdb,0xd9,0xd8,0xd6,0xd4,0xd3,0xd1,0xcf,0xcd,0xcc,0xca,0xc8,0xc6,0xc4,0xc3,0xc1,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xad,0xab,0xa9,0xa6,0xa4,0xa2,0xa0,0x9e,0x9c,0x9a,0x97,0x95,0x93,0x91,0x8f,0x8c,0x8a,0x88,0x86,0x83,0x81,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7d,0x7b,0x78,0x76,0x74,0x72,0x6f,0x6d,0x6b,0x69,0x67,0x64,0x62,0x60,0x5e,0x5c,0x5a,0x58,0x55,0x53,0x51,0x4f,0x4d,0x4b,0x49,0x47,0x45,0x43,0x41,0x42,0x41,0x41,0x40,0x40,0x3f,0x3d,0x3b,0x3a,0x38,0x36,0x34,0x32,0x31,0x2f,0x2d,0x2b,0x2a,0x28,0x26,0x25,0x23,0x22,0x20,0x1f,0x1d,0x1c,0x1b,0x19,0x18,0x17,0x15,0x14,0x13,0x12,0x15,0x14,0x13,0x12,0x11,0x11,0x0f,0x0e,0x0d,0x0c,0x0b,0x0b,0x0a,0x09,0x08,0x07,0x06,0x06,0x05,0x04,0x04,0x03,0x03,0x02, +0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x04,0x03,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x15,0x14,0x13,0x12,0x11,0x11,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1b,0x1c,0x1d,0x1f,0x20,0x22,0x23,0x25,0x26,0x28,0x2a,0x2b,0x2d,0x2f,0x31,0x32,0x34,0x36,0x38,0x3a,0x3b,0x3d,0x42,0x41,0x41,0x40,0x40,0x3f,0x41,0x43,0x45,0x47,0x49,0x4b,0x4d,0x4f,0x51,0x53,0x55,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64,0x67,0x69,0x6b,0x6d,0x6f,0x72,0x74,0x76,0x78,0x7b,0x7d,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x81,0x83,0x86,0x88,0x8a,0x8c,0x8f,0x91,0x93,0x95,0x97,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa9,0xab,0xad,0xaf,0xb1,0xb3,0xb5,0xb7,0xb9,0xbb,0xbd,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xc1,0xc3,0xc4,0xc6,0xc8,0xca,0xcc,0xcd,0xcf,0xd1,0xd3,0xd4,0xd6,0xd8,0xd9,0xdb,0xdc,0xde,0xdf,0xe1,0xe2,0xe3,0xe5,0xe6,0xe7,0xe9,0xea,0xeb,0xec,0xe9,0xea,0xeb,0xec,0xed,0xed,0xef,0xf0,0xf1,0xf2,0xf3,0xf3,0xf4,0xf5, +0xf6,0xf7,0xf8,0xf8,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,0xfc,0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f,0x0e,0x0d,0x0c,0x0c,0x0c,0x0c,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x0e,0x0d,0x0c,0x0c,0x0c,0x0c,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x27,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x1e,0x26,0x25,0x24,0x23,0x22,0x21,0x20,0x1f,0x1e,0x1d,0x1c,0x1b,0x52,0x51,0x50,0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x46,0x46,0x46,0x46,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51, +0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x52,0x51,0x50,0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x83,0x82,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x77,0x77,0x77,0x77,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x82,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb6,0xb5,0xb4,0xb3,0xb2,0xb1,0xb0,0xaf,0xaf,0xaf,0xaf,0xaf,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xe0,0xe1,0xe2,0xe3,0xe3,0xe3,0xe3,0xe3,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd7,0xd7,0xd7,0xd7,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,0xe1,0xe2,0xe3,0xe3,0xe3,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd7,0xd7,0xd7,0xd8,0xd9,0xf0,0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, +0xeb,0xec,0xed,0xee,0xef,0xf0,0xf0,0xf0,0xf0,0xf0,0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe5,0xe4,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd0,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xcf,0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xdb,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xb5,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x86,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,0x75,0x75,0x75,0x75,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x75,} ; +uint8_t FaceY[] = { +0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7d,0x7b,0x78,0x76,0x74,0x72,0x6f,0x6d,0x6b,0x69,0x67,0x64,0x62,0x60,0x5e,0x5c,0x5a,0x58,0x55,0x53,0x51,0x4f,0x4d,0x4b,0x49,0x47,0x45,0x43,0x41,0x42,0x41,0x41,0x40,0x40,0x3f,0x3d,0x3b,0x3a,0x38,0x36,0x34,0x32,0x31,0x2f,0x2d,0x2b,0x2a,0x28,0x26,0x25,0x23,0x22,0x20,0x1f,0x1d,0x1c,0x1b,0x19,0x18,0x17,0x15,0x14,0x13,0x12,0x15,0x14,0x13,0x12,0x11,0x11,0x0f,0x0e,0x0d,0x0c,0x0b,0x0b,0x0a,0x09,0x08,0x07,0x06,0x06,0x05,0x04,0x04,0x03,0x03,0x02,0x02,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x04,0x03,0x02,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x02,0x03,0x03,0x04,0x04,0x05,0x06,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x15,0x14,0x13,0x12,0x11,0x11,0x12,0x13,0x14,0x15,0x17,0x18,0x19,0x1b,0x1c,0x1d,0x1f,0x20,0x22,0x23,0x25,0x26,0x28,0x2a,0x2b,0x2d,0x2f,0x31,0x32,0x34,0x36,0x38,0x3a,0x3b,0x3d,0x42,0x41,0x41,0x40,0x40,0x3f,0x41,0x43,0x45,0x47,0x49,0x4b,0x4d,0x4f,0x51,0x53,0x55,0x58,0x5a,0x5c,0x5e,0x60,0x62,0x64, +0x67,0x69,0x6b,0x6d,0x6f,0x72,0x74,0x76,0x78,0x7b,0x7d,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x81,0x83,0x86,0x88,0x8a,0x8c,0x8f,0x91,0x93,0x95,0x97,0x9a,0x9c,0x9e,0xa0,0xa2,0xa4,0xa6,0xa9,0xab,0xad,0xaf,0xb1,0xb3,0xb5,0xb7,0xb9,0xbb,0xbd,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xc1,0xc3,0xc4,0xc6,0xc8,0xca,0xcc,0xcd,0xcf,0xd1,0xd3,0xd4,0xd6,0xd8,0xd9,0xdb,0xdc,0xde,0xdf,0xe1,0xe2,0xe3,0xe5,0xe6,0xe7,0xe9,0xea,0xeb,0xec,0xe9,0xea,0xeb,0xec,0xed,0xed,0xef,0xf0,0xf1,0xf2,0xf3,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf8,0xf9,0xfa,0xfa,0xfb,0xfb,0xfc,0xfc,0xfd,0xfd,0xfd,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfd,0xfd,0xfd,0xfc,0xfc,0xfb,0xfb,0xfa,0xfa,0xf9,0xf8,0xf8,0xf7,0xf6,0xf5,0xf4,0xf3,0xf3,0xf2,0xf1,0xf0,0xef,0xe9,0xea,0xeb,0xec,0xed,0xed,0xec,0xeb,0xea,0xe9,0xe7,0xe6,0xe5,0xe3,0xe2,0xe1,0xdf,0xde,0xdc,0xdb,0xd9,0xd8,0xd6,0xd4,0xd3,0xd1,0xcf,0xcd,0xcc,0xca,0xc8,0xc6,0xc4,0xc3,0xc1,0xbc,0xbd,0xbd,0xbe,0xbe,0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf, +0xad,0xab,0xa9,0xa6,0xa4,0xa2,0xa0,0x9e,0x9c,0x9a,0x97,0x95,0x93,0x91,0x8f,0x8c,0x8a,0x88,0x86,0x83,0x81,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x3e,0x3d,0x3c,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x78,0x77,0x76,0x75,0x75,0x75,0x75,0x75,0x75,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x89,0x89,0x89,0x89,0x89,0x89,0x88,0x87,0x86,0xb3,0xb2,0xb1,0xb0,0xaf,0xae,0xad,0xac,0xab,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xe2,0xe3,0xe4,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xda,0xda, +0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xe0,0xdf,0xde,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xde,0xdf,0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,0xf0,0xf1,0xf1,0xf1,0xf1,0xf1,0xf1,0xf1,0xf0,0xef,0xee,0xed,0xec,0xeb,0xea,0xe9,0xe8,0xe7,0xe6,0xe6,0xe6,0xe6,0xe6,0xe6,0xe6,0xe7,0xe8,0xe5,0xe4,0xe3,0xe2,0xe1,0xe0,0xdf,0xde,0xdd,0xdc,0xdb,0xda,0xd9,0xd8,0xd7,0xd6,0xd5,0xd4,0xd3,0xd2,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xd1,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbe,0xbd,0xbc,0xbb,0xba,0xb9,0xb8,0xb7,0xb6,0xb5,0xb4,0xb3,0xb3,0xb3,0xb3,0xb3,0xb3,0xb2,0xb1,0xb0,0xaf,0xae,0xad,0xac,0xab,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xab,0xac,0xad,0xae,0xaf,0xb0,0xb1,0xb2,0x87,0x88,0x89,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x8a,0x89,0x88,0x87,0x86,0x85,0x84,0x83,0x82,0x81,0x80,0x7f,0x7e,0x7d,0x7c,0x7b,0x7a,0x79,0x78,0x77,0x76,0x76,0x76,0x76, +0x76,0x76,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x7f,0x7e,0x7d,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x4d,0x4e,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4f,0x4e,0x4d,0x4c,0x4b,0x4a,0x49,0x48,0x47,0x46,0x45,0x44,0x43,0x42,0x41,0x40,0x3f,0x3e,0x3d,0x3c,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x0e,0x0d,0x0c,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,} ; +// (Number of pixels: 1000) // Store clock hands co-ordinates... uint8_t HandsX[192] = {} ; // Each hand requires 64 bytes - 3x64=192 uint8_t HandsY[192] = {} ; -int Hours=0, Mins=0, Secs=0, Angle, StartX, StartY, Radius ; +int Hours=0, Mins=0, Secs=0, LEDCtr=0, Angle, StartX, StartY, Radius ; float Radians ; int tmp ; @@ -129,32 +140,14 @@ public: MarginCount = 0 ; } - // Setter functions... - void ReInit () { - // Re-initialises DMA channels to their initial state. - // 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... - hw_clear_bits(&dma_hw->ch[data_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); - hw_clear_bits(&dma_hw->ch[ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); - // Reset the data transfer DMA's to the start of the data Bitmap... - dma_channel_set_read_addr(data_chan, &DAC_data[0], false); - // Re-enable both DMA channels associated with this DAC... - 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); - } - int Set(int _type, int _val) { switch (_type) { case _Freq_: Freq = _val ; // Frequency (numeric) - ReInit() ; // Stop and reset the DAC channel (no restart) DACspeed(Freq * Range) ; // Update State machine run speed break ; case _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 break ; case _Level_: @@ -194,7 +187,7 @@ public: strcpy(ResultStr,MarginVW) ; strcat(ResultStr,"Error - Minimum Frequency\n") ; } -// TBD - remove hardcoded Max frequency + // TBD - remove hardcoded Max frequency else if ((Freq*Range==1000000) && (_dirn==_Up)) { // Attempt to bump above upper limit // else if ((Freq*Range>=MaxDACfreq) && (_dirn==_Up)) { // Attempt to bump above upper limit MarginVW[MWidth - MarginCount] = '\0' ; // Calculate padding required for command characters and cursor @@ -260,9 +253,9 @@ public: 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 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... + // 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 ) @@ -270,8 +263,8 @@ 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 program counter 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 @@ -282,7 +275,7 @@ public: int _phase; float a,b,x1,x2,g1,g2; - // Scale the phase shift to match data size... + // Scale the phase shift to match data size... _phase = Phase * BitMapSize / 360 ; // Input range: 0 -> 360 (degrees) // Output range: 0 -> 255 (bytes) switch (Funct) { @@ -398,69 +391,58 @@ 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 -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, PICO_DEFAULT_LED_PIN ); - pio_sm_set_enabled(_pio, StateMachine, true); - } - - // Setter function... - void Set_Frequency(int _frequency){ - Freq = _frequency; // Copy parm to class var - // Frequency scaled by 2000 as blink.pio requires this number of cycles to complete... - 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 - } -}; - bool Repeating_Timer_Callback(struct repeating_timer *t) { + // Routine called 5 times per second... int i, steps=64, MidX=128, MidY=128 ; - // Bump the time... - if ((++Secs)>59) Secs=0 ; // Always bump seconds - if (Secs==0) { if ((++Mins)>59 ) Mins=0 ; } // Bump minutes when seconds = 0 - if ((Mins==0) && (Secs==0)) { if ((++Hours)>24) Hours=0 ; } // Bump hours when minutes and seconds = 0 + // printf("%d\n",LEDCtr) ; // Debug + LEDCtr -- ; + if (LEDCtr>0) { + // LED off, and no change to the time for 4 out of 5 cycles... + gpio_put(PICO_DEFAULT_LED_PIN, 0); // LED is connected to PICO_DEFAULT_LED_PIN + } else { + // Falls through here once per second. + LEDCtr = 5 ; + gpio_put(PICO_DEFAULT_LED_PIN, 1); // LED is connected to PICO_DEFAULT_LED_PIN - // Calculate seconds hand... - i=0, Radius=127 ; // Radius=Length of seconds hand - Angle=270-(Secs*6) ; // Angle in degrees, shifted 90 degree anti-clockwise - Radians=Angle*3.14159/180 ; // Angle in radians - StartX=Radius*cos(Radians)+MidX ; - StartY=Radius*sin(Radians)+MidY ; - while(i12 - Radians=Angle*3.14159/180 ; // Angle in radians - StartX=Radius*cos(Radians)+MidX ; - StartY=Radius*sin(Radians)+MidY ; - while(i59) Secs=0 ; // Always bump seconds + if (Secs==0) { if ((++Mins)>59 ) Mins=0 ; } // Bump minutes when seconds = 0 + if ((Mins==0) && (Secs==0)) { if ((++Hours)>24) Hours=0 ; } // Bump hours when minutes and seconds = 0 -// printf("%s%d:%d:%d - %d\n",MarginFW,Hours,Mins,Secs,tmp) ; // Debug + // Calculate seconds hand... + i=0, Radius=127 ; // Radius=Length of seconds hand + Angle=270-(Secs*6) ; // Angle in degrees, shifted 90 degree anti-clockwise + Radians=Angle*3.14159/180 ; // Angle in radians + StartX=Radius*cos(Radians)+MidX ; + StartY=Radius*sin(Radians)+MidY ; + while(i12 + Radians=Angle*3.14159/180 ; // Angle in radians + StartX=Radius*cos(Radians)+MidX ; + StartY=Radius*sin(Radians)+MidY ; + while(ilennn - Level = nnn ( 0->100%%%% )\n" "%sle+ - Level + 1\n" "%sle- - Level - 1\n" + "%sti - Time mode (display analog clock)\n" "%swhere...\n" - "%s = DAC channel A,B or Both\n" + "%s = DAC channel A,B or C=both\n" "%snnn = Three digit numeric value\n", MarginVW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, - MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW ) ; + MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, MarginFW, + MarginFW ) ; } -void SysInfo (DAC DACobj[], blink_forever LED_blinky) { +void SysInfo (DAC DACobj[] ) { // Print System Info and resource allocation detils, aligned to current margin settings... // Note: 1) The following string requires '%%%%' to print '%' because... // a) ResultStr is copied to outStr using sprintf - this reduces '%%%%' to '%%' @@ -534,15 +518,9 @@ void SysInfo (DAC DACobj[], blink_forever LED_blinky) { sprintf(ResultStr,"%s|----------------------------------------------------------|\n" "%s| System Info... |\n" "%s|----------------------------------------------------------|\n" + "%s| Target board: Pico |\n" "%s| RP2040 clock frequency: %7.3fMHz |\n" - "%s| Max DAC frequency: %7.3fMHz |\n" - "%s|----------------------------|-----------------------------|\n" - "%s| LED blinker | |\n" - "%s|----------------------------| |\n" - "%s| PIO: %2d | |\n" - "%s| State machine: %2d | |\n" - "%s| GPIO: %2d | |\n" - "%s| Frequency: %2dHz | |\n" + "%s| Max DAC frequency: %7.3fMHz |\n" "%s|----------------------------|-----------------------------|\n" "%s| DAC Channel A | DAC Channel B |\n" "%s|----------------------------|-----------------------------|\n" @@ -553,14 +531,9 @@ void SysInfo (DAC DACobj[], blink_forever LED_blinky) { "%s| Duty cycle: %3d%%%% | Duty cycle: %3d%%%% |\n" "%s| Sine harmonic: %1d | Sine harmonic: %1d |\n" "%s| Triangle Rise: %3d%%%% | Triangle Rise: %3d%%%% |\n", - MarginVW, MarginFW, MarginFW, + MarginVW, MarginFW, MarginFW, MarginFW, MarginFW, (float)clock_get_hz(clk_sys)/1000000, MarginFW, MaxDACfreq/1000000, - MarginFW, MarginFW, MarginFW, - MarginFW, LED_blinky.pioNum, - MarginFW, LED_blinky.StateMachine, - MarginFW, PICO_DEFAULT_LED_PIN, - MarginFW, LED_blinky.Freq, MarginFW, MarginFW, MarginFW, MarginFW, DACobj[_DAC_A].Level, DACobj[_DAC_B].Level, MarginFW, DACobj[_DAC_A].Freq, DACobj[_DAC_B].Freq, @@ -631,11 +604,6 @@ static void MCP41020_Write (uint8_t _ctrl, uint8_t _data) { // Scale the data byte to be in the range 0->255. // Transmit data over the SPI bus to the Digi-Pot. uint8_t buff[2]; - -// Depending on wiring, the MCP41020 Digi-Pot may have the channel selection bits reversed. If so, we will need to... -// if (_ctrl == 0x01) _ctrl = 0x02 ; // Swap channel A/B bits -// else if (_ctrl == 0x02) _ctrl = 0x01 ; // Note: Do not change if both channels are selected (code = 0x03) - buff[0] = _ctrl | 0x10 ; // Set command bit to Write data buff[1] = _data * 2.55 ; // Scale data byte (100%->255) cs_select(Level_CS) ; // Transmit data to Digi-Pot @@ -659,8 +627,8 @@ static void getLine() { } int SetVal(DAC DACobj[], int _Parm) { -// Common code for setting frequency, duty cycle, phase, waaveform and level. -// Handles options to set a specific value or bump up/down... + // Common code for setting frequency, duty cycle, phase, waveform and level. + // Handles options to set a specific value or bump up/down... if (inStr[3] == '+') { // Bump up and grab result for SPI display... if (SelectedChan & 0b01) result = DACobj[_DAC_A].Bump(_Parm,_Up); if (SelectedChan & 0b10) result = DACobj[_DAC_B].Bump(_Parm,_Up) ; @@ -670,73 +638,85 @@ int SetVal(DAC DACobj[], int _Parm) { } else { // Not a bump, so set the absolute value from Parm[0]... if (SelectedChan & 0b01) result = DACobj[_DAC_A].Set(_Parm,Parm[0]) ; if (SelectedChan & 0b10) result = DACobj[_DAC_B].Set(_Parm,Parm[0]) ; - dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels } + // Disable the Ctrl channels... + hw_clear_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_clear_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + // wait for Busy flag to clear... + + // Abort the data channels... + dma_channel_abort(DACobj[_DAC_A].data_chan); + dma_channel_abort(DACobj[_DAC_B].data_chan); + + // Reset the data transfer DMA's to the start of the data Bitmap... + dma_channel_set_read_addr(DACobj[_DAC_A].data_chan, &DACobj[_DAC_A].DAC_data[0], false); + dma_channel_set_read_addr(DACobj[_DAC_B].data_chan, &DACobj[_DAC_B].DAC_data[0], false); + + // Re-enable the Ctrl channels (doesn't restart data transfer)... + hw_set_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_set_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels return result ; } -int main() { - bool InvX=false, InvY=false ; // Clock mode flags to allow inverted output - set_sys_clock_khz(SysClock*1000, true) ; // Set Pico clock speed - MaxDACfreq = clock_get_hz(clk_sys) / BitMapSize ; // Calculate Maximum DAC output frequency for given CPU clock speed + int main() { + bool InvX=false, InvY=false ; // Clock display mode flags to allow inverted output + set_sys_clock_khz(SysClock*1000, true) ; // Set Pico clock speed + MaxDACfreq = clock_get_hz(clk_sys) / BitMapSize ; // Calculate Maximum DAC output frequency for given CPU clock speed stdio_init_all() ; - spi_init(SPI_PORT, 500000); // Set SPI0 at 0.5MHz. + + spi_init(SPI_PORT, 500000); // Set SPI0 at 0.5MHz... gpio_set_function(PIN_CLK, GPIO_FUNC_SPI); gpio_set_function(PIN_TX, GPIO_FUNC_SPI); -// Chip select is active-low, so initialise to a driven-high state... - gpio_init(Display_CS); - gpio_set_dir(Display_CS, GPIO_OUT); - gpio_put(Display_CS, 1); - gpio_init(Level_CS); - gpio_set_dir(Level_CS, GPIO_OUT); - gpio_put(Level_CS, 1); + gpio_init(Display_CS) ; // Initailse the required GPIO ports... + gpio_set_dir(Display_CS, GPIO_OUT) ; + gpio_put(Display_CS, 1) ; // Chip select is active-low, so initialise to high state + gpio_init(Level_CS) ; + gpio_set_dir(Level_CS, GPIO_OUT) ; + gpio_put(Level_CS, 1) ; // Chip select is active-low, so initialise to high state + gpio_init(PICO_DEFAULT_LED_PIN) ; + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT) ; + gpio_set_dir(PIN_CLK, GPIO_OUT) ; // Initialise remaining SPI connections... + gpio_set_dir(PIN_TX, GPIO_OUT) ; -// Setting Max slew rate and gpio drive strength keeps output linear at high frequencies... for (int i=0; i<16; i++) { - gpio_set_slew_rate(i, GPIO_SLEW_RATE_FAST); - gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_12MA); + gpio_set_slew_rate(i, GPIO_SLEW_RATE_FAST); // Setting Max slew rate and gpio drive strength keeps output + gpio_set_drive_strength(i, GPIO_DRIVE_STRENGTH_12MA); // linear at high frequencies... } -// Initialise remaining SPI connections... - gpio_set_dir(PIN_CLK, GPIO_OUT); - gpio_set_dir(PIN_TX, GPIO_OUT); - - struct repeating_timer timer; - add_repeating_timer_ms(-1000, Repeating_Timer_Callback, NULL, &timer); // 7ms - Short enough to prevent Nixie tube flicker - memset(MarginFW,' ',MWidth) ; // Initialise Fixed Width margin... MarginFW[MWidth] = '\0' ; // ... and terminate memset(MarginVW,' ',MWidth) ; // Initialise Variable Width margin... MarginVW[MWidth] = '\0' ; // ... and terminate ResultStr[0] = '\0' ; // Reset string -// 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. + // 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 DACobj[2]; // Array to hold the two DAC channel objects DACobj[_DAC_A].DAC_chan('A',pio1,0); // First DAC channel object in array - resistor network connected to GPIO0->8 DACobj[_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 strcpy(LastCmd,"?") ; // Hitting return will give 'Help' SPI_Display_Write(SysClock) ; // Pico system clock speed (in MHz) MCP41020_Write(0x3, 50) ; // Both channels -> 50% output level - LED_blinky.Set_Frequency(1); // Flash LED at 1Hz- waiting for USB connection - while (!stdio_usb_connected()) { sleep_ms(100); } // Wait for USB connection... - LED_blinky.Set_Frequency(10); // Flash LED at 10Hz - USB connected. SPI_Display_Write(DACobj[_DAC_A].Freq) ; // Frequency => SPI display -// Send (optional) start-up messages to terminal... + // Send (optional) start-up messages to terminal... VerText() ; // Version text printf(ResultStr) ; // Update terminal -// Atomic Restart - starting all 4 DMA channels simultaneously ensures phase sync between both DAC channels - dma_start_channel_mask(DAC_channel_mask); + // Atomic Restart - starting all 4 DMA channels simultaneously ensures phase sync between both DAC channels + dma_start_channel_mask(DAC_channel_mask); // Sets the 'Busy' flag in Ctrl reg + + struct repeating_timer timer; + add_repeating_timer_ms(-200, Repeating_Timer_Callback, NULL, &timer) ; // 5 x per second to blink LED while(1) { ParmCnt=0, Parm[0]=0, Parm[1]=0, Parm[2]=0, Parm[3]=0 ; // Reset all command line parameters @@ -760,7 +740,7 @@ int main() { DACobj[_DAC_A].StatusString() ; DACobj[_DAC_B].StatusString() ; } - if (inStr[0] == 'I') SysInfo(DACobj, LED_blinky); // TBD - inconsitant - make these global ?? + if (inStr[0] == 'I') SysInfo(DACobj); // TBD - inconsitant - make these global ?? } // For all remaining commands, the first character selects DAC channel A or B... @@ -797,57 +777,61 @@ int main() { } // Next two chars select the command... - if ((inStr[1]=='p')&(inStr[2]=='h')) SetVal(DACobj,_Phase_) ; // Phase - if ((inStr[1]=='l')&(inStr[2]=='e')) SetVal(DACobj,_Level_) ; // Level - if ((inStr[1]=='s')&(inStr[2]=='i')) SetVal(DACobj,_Sine_) ; // Sine wave (optional harmonic parameter) - if ((inStr[1]=='f')&(inStr[2]=='r')) { // Frequency - SetVal(DACobj,_Freq_) ; // Set value - dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels - } + if ((inStr[1]=='p')&(inStr[2]=='h')) SetVal(DACobj,_Phase_) ; // Phase + if ((inStr[1]=='l')&(inStr[2]=='e')) SetVal(DACobj,_Level_) ; // Level + if ((inStr[1]=='s')&(inStr[2]=='i')) SetVal(DACobj,_Sine_) ; // Sine wave (optional harmonic parameter) + if ((inStr[1]=='f')&(inStr[2]=='r')) SetVal(DACobj,_Freq_) ; // Frequency // The next two commands need different default values... - if (strlen(inStr)==3) Parm[0] = 50 ; // If no value provided, set default to 50 - if ((inStr[1]=='s')&(inStr[2]=='q')) SetVal(DACobj,_Square_) ; // Set Square wave (optional duty cycle parameter) - if ((inStr[1]=='t')&(inStr[2]=='r')) SetVal(DACobj,_Triangle_) ; // Set Triangle wave (optional duty cycle parameter) + if (strlen(inStr)==3) Parm[0] = 50 ; // If no value provided, set default to 50 + if ((inStr[1]=='s')&(inStr[2]=='q')) SetVal(DACobj,_Square_) ; // Set Square wave (optional duty cycle parameter) + if ((inStr[1]=='t')&(inStr[2]=='r')) SetVal(DACobj,_Triangle_) ; // Set Triangle wave (optional duty cycle parameter) - if ((inStr[1]=='t')&(inStr[2]=='i')) { // Time display... - DACobj[_DAC_A].Set(_Phase_, 0) ; // Phase lock - DACobj[_DAC_B].Set(_Phase_, 0) ; + if ((inStr[1]=='t')&(inStr[2]=='i')) { // Time display... + // Disable the Ctrl channels... + hw_clear_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_clear_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + // wait for Busy flag to clear... - for (int gpio = 0; gpio < 16; gpio++) { // Grabs the GPIO back from the State machines - gpio_init(gpio); - gpio_set_dir(gpio, GPIO_OUT); - } + // Abort the data channels... + dma_channel_abort(DACobj[_DAC_A].data_chan); + dma_channel_abort(DACobj[_DAC_B].data_chan); - // Disable both DMA channels associated with this DAC... - hw_clear_bits(&dma_hw->ch[DACobj[_DAC_A].data_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); - hw_clear_bits(&dma_hw->ch[DACobj[_DAC_B].data_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); - // Reset the data transfer DMA's to the start of the data Bitmap... - dma_channel_set_read_addr(DACobj[_DAC_A].data_chan, &DACobj[_DAC_A].DAC_data[0], false); - dma_channel_set_read_addr(DACobj[_DAC_B].data_chan, &DACobj[_DAC_B].DAC_data[0], false); + // Re-enable the Ctrl channels (doesn't restart data transfer)... + hw_set_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_set_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); pio_sm_set_enabled(pio1,0,false) ; // disable State machine 0 !! HARD CODED !! pio_sm_set_enabled(pio1,1,false) ; // disable State machine 1 + for (uint i=0; i<16; i++) { // Grab the GPIO back from the State machines + gpio_init(i); + gpio_set_dir(i, GPIO_OUT); + } gpio_clr_mask(0xff) ; // clear first 16 GPIO outputs ResultStr[0] = '\0' ; // String also used as a flag, so needs to be cleared while (ResultStr[0] == '\0') { // exit on keypress float Radians ; + int outX, outY ; // Draw the clock face... for (int i=0; i",MarginFW,Hours,Mins,Secs) ; } else if ((c=='q') or (c=='Q')) { - strcpy(ResultStr," Quit clock mode\n") ; // Prevents error message - // Re-enable both DMA channels associated with this DAC... - hw_set_bits(&dma_hw->ch[DACobj[_DAC_A].data_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); - hw_set_bits(&dma_hw->ch[DACobj[_DAC_B].data_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + for (uint i=0; i<16; i++) { pio_gpio_init(pio1, i); } // Hand the GPIO's back to the state machines + + // Reset the data transfer DMA's to the start of the data Bitmap... + dma_channel_set_read_addr(DACobj[_DAC_A].data_chan, &DACobj[_DAC_A].DAC_data[0], false); + dma_channel_set_read_addr(DACobj[_DAC_B].data_chan, &DACobj[_DAC_B].DAC_data[0], false); pio_sm_set_enabled(pio1,0,true) ; // Re-enable State machine 0 !! HARD CODED !! pio_sm_set_enabled(pio1,1,true) ; // Re-enable State machine 1 - // Re-init the state machines ???? + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels + + strcpy(ResultStr," Quit clock mode\n") ; // Prevents error message } } } @@ -896,31 +884,44 @@ int main() { // The final command is a continual loop creating the sweep function... 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 + // Parm[0]=Low frequency, Parm[1]=High frequency, Parm[2]=Scan speed, Parm[3]=Low/High pause i = Parm[0]; for (;;) { - if (SelectedChan & 0b01) result = DACobj[_DAC_A].Set(_Freq_,i) ; // Set frequency, display status - if (SelectedChan & 0b10) result = DACobj[_DAC_B].Set(_Freq_,i) ; // Set frequency, display status - dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels... - printf(ResultStr) ; // Update terminal - ResultStr[0] = '\0' ; // Reset the string variable - SPI_Display_Write(i); // Update SPI display + if (SelectedChan & 0b01) result = DACobj[_DAC_A].Set(_Freq_,i) ; // Set frequency, display status + if (SelectedChan & 0b10) result = DACobj[_DAC_B].Set(_Freq_,i) ; // Set frequency, display status + dma_start_channel_mask(DAC_channel_mask); // Atomic restart all 4 DMA channels... + printf(ResultStr) ; // Update terminal + ResultStr[0] = '\0' ; // Reset the string variable + 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 + // Disable the Ctrl channels... + hw_clear_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_clear_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + // wait for Busy flag to clear... + + // Abort the data channels... + dma_channel_abort(DACobj[_DAC_A].data_chan); + dma_channel_abort(DACobj[_DAC_B].data_chan); + + // Re-enable the Ctrl channels (doesn't restart data transfer)... + hw_set_bits(&dma_hw->ch[DACobj[_DAC_A].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + hw_set_bits(&dma_hw->ch[DACobj[_DAC_B].ctrl_chan].al1_ctrl, DMA_CH0_CTRL_TRIG_EN_BITS); + + dma_start_channel_mask(DAC_channel_mask); // Atomic restart both DAC channels i = i + dirn; - c = getchar_timeout_us (0); // Non-blocking char input + c = getchar_timeout_us (0); // Non-blocking char input if ((c>=32) & (c<=126)) { - strcpy(ResultStr," Exit sweep mode\n") ; // Prevents error message - break; } // exit on keypress - sleep_ms(Parm[2]); // Speed of scan + strcpy(ResultStr," Exit sweep mode\n") ; // Prevents error message + break; } // exit on keypress + sleep_ms(Parm[2]); // Speed of scan } } - if (strlen(ResultStr) == 0) { // No result can only mean unrecognised command - strcpy(MarginVW,MarginFW) ; // Reset Variable Width margin + if (strlen(ResultStr) == 0) { // No result can only mean unrecognised command + strcpy(MarginVW,MarginFW) ; // Reset Variable Width margin tmp = strlen(inStr) ; if (tmp != 0) tmp ++ ; // Bump to allow for cursor character MarginVW[MWidth - tmp] = '\0' ; // Calculate padding for input and cursor diff --git a/Function Generator/FunctionGenerator.uf2 b/Function Generator/FunctionGenerator.uf2 new file mode 100644 index 0000000000000000000000000000000000000000..a48d677dfc8389ddc7c934d172758329392c1f69 GIT binary patch literal 123392 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