Added DAC + state machine

master
Tony 2022-04-20 13:32:41 +01:00
rodzic 0c4ebba4b2
commit bf5d6687fd
5 zmienionych plików z 236 dodań i 77 usunięć

Wyświetl plik

@ -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)

Wyświetl plik

@ -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);
}
%}

Wyświetl plik

@ -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);
}
%}

Wyświetl plik

@ -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);
}
%}

Wyświetl plik

@ -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);
}