diff --git a/Function Generator/CMakeLists.txt b/Function Generator/CMakeLists.txt index 51b32b2..1af378f 100644 --- a/Function Generator/CMakeLists.txt +++ b/Function Generator/CMakeLists.txt @@ -1,15 +1,15 @@ add_executable(pio_rotary_encoder) -# by default the header is generated into the build dir -pico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_blink.pio) pico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_rotary_encoder.pio) -# however, alternatively you can choose to generate it somewhere else (in this case in the source tree for check in) +pico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_blink.pio) +pico_generate_pio_header(pio_rotary_encoder ${CMAKE_CURRENT_LIST_DIR}/pio_DAC.pio) target_sources(pio_rotary_encoder PRIVATE pio_rotary_encoder.cpp) target_link_libraries(pio_rotary_encoder PRIVATE pico_stdlib hardware_pio + hardware_dma ) pico_add_extra_outputs(pio_rotary_encoder) diff --git a/Function Generator/pio_DAC.pio b/Function Generator/pio_DAC.pio new file mode 100644 index 0000000..b9345e5 --- /dev/null +++ b/Function Generator/pio_DAC.pio @@ -0,0 +1,65 @@ +.program pio_DAC +.wrap_target + set y,0b00000 + mov pins, y + set x, 31 +label01: + jmp x--, label01 +; + set y,0b00001 + mov pins, y + set x, 31 +label02: + jmp x--, label02 +; + set y,0b00010 + mov pins, y + set x, 31 +label03: + jmp x--, label03 +; + set y,0b00011 + mov pins, y + set x, 31 +label04: + jmp x--, label04 +; + set y,0b00100 + mov pins, y + set x, 31 +label05: + jmp x--, label05 +; + set y,0b00101 + mov pins, y + set x, 31 +label06: + jmp x--, label06 +; + set y,0b00110 + mov pins, y + set x, 31 +label07: + jmp x--, label07 +; + set y,0b00111 + mov pins, y + set x, 31 +label08: + jmp x--, label08 +; +.wrap + +% c-sdk { +// this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pin + +void pio_DAC_program_init(PIO pio, uint sm, uint offset, uint pin) { + for(uint i=2; i<6; i++) { + pio_gpio_init(pio, i); + } + pio_sm_set_consecutive_pindirs(pio, sm, 2, 5, true); + pio_sm_config c = pio_DAC_program_get_default_config(offset); + sm_config_set_out_pins(&c, 2, 5); + pio_sm_init(pio, sm, offset, &c); +} +%} diff --git a/Function Generator/pio_blink copy.pio b/Function Generator/pio_blink copy.pio new file mode 100644 index 0000000..ebc79b4 --- /dev/null +++ b/Function Generator/pio_blink copy.pio @@ -0,0 +1,27 @@ +.program pio_blink +.wrap_target + set y,0b00001 + mov pins, y + set x, 31 +label01: + jmp x--, label01 [31] +; + set y,0b00000 + mov pins, y + set x, 31 +label02: + jmp x--, label02 [31] +.wrap ; Blink forever! + +% c-sdk { +// this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pin + +void blink_program_init(PIO pio, uint sm, uint offset, uint pin, uint div) { + pio_gpio_init(pio, pin); + pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); + pio_sm_config c = pio_blink_program_get_default_config(offset); + sm_config_set_clkdiv(&c, div); // Set the clock divider for the state machine + sm_config_set_out_pins(&c, pin, 1); + pio_sm_init(pio, sm, offset, &c); +} +%} diff --git a/Function Generator/pio_blink.pio b/Function Generator/pio_blink.pio index 0b0d4b2..ebc79b4 100644 --- a/Function Generator/pio_blink.pio +++ b/Function Generator/pio_blink.pio @@ -1,34 +1,27 @@ -; -; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. -; -; SPDX-License-Identifier: BSD-3-Clause -; - -; SET pin 0 should be mapped to your LED GPIO - .program pio_blink - pull block - out y, 32 .wrap_target - mov x, y - set pins, 1 ; Turn LED on -lp1: - jmp x-- lp1 ; Delay for (x + 1) cycles, x is a 32 bit number - mov x, y - set pins, 0 ; Turn LED off -lp2: - jmp x-- lp2 ; Delay for the same number of cycles again -.wrap ; Blink forever! - + set y,0b00001 + mov pins, y + set x, 31 +label01: + jmp x--, label01 [31] +; + set y,0b00000 + mov pins, y + set x, 31 +label02: + jmp x--, label02 [31] +.wrap ; Blink forever! % c-sdk { // this is a raw helper function for use by the user which sets up the GPIO output, and configures the SM to output on a particular pin -void blink_program_init(PIO pio, uint sm, uint offset, uint pin) { +void blink_program_init(PIO pio, uint sm, uint offset, uint pin, uint div) { pio_gpio_init(pio, pin); pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); - pio_sm_config c = blink_program_get_default_config(offset); - sm_config_set_set_pins(&c, pin, 1); + pio_sm_config c = pio_blink_program_get_default_config(offset); + sm_config_set_clkdiv(&c, div); // Set the clock divider for the state machine + sm_config_set_out_pins(&c, pin, 1); pio_sm_init(pio, sm, offset, &c); } %} diff --git a/Function Generator/pio_rotary_encoder.cpp b/Function Generator/pio_rotary_encoder.cpp index 86cce61..ee0eb66 100644 --- a/Function Generator/pio_rotary_encoder.cpp +++ b/Function Generator/pio_rotary_encoder.cpp @@ -4,8 +4,10 @@ #include "hardware/pio.h" #include "hardware/irq.h" #include "hardware/clocks.h" +#include "hardware/dma.h" #include "pio_rotary_encoder.pio.h" #include "pio_blink.pio.h" +#include "pio_DAC.pio.h" // Ref. Commands to use when my useless laptop crashes causing VSCode to trash the environment... // cd ./build @@ -21,10 +23,10 @@ public: // constructor // rotary_encoder_A is the pin for the A of the rotary encoder. // The B of the rotary encoder has to be connected to the next GPIO. - RotaryEncoder(uint rotary_encoder_A) { + RotaryEncoder(uint rotary_encoder_A, uint freq) { uint8_t rotary_encoder_B = rotary_encoder_A + 1; PIO pio = pio0; // Use pio 0 - uint8_t sm = 0; // Use state machine 0 + uint8_t sm = 1; // Use state machine 1 pio_gpio_init(pio, rotary_encoder_A); gpio_set_pulls(rotary_encoder_A, false, false); // configure the used pins as input without pull up pio_gpio_init(pio, rotary_encoder_B); @@ -40,6 +42,9 @@ public: pio_sm_init(pio, sm, 16, &c); // init the state machine // Note: the program starts after the jump table -> initial_pc = 16 pio_sm_set_enabled(pio, sm, true); // enable the state machine + + printf("PIO:0, SM:%d running 'rotarty encoder' @ %dHz\n", sm, freq); + } void set_rotation(int _rotation) { // set the current rotation to a specific value @@ -70,11 +75,20 @@ private: class blink_forever { // Class to initialise a state macne to blink a GPIO pin public: - blink_forever(PIO pio, uint sm, uint offset, uint pin, uint freq) { - blink_program_init(pio, sm, offset, pin); - pio_sm_set_enabled(pio, sm, true); - printf("Blinking pin %d at %d Hz\n", pin, freq); - pio->txf[sm] = clock_get_hz(clk_sys) / (2 * freq); + blink_forever(PIO pio, uint sm, uint offset, uint pin, uint freq, uint blink_div) { + blink_program_init(pio, sm, offset, pin, blink_div); + pio_sm_set_enabled(pio, sm, true); + printf("PIO:0, SM:%d running 'blink' @ %dHz\n", sm, freq); + } +}; + +class DAC_write { +public: + DAC_write(PIO pio, uint sm, uint offset, uint pin, uint freq) { + pio_DAC_program_init(pio, sm, offset, pin ); + pio_sm_set_enabled(pio, sm, true); + printf("PIO:1, SM:%d running 'DAC' @ %dHz\n", sm, freq); +// pio->txf[sm] = clock_get_hz(clk_sys) / (2 * freq); // Write to FIFO } }; @@ -87,7 +101,14 @@ int EncoderPorts[2] = { 16, 17 }; // GPIO ports connecting to int NixieBuffer[3] = { 6, 7, 8 }; // Values to be displayed on Nixie tubes - Tube0=>1's // - Tube1=>10's // - Tube2=>100's -int raw_sin[sine_table_size] ; // Align DAC data +int raw_sin[sine_table_size] ; +unsigned short DAC_data[sine_table_size] __attribute__ ((aligned(2048))) ; // Align DAC data + +//#define DAC_config_chan_A 0b0011000000000000 // A-channel, 1x, active +const uint32_t transfer_count = sine_table_size ; // Number of DMA transfers per event +static inline void dma_channel_set_timer0(uint32_t timerval) { // Modify the TIMER0 register of the dma channel + dma_hw->timer[0] = timerval; +} void WriteCathodes (int Data) { // Create bit pattern on cathode GPIO's corresponding to the Data input... @@ -103,63 +124,116 @@ void WriteCathodes (int Data) { } int main() { +// set_sys_clock_khz(280000, true); // 1MHz from DAC (Note: kills Picoprobe connection) int scan = 0, lastval, temp; + static const float blink_freq = 16000; // Reduce SM clock to keep flash visible... + float blink_div = (float)clock_get_hz(clk_sys) / blink_freq; // ... calculate the required blink SM clock divider + static const float rotary_freq = 16000; // Clock speed reduced to eliminate rotary encoder jitter... + float rotary_div = (float)clock_get_hz(clk_sys) / rotary_freq; //... then calculate the required rotary encoder SM clock divider stdio_init_all(); // needed for printf - RotaryEncoder my_encoder(16); // the A of the rotary encoder is connected to GPIO 16, B to GPIO 17 - my_encoder.set_rotation(0); // initialize the rotatry encoder rotation as 0 - // Iterate through arrays to initialise the GPIO ports... - for ( uint i = 0; i < sizeof(DAC) / sizeof( DAC[0]); i++ ) { - gpio_init(DAC[i]); - gpio_set_dir(DAC[i], GPIO_OUT); // Set as output - } + +// Set up the GPIO pins... + const uint Onboard_LED = PICO_DEFAULT_LED_PIN; // Debug use - intialise the Onboard LED... + gpio_init(Onboard_LED); + gpio_set_dir(Onboard_LED, GPIO_OUT); + // Initialise the Nixie cathodes... for ( uint i = 0; i < sizeof(NixieCathodes) / sizeof( NixieCathodes[0]); i++ ) { gpio_init(NixieCathodes[i]); gpio_set_dir(NixieCathodes[i], GPIO_OUT); // Set as output } + // Initialise the Nixe anodes... for ( uint i = 0; i < sizeof(NixieAnodes) / sizeof( NixieAnodes[0]); i++ ) { gpio_init(NixieAnodes[i]); gpio_set_dir(NixieAnodes[i], GPIO_OUT); // Set as output } + // Initialise the rotary encoder... for ( uint i = 0; i < sizeof(RotaryEncoder) / sizeof( EncoderPorts[0]); i++ ) { gpio_init(EncoderPorts[i]); gpio_set_dir(EncoderPorts[i], GPIO_IN); // Set as input gpio_pull_up(EncoderPorts[i]); // Enable pull up } - const uint Onboard_LED = PICO_DEFAULT_LED_PIN; // Debug - also intialise the Onboard LED... - gpio_init(Onboard_LED); - gpio_set_dir(Onboard_LED, GPIO_OUT); +// Set up the State machines... PIO pio = pio0; uint offset = pio_add_program(pio, &pio_blink_program); - printf("Loaded program at %d\n", offset); + blink_forever my_blinker(pio, 0, offset, 25, blink_freq, blink_div); // SM0=>onboard LED + RotaryEncoder my_encoder(16, rotary_freq); // the A of the rotary encoder is connected to GPIO 16, B to GPIO 17 + pio = pio1; + offset = pio_add_program(pio, &pio_DAC_program); + DAC_write my_DAC(pio, 2, offset, 2, 100); // DAC; State machine #2, first GPIO=>2, 100Hz + my_encoder.set_rotation(0); // Zero the rotatry encoder rotation - blink_forever my_blinker(pio, 1, offset, 25, 10); // SM1, onboard LED, 10Hz -// blink_pin_forever(pio, 0, offset, 0, 3); // Optional: Specify additional SM's, different pins, -// blink_pin_forever(pio, 2, offset, 11, 1); // differnt frequencies - - // A-channel, 1x, active - #define DAC_config_chan_A 0b0011000000000000 - - // Build sine table +// Build sine table unsigned short DAC_data[sine_table_size] __attribute__ ((aligned(2048))) ; int i ; for (i=0; i<(sine_table_size); i++){ // raw_sin[i] = (int)(2047 * sin((float)i*6.283/(float)sine_table_size) + 2047); // 12 bit raw_sin[i] = (int)(15 * sin((float)i*6.283/(float)sine_table_size) + 15); // 5 bit - DAC_data[i] = DAC_config_chan_A | (raw_sin[i] & 0x0fff) ; +// DAC_data[i] = DAC_config_chan_A | (raw_sin[i] & 0x0fff) ; + DAC_data[i] = raw_sin[i] ; // memory alligned data } - // Setup data on DAC output... +/* // Get a free channel, panic() if there are none + int data_chan = dma_claim_unused_channel(true); + int ctrl_chan = dma_claim_unused_channel(true); + printf("data channel=%d\n", data_chan); + printf("ctrl channel=%d\n", ctrl_chan); */ + +/* // Setup the control channel + dma_channel_config c = dma_channel_get_default_config(ctrl_chan); // default configs + channel_config_set_transfer_data_size(&c, DMA_SIZE_32); // 32-bit txfers + channel_config_set_read_increment(&c, false); // no read incrementing + channel_config_set_write_increment(&c, false); // no write incrementing + + dma_channel_configure( + ctrl_chan, + &c, + &dma_hw->ch[data_chan].al1_transfer_count_trig, // txfer to transfer count trigger + &transfer_count, + 1, + false + ); */ + + /* // Confirm memory alignment + printf("\n\nBeginning: %x", &DAC_data[0]); + printf("\nFirst: %x", &DAC_data[1]); + printf("\nSecond: %x\n\n", &DAC_data[2]); + +// 16 bit transfers. Read address increments after each transfer. + dma_channel_config c2 = dma_channel_get_default_config(data_chan); // DREQ to Timer 0 is selected, so the DMA is throttled to audio rate + channel_config_set_transfer_data_size(&c2, DMA_SIZE_16); // 16 bit transfers + channel_config_set_read_increment(&c2, true); // increment the read adddress, don't increment write address + channel_config_set_write_increment(&c2, false); + dma_channel_set_timer0(0x0017ffff) ; // (X/Y)*sys_clk, where X is the first 16 bytes and Y is the second + // sys_clk is 125 MHz unless changed in code + channel_config_set_dreq(&c2, 0x3b); // 0x3b means timer0 (see SDK manual) + channel_config_set_chain_to(&c2, ctrl_chan); // chain to the controller DMA channel + channel_config_set_ring(&c2, false, 9); // 1 << 9 byte boundary on read ptr + // set wrap boundary. This is why we needed alignment! + */ +// dma_channel_configure( +// data_chan, // Channel to be configured +// &c2, // The configuration we just created +// &spi_get_hw(SPI_PORT)->dr, // write address +// DAC_data, // The initial read address (AT NATURAL ALIGNMENT POINT) +// sine_table_size, // Number of transfers; in this case each is 2 byte. +// false // Don't start immediately. +// ); + +// start the control channel +// dma_start_channel_mask(1u << ctrl_chan) ; + +// Setup data on DAC output... int DAC_count = 0, DAC_val; bool BitSet; - while (true) { // infinite loop to print the current rotation + while (true) { // infinite loop to print the current rotation if (my_encoder.get_rotation() != lastval) { temp = my_encoder.get_rotation(); printf("rotation=%d\n", temp); lastval = temp; - NixieBuffer[0] = temp % 10 ; // finished with temp, so ok to destroy it + NixieBuffer[0] = temp % 10 ; // finished with temp, so ok to destroy it temp /= 10 ; NixieBuffer[1] = temp % 10 ; temp /= 10 ; @@ -167,36 +241,36 @@ int main() { } if (scan==0) { - gpio_put(NixieAnodes[2], 0) ; // Turn off previous anode - WriteCathodes(NixieBuffer[0]); // Set up new data on cathodes (Units) - gpio_put(NixieAnodes[0], 1) ; // Turn on current anode + gpio_put(NixieAnodes[2], 0) ; // Turn off previous anode + WriteCathodes(NixieBuffer[0]); // Set up new data on cathodes (Units) + gpio_put(NixieAnodes[0], 1) ; // Turn on current anode } if (scan==1) { - gpio_put(NixieAnodes[0], 0) ; // Turn off previous anode - WriteCathodes(NixieBuffer[1]); // Set up new data on cathodes (10's) - gpio_put(NixieAnodes[1], 1) ; // Turn on current anode + gpio_put(NixieAnodes[0], 0) ; // Turn off previous anode + WriteCathodes(NixieBuffer[1]); // Set up new data on cathodes (10's) + gpio_put(NixieAnodes[1], 1) ; // Turn on current anode } if (scan==2) { - gpio_put(NixieAnodes[1], 0) ; // Turn off previous anode - WriteCathodes(NixieBuffer[2]); // Set up new data on cathodes (100's) - gpio_put(NixieAnodes[2], 1) ; // Turn on current anode + gpio_put(NixieAnodes[1], 0) ; // Turn off previous anode + WriteCathodes(NixieBuffer[2]); // Set up new data on cathodes (100's) + gpio_put(NixieAnodes[2], 1) ; // Turn on current anode } scan++; if (scan == 3) { scan = 0; } DAC_count++; if (DAC_count == 256) { DAC_count = 0; } - DAC_val = raw_sin[DAC_count]; // read value from Sine table - BitSet = (DAC_val & 1) ? true : false; // test bit 0 - gpio_put(DAC[0], BitSet); // Transfer to GPIO - BitSet = (DAC_val & 2) ? true : false; // test bit 1 - gpio_put(DAC[1], BitSet); // Transfer to GPIO - BitSet = (DAC_val & 4) ? true : false; // test bit 2 - gpio_put(DAC[2], BitSet); // Transfer to GPIO - BitSet = (DAC_val & 8) ? true : false; // test bit 3 - gpio_put(DAC[3], BitSet); // Transfer to GPIO - BitSet = (DAC_val & 16) ? true : false; // test bit 4 - gpio_put(DAC[4], BitSet); // Transfer to GPIO + // DAC_val = raw_sin[DAC_count]; // read value from Sine table + // BitSet = (DAC_val & 1) ? true : false; // test bit 0 + // gpio_put(DAC[0], BitSet); // Transfer to GPIO + // BitSet = (DAC_val & 2) ? true : false; // test bit 1 + // gpio_put(DAC[1], BitSet); // Transfer to GPIO + // BitSet = (DAC_val & 4) ? true : false; // test bit 2 + // gpio_put(DAC[2], BitSet); // Transfer to GPIO + // BitSet = (DAC_val & 8) ? true : false; // test bit 3 + // gpio_put(DAC[3], BitSet); // Transfer to GPIO + // BitSet = (DAC_val & 16) ? true : false; // test bit 4 + // gpio_put(DAC[4], BitSet); // Transfer to GPIO sleep_ms(2); }