├── DMA ├── PWM_to_ADC.jpg ├── README.md └── DDS_ADC_PWM.ino ├── PIO ├── NTSC_1_bit │ ├── scope.jpg │ ├── schematic.png │ ├── TV_test_image.jpg │ ├── README.md │ ├── pio_ntsc.h │ ├── pio_ntsc.pio │ ├── PIO_NTSC_DMA_graphics.ino │ └── ascii_characters.h ├── NTSC_4_bit │ ├── schematic.png │ ├── scan_line_4_bit.jpg │ ├── image_4bit_improved.jpg │ ├── README.md │ ├── pio_ntsc_4bit.h │ ├── pio_ntsc_4bit.pio │ ├── PIO_NTSC_DMA_4_bit.ino │ └── ascii_characters.h ├── input_capture │ ├── pio_input_capture_2a.pio │ ├── pio_input_capture_2a.h │ ├── README.md │ └── PIO_input_capture_2a.ino ├── stepper_control │ ├── PIO_stepper_2.pio │ ├── pio_stepper_2.h │ ├── README.md │ └── PIO_stepper_2.ino ├── README.md └── Two_steppers │ ├── README.md │ ├── pio_stepper_two.pio │ ├── pio_stepper_two.h │ └── PIO_stepper_two_motor.ino ├── README.md └── MBED-setup ├── FirstExample.ino ├── Second_example.ino ├── README.md └── Third_example.ino /DMA/PWM_to_ADC.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/DMA/PWM_to_ADC.jpg -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/scope.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/PIO/NTSC_1_bit/scope.jpg -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/PIO/NTSC_1_bit/schematic.png -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/PIO/NTSC_4_bit/schematic.png -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/TV_test_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/PIO/NTSC_1_bit/TV_test_image.jpg -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/scan_line_4_bit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/PIO/NTSC_4_bit/scan_line_4_bit.jpg -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/image_4bit_improved.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brucland/RP2040/HEAD/PIO/NTSC_4_bit/image_4bit_improved.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RP2040 2 | Building an understanding of the Pi RP2040 by iterative programming and head-scratching. 3 | --- 4 | I am a retired lecturer from Electrical and Computer Engineering at Cornell University where I taught hardware design courses 5 | for over 20 years. I have taught using Atmel AVR, Microchip PIC32, and Altera/Intel Cyclone 2, 4 and 5 FPGAs. 6 | The courses are now being taught by Van Hunter Adams. 7 | 8 | Last years web pages are at 9 | https://people.ece.cornell.edu/land/courses/ece4760/index.html 10 | and 11 | https://people.ece.cornell.edu/land/courses/ece5760/index.html. 12 | For the last five years I also taught a technical writing course. 13 | https://people.ece.cornell.edu/land/courses/ece4920/index.html 14 | 15 | Now Hunter and I are looking for the next architecture to use for the microcontroller course. 16 | We have been experimenting with the C-SDK, MBED/Arduino, and MIcroPython. After messing with 17 | MicroPython for a few weeks, (see 18 | https://people.ece.cornell.edu/land/courses/ece4760/RP2040/index_rp2040_Micropython.html) 19 | I switched to the MBED/Arduino environment which allows free intermixing 20 | of MBED and C-SDK constructs. The resulting programs have NO Arduino syntax (except for the ino extension) 21 | but use the high-level MBED RTOS, mixed with C-SDK, on core0, and only the C-SDK on core1. 22 | -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/README.md: -------------------------------------------------------------------------------- 1 | PIO generated 1-bit, 255x200 resolution, NTSC video 2 | ---- 3 | You might think that NTSC video (analog broadcast video standard in USA) is completely obsolete but it is not. The TV monitor which displays the image is quite stupid and must be sent sync and pixel data as a single time-varying voltage in realtime. As a result, the communication time demands can be high. On an 8-bit AVR, raster data transfer required 80% of the CPU! On a 32-bit PIC32 with DMA, the percentage dropped to 8%. Using the PIO parallel i/o processors on the rp2040, the ARM CPU time required to keep the monitor supplied with data is zero percent. CPU time is still required to generate the graphics content (e.g. points, lines, text), of course. 4 | 5 | The video system fit into two PIO state machines, with different clock rates. The slower machine generates the sync pulses, which have a minimum feature time of aound 5 uec. The faster machine generates the 1-bit video, with a bit time of around 0.2 uSec. The rate gives a pixel density of 256 points across the scan-line active time of 51 uSec. The rest of the 63.55 uSec scan line is used for sync and image centering.The sync and video are then combined using a two bit DAC (schematic.png) to convert to a single voltage. Sync is defined as 0 volts, with black pixels at 0.3 volts, and white pixels at 1.3 volts. The start of each scan line is signaled by a sync pulse of just under 5 uSec. The start of a new frame is signalled by a sync pulse of about 180 uSec. One scan line is shown on the scope.jpg image. A test image is below. 6 | 7 | ![TV](TV_test_image.jpg) 8 | -------------------------------------------------------------------------------- /PIO/input_capture/pio_input_capture_2a.pio: -------------------------------------------------------------------------------- 1 | ; 2 | .program capture 3 | ; In which we try to use the PIO as a timer 4 | ; then blast the captured times to the IN FIFO 5 | 6 | ; gpio 4 rising edge occurance will be time 7 | ; stamped and transfered to FIFO 8 | ; This program just counts while for the edge, 9 | ; loads an time into a FIFO, 10 | ; Then counts and waits for a falling edge and loops 11 | ; 12 | set x 0x1f ; init x timer 0x0000001f -- can set only 5 bits 13 | mov x ::x ; filp the bits into the high positions 0xf8000000 14 | 15 | .wrap_target 16 | wait1: 17 | jmp pin got1 ; wait for rising edge 18 | jmp x-- wait1 ; loop is 2 cycles 19 | got1: 20 | in x 32 ; send the counter to isr and autopush 21 | ; 22 | wait0: 23 | jmp pin not0 24 | jmp wait1 ; got the 0, so wait for 1 25 | not0: 26 | jmp x-- wait0 ; loop is 2 cycles 27 | ; 28 | .wrap ; 29 | 30 | % c-sdk { 31 | // this is a raw helper function for use by the user which sets up the GPIO output, 32 | // and configures the SM to output on a particular pin 33 | 34 | void capture_program_init(PIO pio, uint sm, uint offset, uint pin) { 35 | // No OUTPUT pins 36 | //pio_gpio_init(pio, pin); 37 | // the jump pin 38 | pio_gpio_init(pio, 4); 39 | //pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); //no output 40 | pio_sm_config c = capture_program_get_default_config(offset); 41 | // Using 'out' NOT 'set' in modified program 42 | //sm_config_set_out_pins(&c, pin, 1); 43 | // no output 44 | //sm_config_set_set_pins(&c, pin, 1); 45 | // JMP pin is specified separately as GPIO #, GPIO 4 46 | sm_config_set_jmp_pin (&c, 4) ; 47 | // no output FIFO from core0, all input to core0 48 | sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_RX) ; 49 | // autopush the isr to eliminate one istrcution 50 | sm_config_set_in_shift (&c, true, true, 1) ; 51 | pio_sm_init(pio, sm, offset, &c); 52 | } 53 | %} 54 | -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/README.md: -------------------------------------------------------------------------------- 1 | PIO generated 4-bit, 255x200 resolution, NTSC video 2 | ---- 3 | You might think that NTSC video (analog broadcast video standard in USA) is completely obsolete but it is not. The TV monitor which displays the image is quite stupid and must be sent sync and pixel data as a single time-varying voltage in realtime. As a result, the communication time demands can be high. On an 8-bit AVR, raster data transfer required 80% of the CPU! On a 32-bit PIC32 with DMA, the percentage dropped to 8%. Using the PIO parallel i/o processors on the rp2040, the ARM CPU time required to keep the monitor supplied with data is zero percent. CPU time is still required to generate the graphics content (e.g. points, lines, text), of course. 4 | 5 | The video system fit into two PIO state machines, with different clock rates. The slower machine generates the sync pulses, which have a minimum feature time of aound 5 uec. The faster machine generates the 4-bit video, with a bit time of around 0.2 uSec. The rate gives a pixel density of 256 points across the scan-line active time of 51 uSec. The rest of the 63.55 uSec scan line is used for sync and image centering.The sync and video are then combined using a five-bit DAC (schematic.png) to convert to a single voltage. Sync is defined as 0 volts, with black pixels at 0.3 volts, and white pixels at 1.3 volts. The start of each scan line is signaled by a sync pulse of just under 5 uSec. The start of a new frame is signalled by a sync pulse of about 180 uSec. The images below show the grey scale test image with the 4-bit gray levels indicated below 16 rectangles. One scan line is shown in the other image in which you can see the voltage video levels coresponding to the grey scale in rectangles in the upper-left corner of the image screen. The scan line image also shows two h-sync pulses and a series of very short pulses corresponding to the intersection if the scan line with the different intensity sloping lines to the right. 6 | 7 | ![TV](image_4bit_improved.jpg) 8 | ![scan](scan_line_4_bit.jpg) 9 | -------------------------------------------------------------------------------- /PIO/input_capture/pio_input_capture_2a.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- // 2 | // This file is autogenerated by pioasm; do not edit! // 3 | // -------------------------------------------------- // 4 | 5 | #pragma once 6 | 7 | #if !PICO_NO_HARDWARE 8 | #include "hardware/pio.h" 9 | #endif 10 | 11 | // ------- // 12 | // capture // 13 | // ------- // 14 | 15 | #define capture_wrap_target 2 16 | #define capture_wrap 7 17 | 18 | static const uint16_t capture_program_instructions[] = { 19 | 0xe03f, // 0: set x, 31 20 | 0xa031, // 1: mov x, ::x 21 | // .wrap_target 22 | 0x00c4, // 2: jmp pin, 4 23 | 0x0042, // 3: jmp x--, 2 24 | 0x4020, // 4: in x, 32 25 | 0x00c7, // 5: jmp pin, 7 26 | 0x0002, // 6: jmp 2 27 | 0x0045, // 7: jmp x--, 5 28 | // .wrap 29 | }; 30 | 31 | #if !PICO_NO_HARDWARE 32 | static const struct pio_program capture_program = { 33 | .instructions = capture_program_instructions, 34 | .length = 8, 35 | .origin = -1, 36 | }; 37 | 38 | static inline pio_sm_config capture_program_get_default_config(uint offset) { 39 | pio_sm_config c = pio_get_default_sm_config(); 40 | sm_config_set_wrap(&c, offset + capture_wrap_target, offset + capture_wrap); 41 | return c; 42 | } 43 | 44 | // this is a raw helper function for use by the user which sets up the GPIO output, 45 | // and configures the SM to output on a particular pin 46 | void capture_program_init(PIO pio, uint sm, uint offset, uint pin) { 47 | // No OUTPUT pins 48 | //pio_gpio_init(pio, pin); 49 | // the jump pin 50 | pio_gpio_init(pio, 4); 51 | //pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); //no output 52 | pio_sm_config c = capture_program_get_default_config(offset); 53 | // Using 'out' NOT 'set' in modified program 54 | //sm_config_set_out_pins(&c, pin, 1); 55 | // no output 56 | //sm_config_set_set_pins(&c, pin, 1); 57 | // JMP pin is specified separately as GPIO #, GPIO 4 58 | sm_config_set_jmp_pin (&c, 4) ; 59 | // no output FIFO from core0, all input to core0 60 | sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_RX) ; 61 | // autopush the isr to eliminate one istrcution 62 | sm_config_set_in_shift (&c, true, true, 1) ; 63 | pio_sm_init(pio, sm, offset, &c); 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /PIO/stepper_control/PIO_stepper_2.pio: -------------------------------------------------------------------------------- 1 | ; Bruce Land -- 7/19/2021 2 | ; 3 | .program stepper 4 | ; Use 4 outputs to run a stepper motor pins 6 to 9 5 | ; get a pulse duration from cpu 32-bit 6 | ; get a step sequence from cpu (8-steps, 4 bits each) 7 | ; step through the sequence, each for the duration specified 8 | ; For full-step we get two sequences, for half-step get one 9 | ; The assumption is that this code will step at constant speed 10 | ; until signalled by the CPU. 11 | ; CPU signals new data on pin 4, but PIO finishes currect sequence 12 | ; then PIO ACKs on pin 10, read by pin 11 on CPU 13 | ; 14 | ; based on: 15 | ; https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4.py 16 | 17 | new_data: 18 | ; cpu will clear data-signal 19 | ; when it gets the ACK 20 | ; ACK the request 21 | set pins 1 ; ACK on pin 10 22 | ; 23 | pull block ; get pulse duration 24 | mov isr, osr ; store duration into isr 25 | ; 26 | pull block ; get step sequence 27 | mov y, osr ; store sequence in y 28 | ; 29 | set pins 0 ; clear the ACK 30 | ; 31 | cpu_wait: 32 | jmp pin cpu_wait ; wait for cpu to clear request 33 | 34 | .wrap_target 35 | jmp !osre step ; all steps used? 36 | ; only check for new data at end of full sequence 37 | jmp pin new_data ; cpu signals new data ready 38 | mov osr, y ; if all used, put more in osr 39 | ; 40 | ; now set 4 pins and count duration 41 | step: 42 | out pins 4 ; get the next 4 bits onto pins 43 | mov x, isr ; retreive pulse length 44 | count_dur: ; and count the duration 45 | jmp x-- count_dur 46 | .wrap 47 | 48 | % c-sdk { 49 | // this is a raw helper function for use by the user which sets up the GPIO output, 50 | // and configures the SM to output on a particular pin 51 | 52 | void stepper_program_init(PIO pio, uint sm, uint offset, uint pin) { 53 | 54 | pio_gpio_init(pio, pin); 55 | pio_gpio_init(pio, pin+1); 56 | pio_gpio_init(pio, pin+2); 57 | pio_gpio_init(pio, pin+3); 58 | pio_gpio_init(pio, 10); // ACK 59 | // 60 | pio_sm_set_out_pins (pio, sm, pin, 4) ; 61 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 4, true); 62 | pio_sm_set_set_pins (pio, sm, 10, 1) ; // ACK 63 | pio_sm_set_consecutive_pindirs(pio, sm, 10, 1, true); // ACK 64 | // 65 | pio_sm_config c = stepper_program_get_default_config(offset); 66 | // 67 | // Using 'out' and 'set' 68 | sm_config_set_out_pins(&c, pin, 4); 69 | sm_config_set_set_pins(&c, 10, 1); // ACK 70 | // 71 | // JMP pin is specified separately as GPIO #, GPIO 4 72 | sm_config_set_jmp_pin (&c, 4) ; 73 | // 74 | // the out FIFO, shift right, no autopull, threshold 31 75 | sm_config_set_out_shift (&c, true, false, 31) ; 76 | 77 | pio_sm_init(pio, sm, offset, &c); 78 | } 79 | %} 80 | -------------------------------------------------------------------------------- /PIO/stepper_control/pio_stepper_2.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- // 2 | // This file is autogenerated by pioasm; do not edit! // 3 | // -------------------------------------------------- // 4 | 5 | #pragma once 6 | 7 | #if !PICO_NO_HARDWARE 8 | #include "hardware/pio.h" 9 | #endif 10 | 11 | // ------- // 12 | // stepper // 13 | // ------- // 14 | 15 | #define stepper_wrap_target 7 16 | #define stepper_wrap 12 17 | 18 | static const uint16_t stepper_program_instructions[] = { 19 | 0xe001, // 0: set pins, 1 20 | 0x80a0, // 1: pull block 21 | 0xa0c7, // 2: mov isr, osr 22 | 0x80a0, // 3: pull block 23 | 0xa047, // 4: mov y, osr 24 | 0xe000, // 5: set pins, 0 25 | 0x00c6, // 6: jmp pin, 6 26 | // .wrap_target 27 | 0x00ea, // 7: jmp !osre, 10 28 | 0x00c0, // 8: jmp pin, 0 29 | 0xa0e2, // 9: mov osr, y 30 | 0x6004, // 10: out pins, 4 31 | 0xa026, // 11: mov x, isr 32 | 0x004c, // 12: jmp x--, 12 33 | // .wrap 34 | }; 35 | 36 | #if !PICO_NO_HARDWARE 37 | static const struct pio_program stepper_program = { 38 | .instructions = stepper_program_instructions, 39 | .length = 13, 40 | .origin = -1, 41 | }; 42 | 43 | static inline pio_sm_config stepper_program_get_default_config(uint offset) { 44 | pio_sm_config c = pio_get_default_sm_config(); 45 | sm_config_set_wrap(&c, offset + stepper_wrap_target, offset + stepper_wrap); 46 | return c; 47 | } 48 | 49 | // this is a raw helper function for use by the user which sets up the GPIO output, 50 | // and configures the SM to output on a particular pin 51 | void stepper_program_init(PIO pio, uint sm, uint offset, uint pin) { 52 | pio_gpio_init(pio, pin); 53 | pio_gpio_init(pio, pin+1); 54 | pio_gpio_init(pio, pin+2); 55 | pio_gpio_init(pio, pin+3); 56 | pio_gpio_init(pio, 10); // ACK 57 | // 58 | pio_sm_set_out_pins (pio, sm, pin, 4) ; 59 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 4, true); 60 | pio_sm_set_set_pins (pio, sm, 10, 1) ; // ACK 61 | pio_sm_set_consecutive_pindirs(pio, sm, 10, 1, true); // ACK 62 | // 63 | pio_sm_config c = stepper_program_get_default_config(offset); 64 | // 65 | // Using 'out' and 'set' 66 | sm_config_set_out_pins(&c, pin, 4); 67 | sm_config_set_set_pins(&c, 10, 1); // ACK 68 | // 69 | // JMP pin is specified separately as GPIO #, GPIO 4 70 | sm_config_set_jmp_pin (&c, 4) ; 71 | // 72 | // the out FIFO, shift right, no autopull, threshold 31 73 | sm_config_set_out_shift (&c, true, false, 31) ; 74 | pio_sm_init(pio, sm, offset, &c); 75 | } 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /PIO/stepper_control/README.md: -------------------------------------------------------------------------------- 1 | 2 | Background 3 | --- 4 | Unipolar (5 or 6 wire) stepper motors require a 4-phase 5 | sequence of pulses to rotate. Typically the job of producing the 6 | pulse trains is put in a interrupt-service-routine on small controllers. 7 | The PIO i/o co-processor on RP2040 can produce the sequenced pulses to offload 8 | the main cpu. The PIO unit waits for pulse-rate and sequence information from the 9 | cpu, then produces an indefinite number of pulses at that rate, until signalled by the 10 | cpu. When signalled, the PIO finishes the curent sequence of either 4 full steps, or 8 11 | half-steps, signals the cpu that it is ready, waits for new data, then starts the 12 | new sequence. The new data consists of two 32-bit words. The first is the length of each stepper pulse 13 | in machine cycles 125million/sec. The second is the desired sequence. A 32-bit word can contain 8 14 | full-steps or 8 half steps. 15 | A video of the PIO running a 28BYJ48 stepper is at https://youtu.be/TKRRkvhqN08 16 | 17 | The code sructure 18 | --- 19 | There are two source files which are compiled using the Arduino 2.0beta IDE and a PIO assembler. 20 | The C++ source file has a *.ino* extension 21 | The assembler I have been using is a web version at https://wokwi.com/tools/pioasm. PIOASM takes 22 | assembler source (of course) and also allows you to write some of the state machine C set up code in the same file. 23 | The output of the assembler is a C header file with an array representing the assembled PIO code, 24 | a couple of assembler-written C routines, and with the C code you specifed passed through to the header file. 25 | 26 | There are therefore three files: 27 | 1. the C++ source file (.ino), 28 | 1. the PIO assembler source (.pio) 29 | 1. the output of the PIO assembler (.h) which is included into the C++ source. 30 | 31 | The C program uses both MBED and 32 | C-SDK functions. MBED threads are used for multitasking and USB serial support. The PIO is initialized 33 | and started using C-SDK low-level functions. The PIO itself runs a weird, stripped down assembly language, in which 34 | each opcode may execute several related functions, but ALWAYS in one cycle (including conditional jumps). 35 | There are four 32-bit registers: x, y, osr, and isr. There are nine opcodes. Some of them used in this program: 36 | * The *pull* opcode grabs a 32-bit value from the CPU output FIFO and loads it into the *osr* register. 37 | * The *mov* command does what you would expect, but can optionally logically invert or bit-reverse on move. 38 | * The *set* opcode loads immediate data. In my program it sets i/o pin values directly. 39 | * The *out* opcode always operates on the osr register. It shifts out the number of bits you specify iteratively 40 | unitl it is empty or you reload osr. 41 | * The *jmp* opcode can perform unconditional or conditional junps in one cycle. The jump conditions are limited, but optimized for speed and i/o. 42 | 43 | Much of the function of the program depends on the context that you set up 44 | in configuration registers. For instance, the *jmp* opcode can be made conditional on a pin value, but only one pin, 45 | and that is specified in PIO setup C code. 46 | 47 | -------------------------------------------------------------------------------- /PIO/README.md: -------------------------------------------------------------------------------- 1 | Using the PIO 2 | ------------ 3 | Introduction: 4 | ------------- 5 | The PIO subsystem contains eight completely separate, small i/o state machines for fast, cycle accurate, 6 | i/o protocol generation. Examples might be extra SPI channels, VGA driver, DVI driver, pulse density modulation, 7 | or stepper motor sequencer. There is a nine-instruction assembly language used to program each PIO state machine. 8 | The instructions are Turing-complete, but not meant for general computation (e.g. don't use them to add 32-bit integers). 9 | Each state machine has transmit-receive FIFOs which can read/written by the M0 cores, or by the DMA system. 10 | Each state machine can also read/write any of the GPIO pins. YOu can toggle an i/o pin as fast as 62 MHz, but you will not 11 | see such a fast signal if you are using a solderless breadboard. 12 | 13 | -- PIO processor (there are 8) 14 | From the documentation: 15 | 16 | * Each PIO is programmable in the same sense as a processor: 17 | * the four state machines independently 18 | * execute short, sequential programs, to manipulate GPIOs and transfer data. Unlike a general 19 | * purpose processor, PIO state machines are highly specialised for IO, with a focus on determinism, 20 | * precise timing, and close integration with fixed-function hardware. Each state machine is equipped 21 | * with: 22 | * * Two 32-bit shift registers: either direction, any shift count 23 | * * Two 32-bit scratch registers 24 | * * 4x32 bit bus FIFO in each direction (TX/RX), reconfigurable as 8x32 in a single direction 25 | * * Fractional clock divider (16 integer, 8 fractional bits) 26 | * * Flexible GPIO mapping 27 | * * DMA interface, sustained throughput up to 1 word per clock from system DMA 28 | * * IRQ flag set/clear/status 29 | 30 | The assembly language: 31 | ------------------------ 32 | See C-SDK manual, section 3.4. The PIO has a total of nine instructions: 33 | *JMP, WAIT, IN, OUT, PUSH, PULL, MOV, IRQ, and SET* (from the RP2040 datasheet) 34 | Program memory is only 32 insetructions long, but each instruction can have several 35 | simultaneous effects, including a variable delay after execution (for pulse length trimming), the ability to 36 | set/clear a group of pins(side-set), and, of course, the main opcode function. Some instructions can also be set up 37 | to auto-pull or auto-push the i/o FIFOs at the same time they perform other functions. An extra SPI channel takes 38 | 5 instructions, PWM takes 7, VGA takes ~30. 39 | 40 | Merging PIO code with C: 41 | ------------------------ 42 | PIOASM runs as a separate step in the default C-SDK make-process. 43 | The C compile details are hidden by the Arduino-MBED interface (good), but aso hides the ability to build PIO code (bad). 44 | The solution is to use a stand-alone version of PIOASM. The version I have been using is a web version at 45 | https://wokwi.com/tools/pioasm. PIOASM takes assembler source (of course) and also allows you to write some of the 46 | state machine C set up code in the same file. The output of the assembler is a C header file with an array 47 | representing the assembled PIO code, a couple of assembler-written C routines, and with the C code you specifed 48 | passed through to the header file. 49 | -------------------------------------------------------------------------------- /MBED-setup/FirstExample.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A basic example to show multithreading in mbed OS. 3 | -> Convert all i/o to MBED calls from Arduino 4 | * Two threads are created to toggle pins as fast as ;possible 5 | * with or without yielding (and no ISR). 6 | -- With yield: two pulse trains at 142 KHz, pulse duration 0.1 uSec 7 | looks like task context switching time ~3 uSec 8 | -- no yield: alternating bursts of pulse trains at 4 MHz about 5 mSec long 9 | appears that context switch is ~3 uSec 10 | * With one yielding thread and ISR 11 | -- two pulse trains of 107 KHz, entry into ISR ~5 uSec, exit ~4 uSec 12 | * With two yielding threads and ISR 13 | -- three pulse trains of ~80 KHz, entry into ISR ~5 uSec, exit ~4 uSec 14 | -- p19 ISR input is connected to p20 pulse output 15 | * with USB serial (and two fast threads and ISR) you MUST: 16 | -- check to make sure the board is correct (aviod compile error) 17 | -- open a PuTTY window with the correct COM port number and baud rate 18 | -- DO NOT choose the Pico+COM board entry 19 | (the arduino monitor does not work in this mode) 20 | */ 21 | #include "rtos.h" 22 | #include "mbed.h" 23 | #include "USBSerial.h" 24 | 25 | using namespace rtos; // we will be using rtos::ThisThread 26 | using namespace mbed; 27 | 28 | // Declaring the Thread objects 29 | // NOTE: threads hang until serial monitor is attached 30 | Thread led20_fast_thread; 31 | Thread led21_fast_thread; 32 | Thread print_thread ; 33 | 34 | // Declare i/o - 35 | // set up an ISR source for timing, ISR input from p20 36 | InterruptIn int_p19(p19); 37 | // thread logic output to scope 38 | DigitalOut LED_p21(p21); 39 | // another thread logic output to scope + ISR 40 | DigitalOut LED_p20(p20); 41 | // ISR toggle logic to scope 42 | DigitalOut LED_p18(p18); 43 | // builtin LED 44 | DigitalOut LED_p25(p25); 45 | 46 | // Make an interrupt routine 47 | void ISR_function() { 48 | LED_p18 = 1 ; 49 | LED_p18 = 0 ; 50 | } 51 | 52 | // without yield about 1 MHz 53 | // with yield, about 460 KHz 54 | // 1/0 pulse length 500 nSec/ 55 | void led21_fast_function() { 56 | while(1){ 57 | LED_p21 = 1; 58 | ThisThread::sleep_for(1); 59 | LED_p21 = 0; 60 | //yield(); 61 | ThisThread::sleep_for(1); 62 | } 63 | } 64 | // 65 | void led20_fast_function() { 66 | while(1){ 67 | LED_p20 = 1 ; 68 | ThisThread::sleep_for(2); 69 | LED_p20 = 0 ; 70 | //yield(); 71 | ThisThread::sleep_for(1); 72 | } 73 | } 74 | 75 | // make a serial port 76 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 77 | USBSerial serial_port ; 78 | 79 | void serial_function() { 80 | int count=0; 81 | int test_in ; 82 | 83 | while(1){ 84 | count++ ; 85 | // test input and print 86 | // In PuTTY setup, Terminal panel: 87 | // set Local echo and Local editing to 'FORCE ON' 88 | serial_port.scanf("%d", &test_in) ; 89 | // 90 | serial_port.printf("%d %d\n\r", count, test_in); 91 | // 92 | LED_p25 = 1 ; 93 | ThisThread::sleep_for(250); 94 | LED_p25 = 0 ; 95 | } 96 | } 97 | // 98 | int main() { 99 | int_p19.fall(&ISR_function) ; 100 | led21_fast_thread.start(led21_fast_function); 101 | led20_fast_thread.start(led20_fast_function); 102 | print_thread.start(serial_function); 103 | } 104 | -------------------------------------------------------------------------------- /MBED-setup/Second_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A basic example to show multithreading in mbed OS. 3 | -> Convert all i/o to MBED calls from Arduino 4 | * Two yielding threads, one timer ISR, and one input pin ISR 5 | * With two yielding threads and two ISR 6 | -- Timer ISR triggers p20 pulse 7 | -- p19 ISR input is connected to p20 pulse output 8 | latency ~10 uSec, jitter 1 uSec 9 | probably 5 uSec to get out of timer isr + 5 to get into p19 isr 10 | * with USB serial you MUST: 11 | -- check to make sure the board is correct (aviod compile error) 12 | -- open a PuTTY window with the correct COM port number and baud rate 13 | -- DO NOT choose the Pico+COM board entry 14 | (the arduino monitor does not work in this mode) 15 | */ 16 | #include "rtos.h" 17 | #include "mbed.h" 18 | #include "USBSerial.h" 19 | 20 | using namespace rtos; // we will be using rtos::ThisThread 21 | using namespace mbed; 22 | 23 | // Declaring the Thread objects 24 | // NOTE: threads hang until serial monitor is attached 25 | Thread led21_thread; 26 | Thread print_thread ; 27 | 28 | // Set up timer ISR ticker to toggle pin 20 29 | Ticker led20_ISR ; 30 | // set up an ISR input from p20 31 | InterruptIn int_p19(p19); 32 | 33 | // Declare i/o - 34 | // thread logic output to scope 35 | DigitalOut LED_p21(p21); 36 | // another thread logic output to scope + ISR 37 | DigitalOut LED_p20(p20); 38 | // ISR toggle logic to scope 39 | DigitalOut LED_p18(p18); 40 | // builtin LED 41 | DigitalOut LED_p25(p25); 42 | 43 | // Make an ISR to be Triggered by timer to toggle p20 44 | void led20_function() { 45 | LED_p20 = 1 ; 46 | // stretch the pulse a little 47 | wait_us(2) ; 48 | LED_p20 = 0 ; 49 | } 50 | 51 | // Make an interrupt routine for pin19 ISR to toggle pin18 52 | void ISR_function() { 53 | LED_p18 = 1 ; 54 | wait_us(2) ; 55 | LED_p18 = 0 ; 56 | } 57 | 58 | // Thread function pin toggle 59 | // Thread functions DO NOT exit 60 | void led21_function() { 61 | while(1){ 62 | LED_p21 = 1; 63 | ThisThread::sleep_for(1); 64 | LED_p21 = 0; 65 | //yield(); 66 | ThisThread::sleep_for(1); 67 | } 68 | } 69 | 70 | // thread to make a serial port and use it 71 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 72 | // Thread functions DO NOT exit 73 | USBSerial serial_port ; 74 | void serial_function() { 75 | int count=0; 76 | int test_in ; 77 | 78 | while(1){ 79 | count++ ; 80 | // test input and print 81 | // In PuTTY setup, Terminal panel: 82 | // set Local echo and Local editing to 'FORCE ON' 83 | serial_port.scanf("%d", &test_in) ; 84 | // formatted output 85 | serial_port.printf("%d %d\n\r", count, test_in); 86 | // blink for testing 87 | LED_p25 = 1 ; 88 | ThisThread::sleep_for(250); 89 | LED_p25 = 0 ; 90 | } 91 | } 92 | 93 | // Now start everything 94 | int main() { 95 | // attach a function to the p19 instrrupt 96 | int_p19.fall(&ISR_function) ; 97 | // attach a function to Ticker interrupt 98 | // 20 uSec is near to the minimum for this method (thread timing is poor at this speed) 99 | led20_ISR.attach(&led20_function, std::chrono::microseconds(200)); 100 | // start blink thread 101 | led21_thread.start(led21_function); 102 | // start print thread 103 | print_thread.start(serial_function); 104 | } 105 | -------------------------------------------------------------------------------- /PIO/Two_steppers/README.md: -------------------------------------------------------------------------------- 1 | Background 2 | --- 3 | Unipolar (5 or 6 wire) stepper motors require a 4-phase 4 | sequence of pulses to rotate. Typically the job of producing the 5 | pulse trains is put in a interrupt-service-routine on small controllers. 6 | A single PIO i/o co-processor on RP2040 can produce the sequenced pulses for two motors, and count motor steps, to offload 7 | the main cpu. The PIO unit waits for pulse-rate and sequence information from the 8 | cpu, then produces a counted number of pulses at that rate. One of the PIO state machines counts steps and signals the stepper mahine to stop. 9 | When signalled, the PIO finishes the curent sequence of either 4 full steps, or 8 10 | half-steps, signals the cpu that it is ready, waits for new data, then starts the 11 | new sequence. The new data consists of three 32-bit words. The first is the length of each stepper pulse 12 | in machine cycles 125million/sec. The second is the desired sequence. A 32-bit word can contain 8 13 | full-steps or 8 half steps. The third is the desired number of counts. 14 | 15 | For a robot or plotter you need at least two motors. Using space-optimized PIO programs, I fit two drivers and step counters into one PIO. You could put two more motors on the other PIO. Two motors use a total of 10 pins. 4 for each motor, and one each for count-SM to stepper-SM signaling. With no load the motors draw around 0.5 amp total when running in half-step mode (better torque, more current). Using the 28BYJ-48 stepper and its ULN2003 driver board, GPIO 6 to 9 are connected to motor controller in1 to in4 on the first motor. GPIO 10 to 13 are connected to motor controller in1 to in4 on the second motor. GPIO 5 and 14 are used for signalling between state machines and should not have an external connection. 16 | 17 | The full step sequences turn on one phase at a time in one of two patterns for forward and reverse. In terms of the motor input lines the sequences are 1-2-3-4-1-2-3-4 or 4-3-2-1-4-3-2-1. These are encoded in a single 32-bit word as 0x12481248 and 0x84218421. The half step sequences turn on one or two consecutive pins in an overlapping pattern. The sequences are 1-12-2-23-3-34-4-41 and 41-4-34-3-23-2-12-1, encoded as 0x13264c89 and 0x98c46231. The 'stop' pattern, 0x80808080, just toggles the pin that the counter sate machine uses. This allows one (or both) motors to stop while still being timed by the counter state machine. The program is initialized to stop pattern. 18 | 19 | The code sructure 20 | --- 21 | There are two source files which are compiled using the Arduino 2.0beta IDE and a PIO assembler. 22 | The revised source files are a bit ponderous because of the state machine initialization routines. All that main does is to start all of the state machines, ask the user for parameters, then do nothing until the sequence of steps is finished. 23 | The C++ source file has a *.ino* extension. 24 | The assembler I have been using is a web version at https://wokwi.com/tools/pioasm. PIOASM takes 25 | assembler source (of course) and also allows you to write some of the state machine C set up code in the same file. 26 | The output of the assembler is a C header file with an array representing the assembled PIO code, 27 | a couple of assembler-written C routines, and with the C code you specifed passed through to the header file. 28 | 29 | There are therefore three files: 30 | 1. the C++ source file (.ino), 31 | 1. the PIO assembler source (.pio) 32 | 1. the output of the PIO assembler (.h) which is included into the C++ source. 33 | -------------------------------------------------------------------------------- /PIO/input_capture/README.md: -------------------------------------------------------------------------------- 1 | Background 2 | ---------- 3 | The RP2020 has no "input capture" peripherial that uses hardare to grab a time stamp for 4 | an externl event (edge on i/o pin). Both the AVR and PIC32 that I have used can capture times 5 | in hardware, and I find it useful. 6 | The PIO can be used to implement a fast timer/counter, 7 | detect i/o pin edges, and log the time stamps at full bus rate to a 8-slot hardware FIFO. The FIFO 8 | can then be read by the M0 core at some much slower rate. The implemented capture has a useful dynamic range 9 | from 10 MHz events down to a milliHertz. If the capture rate is slower than the thread execution rate, then 10 | an indefinite number of captures is possible. If the capture rate is very high, then only the first 8 will be logged to 11 | the FIFO, then the system will stall until the CPU reads the FIFO. 12 | 13 | The code structure 14 | ------------------ 15 | There are two source files which are compiled using the Arduino 2.0beta IDE and a PIO assembler. 16 | The C++ source file has a *.ino* extension 17 | The assembler I have been using is a web version at https://wokwi.com/tools/pioasm. PIOASM takes 18 | assembler source (of course) and also allows you to write some of the state machine C set up code in the same file. 19 | The output of the assembler is a C header file with an array representing the assembled PIO code, 20 | a couple of assembler-written C routines, and with the C code you specifed passed through to the header file. 21 | 22 | There are therefore three files: 23 | 1. the C++ source file (.ino), 24 | 1. the PIO assembler source (.pio) 25 | 1. the output of the PIO assembler (.h) which is included into the C++ source. 26 | 27 | The C program uses both MBED and 28 | C-SDK functions. MBED threads are used for multitasking and USB serial support. The PIO is initialized 29 | and started using C-SDK low-level functions. The PIO itself runs a weird, stripped down assembly language, in which 30 | each opcode may execute several related functions, but ALWAYS in one cycle (including conditional jumps). 31 | There are four 32-bit registers: x, y, osr, and isr. There are nine opcodes. Some of them used in this program: 32 | * The *pull* opcode grabs a 32-bit value from the CPU output FIFO and loads it into the *osr* register. 33 | * The *mov* command does what you would expect, but can optionally logically invert or bit-reverse on move. 34 | * The *set* opcode loads immediate data. In my program it sets i/o pin values directly. 35 | * The *in* opcode always operates on the isr register. It shifts in the number of bits you specify iteratively 36 | into isr register. The C code sets the PIO configuration to autopush the isr into the CPU input FIFO to save one instruction. 37 | * Since the ISA can only decriment a counter, and because the *set* command only takes 5-bit immediate values, two instructions 38 | are necessary to initialize register x to a large value. 39 | `set x 0x1f ` ; init x timer 0x0000001f -- can set only 5 bits 40 | `mov x ::x ` ; use the mov instruction to filp the bits into the high positions 0xf8000000 41 | 42 | As configured, the PIO state machine counts at 62.5 MHz, with an overhead of two cycles per timing event (easy to 43 | compensate for). If the input is a 10 MHz square wave, there will be 6 or 7 counts recorded in each FIFO entry (barely 44 | useful). If the input is a 1 KHz square wave there will be 62500 counts per event. The counter/timer will underflow after about 45 | 33 seconds, so the lower frequency limit is 0.03 Hz, unless you divide the PIO clock. 46 | -------------------------------------------------------------------------------- /MBED-setup/README.md: -------------------------------------------------------------------------------- 1 | Support for the RP2040 has been officially ported to Arduino environment. The interesting part of this is that the port uses the MBED ARM programming base. The overall effect is that you can use the Arudino easy install and toolchain, but use only C, C++ constructs. The MBED system includes a preemptive multtasker RTOS, but currently no support for multicore, PIO, SIO, or DMA subsystems. Also, some of the device interfaces (e.g. ADC) are limited compared to what the hardware can do. The RTOS defaults to round-robin scheduling, with a time slice of about 5 mSec, and with priority control of threads. A higher priority thread always runs, if it is ready. 2 | - MBED 6 docs https://os.mbed.com/docs/mbed-os/v6.10/introduction/index.html 3 | - Arduino MBED description https://blog.arduino.cc/2021/04/27/arduino-mbed-core-for-rp2040-boards/ 4 | 5 | Setup and preliminary tests 6 | The setup is quite easy. I liked the description at Tom's Hardware (https://www.tomshardware.com/news/raspberry-pi-pico-arduino-official). I am using the 2.0 beta IDE. Downloading the board support package using the board manager worked well, as long as you have a fast connection. MBED documentation and examples are a little scattered, but there is a lot of material. The Pico board is not recognized completely as a program target, so you need to disconnect the Pico, hold down the program switch, and connect it. I am using a switchable USB hub for this. Before you compile, make sure the board-select text field shows Raspberry Pi Pico, with a red X next to it. 7 | 8 | First example: 9 | The first example code aims at trying to understand the speed of task context switches, interrupts, and i/o primitives in MBED. Two threads just toggle i/o pins at around 500 Hz. Another thread handles serial communication through the USBserial device. One of the toggle threads is used as a signal driver for an input pin set up as an interrupt source. The associated ISR just toggles another i/o line. MAIN binds the ISR to the interrupt, starts three threads and exits. Setting a pin high-low as fast as possible results in a 100 nSec pulse. Entering the ISR takes about 5 uSec and exiting about 4 uSec. For comparision, raw C-SDK calls take 1 uSec to enter and 4 uSec to exit. Context switching betwen threads is about 3 uSec, so probably the ISR entry includes a RTOS context switch. After programming, the board enumerates as a Pico on some COM port, but don't choose this. Just open a PuTTY window at attach to the COM port. In PuTTY setup, Terminal panel: Set Local echo and Local editing to 'FORCE ON'. The Arduino serial monitor does not seem to work. This code is written without using any Arduino primitives or functions. The MBED libraries and RTOS libraries replace the Arduino libraries. 10 | 11 | Second example: 12 | The second example has a timer ISR, i/o pin ISR, a toggle thread, and the USBserial thread. The timers ISR toggles a pin, which acts as a driver for the i/o pin interrupt, which toggles another pin.The latency between the timer-triggered pulse and the i/o pin-tirggered pulse is about 10 uSec, with a jitter of 1 uSec. The latency corresponds to the time required to exit one ISR and enter another. The Ticker interrupt is good up to about 10 KHz, without loading the cpu too much. 13 | 14 | Third example: 15 | It turns out that MBED does not know that core1 exists, but core1 can be started and programmed using the C-SDK. Interrupts run at the C-SDK speed. There is no RTOS on core1. One posssible user model is to put interactive, multitasking threads on core0, and put fast computational routines on core1. Of course, either core can start hardware coprocessors running, then step out of the way of DMA, PWM, PIO, an other hardware systems. As a first compatability check (third example), I started two ISRs and two threads on core0, much like the first example above, but with the addition of C-SDK calls to start the ADC and read it. This shows that (at least some) C_SDK functions can co-exist with MBED. Core1 starts an ISR, checks its core ID, then endlessly toggles a pin and increments a spin-lock protected counter. Again, only C-SDK routines are used on core1. MBED functions simply crash core1. The core0 MAIN enables the two ISRs, starts two threads, initializes the spin-lock, resets core1, then launches core1. The USB serial thread running on core0 verifies that core1 indeed starts, then prints a sequence number, the ADC reading, and the spin-lock protected count from core1. Communication is via global variables. 16 | -------------------------------------------------------------------------------- /PIO/stepper_control/PIO_stepper_2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Stepper motor sequencer 3 | * 4 | */ 5 | // MBED 6 | #include "math.h" 7 | #include "rtos.h" 8 | #include "mbed.h" 9 | #include "USBSerial.h" 10 | // C-SDK 11 | #include "hardware/adc.h" 12 | #include "hardware/gpio.h" 13 | #include "hardware/dma.h" 14 | #include "hardware/pwm.h" 15 | #include "hardware/pio.h" 16 | #include "pico/multicore.h" 17 | #include "pico/time.h" 18 | #include "hardware/clocks.h" 19 | 20 | // multicore sync functions 21 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 16 to 31 OK 22 | // sycc.h also has get_cpuid function 23 | #include "hardware/sync.h" 24 | // 25 | // PIO program/setup include 26 | #include "pio_stepper_2.h" 27 | // 28 | using namespace rtos; // we will be using rtos::ThisThread 29 | using namespace mbed; 30 | 31 | // =========================================== 32 | // 33 | // core 0 will run the UI for PIO testing 34 | // and blinks an LED 35 | 36 | // === CORE 0 setup ========================== 37 | // Declaring the Thread objects (MBED) 38 | // NOTE: threads hang until serial monitor is attached 39 | Thread led25_thread; 40 | Thread print_thread ; 41 | 42 | // pio assembler link 43 | PIO pio = pio0; 44 | uint offset = pio_add_program(pio, &stepper_program); 45 | // set up pio state machine 46 | void pio_test_start(PIO pio, uint sm, uint offset, uint pin) { 47 | stepper_program_init(pio, sm, offset, pin); 48 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 49 | // drain the FIFOs 50 | pio_sm_clear_fifos (pio, sm); 51 | pio_sm_set_enabled(pio, sm, true); 52 | } 53 | 54 | // Thread function pin toggle (MBED) 55 | DigitalOut LED_p25(p25); 56 | // Thread functions DO NOT exit 57 | void led25_function() { 58 | while(1){ 59 | LED_p25 = 1; 60 | ThisThread::sleep_for(250); 61 | LED_p25 = 0; 62 | ThisThread::sleep_for(250); 63 | } 64 | } 65 | 66 | // thread to make a serial port and use it 67 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 68 | // 69 | // Thread functions DO NOT exit 70 | USBSerial serial_port ; // (MBED object) 71 | // 72 | DigitalOut pio_new_data_p4(p4); // MBED 73 | DigitalIn pio_ack(p11) ; 74 | // 75 | // serial thead 76 | void serial_function() { 77 | // number of 125 MHz ticks in the stepper step 78 | int step_count = 1000000 ; 79 | // step patterns 80 | #define step_stop 0x00000000 81 | // bits 1-2-3-4-1-2-3-4 82 | #define step_full_forward 0x12481248 83 | #define step_full_reverse 0x84218421 84 | // bits 1-12-2-23-3-34-4-41 85 | #define step_half_forward 0x13264c89 86 | #define step_half_reverse 0x98c46231 87 | unsigned int steps[5] = 88 | {step_stop, step_full_forward, step_full_reverse, step_half_forward, step_half_reverse} ; 89 | int pattern = 1; 90 | 91 | // the usual sart message 92 | serial_port.printf("Starting ...PIO\r\n") ; 93 | 94 | // === start the PIO === 95 | #define gpio6 6 96 | // start sm running 97 | pio_test_start(pio, 0, offset, gpio6); 98 | // give the state machine initial data 99 | pio_sm_put_blocking (pio, 0, step_count); 100 | // put this between data-send because PIO so fast 101 | pio_new_data_p4 = 0 ; 102 | pio_sm_put_blocking (pio, 0, steps[pattern]); 103 | 104 | 105 | // === 106 | while(1){ 107 | // test input and print 108 | // In PuTTY setup, Terminal panel: 109 | // set Local echo and Local editing to 'FORCE ON' 110 | serial_port.printf("step_count, pattern>") ; 111 | serial_port.scanf("%d %d", &step_count, &pattern) ; 112 | if ((pattern < 0) || (pattern > 3)) pattern = 0; 113 | // tell PIO there is new data 114 | pio_new_data_p4 = 1 ; 115 | // wait for ACK 116 | while(pio_ack.read() == 0) {}; 117 | // sned the data 118 | pio_sm_put_blocking (pio, 0, step_count); 119 | pio_sm_put_blocking (pio, 0, steps[pattern]); 120 | // signal new data sent 121 | pio_new_data_p4 = 0 ; 122 | 123 | //ThisThread::sleep_for(10); 124 | //busy_wait_us (50); 125 | } 126 | } 127 | 128 | // ==== core 0 MAIN: Start everything ============ 129 | // program entry 130 | int main() { 131 | // start blink thread -- MBED 132 | led25_thread.start(led25_function); 133 | // start print thread -- MBED 134 | print_thread.start(serial_function); 135 | } 136 | // end //// 137 | /////////// 138 | -------------------------------------------------------------------------------- /DMA/README.md: -------------------------------------------------------------------------------- 1 | With a new system, I generally test DMA by trivially moving an array, then see how fast I can do Direct Digital Synthesis (repetitive copy of an array to PWM), then move ADC samples as quickly as possible to an array or another PWM channel. The C-SDK on core1 was used to start a PWM slice and two DMA channels. The DMA channels were used to output a sine table to the PWM as fast as possible. PWM speed is about 0.5 MHz for 8-bit resolution, but the DMA channels can transfer up to about 60 MB/sec. The DMA channels are throttled to a specific sample rate to set the desired sine wave frequency. Two DMA channels are necessary to repetitively move an array. The source address for DMA channel zero will be the adddress of the sine buffer memory. The destination address will be fixed at the PWM slice one compare register. The transfer count will be the length of the sine buffer. DMA channel zero will chain to DMA channel 1 after the entire sine buffer is sent. DMA channel 1 will have a source address equal to the address, of the address, of the sine buffer. It will have a destination address of the DMA channel zero source address register, and a tranfer count of one word. DMA channel 1 will reload the source address of channel zero, then chain back to channel zero. The effect is that the sine waveform buffer will be sent continuously to the PWM. Running the PWM at full cpu frequency, with a top-count of 256 (8-bit resolution), works out to a PWM frequency of 500 KHz. 2 | 3 | The DMA channels are triggered by a high resolution timer built into the DMA subsystem. Setting the timer divider for a given output sine wave frequency is a bit obscure. 4 | TREQ source DREQ_TIMER0 = 0x3b is used to trigger DMA channel zero. The timer refered to is DMA timer 0, which has settable frequency as X/Y<1 where X is the top 16 bits of a register, and Y is the bottom 16 bits. It is easy to show that the settability of the frequency is very good at audio frequencies, with a useful range of 7 Hz to 15 KHz. The algorithm for going from a desired output frequency, Fout, to the values of X and Y is slightly involved. For a sine table of length L, and cpu clock frequency of Fclk: 5 | 6 | Fout = (Fclk/L)*(X/Y) 7 | The two settable variables are X and Y, but they are not independent, and must be integers. The solution I used is to set Y to maximum value (0xFFFF), then solve for the integer X0, which will systemmatically cause Fout to be a little low. The next step is to lower Y slightly to set the closest possible frequency. To do that we are going to Taylor expand: 8 | 1/(0xFFFF - ΔY) as (2^-16)/(1 -(2^-16* ΔY)) yielding (2^-16)*(1 +(2^-16 * ΔY)) 9 | As long as ΔY<211 the approximation is good to 0.1%. 10 | Now the output frequency can be written as 11 | Fout = (Fclk/L)*X* (2^-16)*(1 +(2^-16 * ΔY)) 12 | The steps are: solve for X to give lower bound frequency, then compute a small correction. 13 | X0 = Fout/Fclk * L * 2^16 14 | ΔY =( Fout - Fout(int(X0)))/Fclk * L * 2^32/int(X0) 15 | 16 | Core0 is running MBED threads, one of which handles USB serial input/output using the usual C scanf and printf functions.The serial thread blocks on user input, and it blocks on the FIFO connected to core1. The FIFO sends the user input frequency to core1 where it is converted to DMA timer settings. Core1 cannot run any MBED objects. It has to use only native C-SDK functions to set up the PWM and DMA channels and to listen to the FIFO input from core0. 17 | 18 | The DDS sine wave output is feed back to an ADC channel attached to a DMA channel transfering the samples to a PWM slice at ADC rate. The ADC rate is settable up to 0.5 Msamples/sec to as low as 735 samples/sec in auto-sample mode (adc_run(1);). The following image shows the synthesized 1 KHz waveform on the top trace with a synthesis rate of 0.5 Msamples/sec.The bottom trace is the voltage from the low-passed PWM feed back from ADC channel zero, which is sampled at 10 Ksamples/sec. The DDS synthesis is set up on core1 and the ADC playback is setup on core0. Once set up, all of the real work is done in hardware external to the two cores. Core0 is just waiting for user input of frequency and ADC sample rate, while core1 is blocking waiting for a valid frequency value in the FIFO. 19 | The ADC setup turns on autosample, sets channel 0, and configures the ADC FiFO to autoload new conversion values into the FIFO. A DMA channel is configured to watch the FIFO load, then transfer the new value to a PWM duty cycle register. Once set up, the hardware grabs samples and outputs to the PWM with no cpu involvment. The phase shift of the ADC sampled waveform is due to the low ADC sampleing rate I chose, and to the low-pass reconstruction filter. 20 | 21 | ![waveform](PWM_to_ADC.jpg) 22 | 23 | -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/pio_ntsc.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- // 2 | // This file is autogenerated by pioasm; do not edit! // 3 | // -------------------------------------------------- // 4 | 5 | #pragma once 6 | 7 | #if !PICO_NO_HARDWARE 8 | #include "hardware/pio.h" 9 | #endif 10 | 11 | // --------- // 12 | // ntsc_sync // 13 | // --------- // 14 | 15 | #define ntsc_sync_wrap_target 2 16 | #define ntsc_sync_wrap 21 17 | 18 | static const uint16_t ntsc_sync_program_instructions[] = { 19 | 0x80a0, // 0: pull block 20 | 0x60c0, // 1: out isr, 32 21 | // .wrap_target 22 | 0xf800, // 2: set pins, 0 [24] 23 | 0xe101, // 3: set pins, 1 [1] 24 | 0xf800, // 4: set pins, 0 [24] 25 | 0xe101, // 5: set pins, 1 [1] 26 | 0xf600, // 6: set pins, 0 [22] 27 | 0xe035, // 7: set x, 21 28 | 0xa046, // 8: mov y, isr 29 | 0xe101, // 9: set pins, 1 [1] 30 | 0xe100, // 10: set pins, 0 [1] 31 | 0xf701, // 11: set pins, 1 [23] 32 | 0x004a, // 12: jmp x--, 10 33 | 0xe100, // 13: set pins, 0 [1] 34 | 0xe301, // 14: set pins, 1 [3] 35 | 0xc000, // 15: irq nowait 0 36 | 0xb242, // 16: nop [18] 37 | 0x008d, // 17: jmp y--, 13 38 | 0xe03d, // 18: set x, 29 39 | 0xe100, // 19: set pins, 0 [1] 40 | 0xf701, // 20: set pins, 1 [23] 41 | 0x0053, // 21: jmp x--, 19 42 | // .wrap 43 | }; 44 | 45 | #if !PICO_NO_HARDWARE 46 | static const struct pio_program ntsc_sync_program = { 47 | .instructions = ntsc_sync_program_instructions, 48 | .length = 22, 49 | .origin = -1, 50 | }; 51 | 52 | static inline pio_sm_config ntsc_sync_program_get_default_config(uint offset) { 53 | pio_sm_config c = pio_get_default_sm_config(); 54 | sm_config_set_wrap(&c, offset + ntsc_sync_wrap_target, offset + ntsc_sync_wrap); 55 | return c; 56 | } 57 | 58 | // this is a raw helper function for use by the user which sets up the GPIO output, 59 | // and configures the SM to output on a particular pin 60 | void ntsc_sync_program_init(PIO pio, uint sm, uint offset, uint pin) { 61 | pio_gpio_init(pio, pin); 62 | pio_gpio_init(pio, pin+1); 63 | // 64 | pio_sm_set_out_pins (pio, sm, pin, 2) ; 65 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, true); 66 | // 67 | pio_sm_config c = ntsc_sync_program_get_default_config(offset); 68 | // 69 | // Using 'out' and 'set' 70 | sm_config_set_set_pins(&c, pin, 2); 71 | // 72 | // JMP pin is specified separately as GPIO #, GPIO 4 73 | // sm_config_set_jmp_pin (&c, 4) ; 74 | // 75 | // the out FIFO, shift right, no autopull, threshold 31 76 | // sm_config_set_out_shift (&c, true, false, 31) ; 77 | pio_sm_init(pio, sm, offset, &c); 78 | } 79 | 80 | #endif 81 | 82 | // --------- // 83 | // ntsc_data // 84 | // --------- // 85 | 86 | #define ntsc_data_wrap_target 0 87 | #define ntsc_data_wrap 6 88 | 89 | static const uint16_t ntsc_data_program_instructions[] = { 90 | // .wrap_target 91 | 0x20c0, // 0: wait 1 irq, 0 92 | 0xe027, // 1: set x, 7 93 | 0x80a0, // 2: pull block 94 | 0x7401, // 3: out pins, 1 [20] 95 | 0x00e3, // 4: jmp !osre, 3 96 | 0x0042, // 5: jmp x--, 2 97 | 0xe000, // 6: set pins, 0 98 | // .wrap 99 | }; 100 | 101 | #if !PICO_NO_HARDWARE 102 | static const struct pio_program ntsc_data_program = { 103 | .instructions = ntsc_data_program_instructions, 104 | .length = 7, 105 | .origin = -1, 106 | }; 107 | 108 | static inline pio_sm_config ntsc_data_program_get_default_config(uint offset) { 109 | pio_sm_config c = pio_get_default_sm_config(); 110 | sm_config_set_wrap(&c, offset + ntsc_data_wrap_target, offset + ntsc_data_wrap); 111 | return c; 112 | } 113 | 114 | // this is a raw helper function for use by the user which sets up the GPIO output, 115 | // and configures the SM to output on a particular pin 116 | void ntsc_data_program_init(PIO pio, uint sm, uint offset, uint pin) { 117 | //pio_gpio_init(pio, pin); 118 | pio_gpio_init(pio, pin+1); 119 | // 120 | pio_sm_set_out_pins (pio, sm, pin+1, 1) ; 121 | pio_sm_set_set_pins (pio, sm, pin+1, 1) ; 122 | pio_sm_set_consecutive_pindirs(pio, sm, pin+1, 1, true); 123 | // 124 | pio_sm_config c = ntsc_data_program_get_default_config(offset); 125 | // 126 | // Using 'out' and 'set' 127 | sm_config_set_out_pins(&c, pin+1, 1); 128 | sm_config_set_set_pins(&c, pin+1, 1); 129 | // 130 | // JMP pin is specified separately as GPIO #, GPIO 4 131 | // sm_config_set_jmp_pin (&c, 4) ; 132 | // 133 | // the out FIFO, shift right, no autopull, threshold 31 134 | sm_config_set_out_shift (&c, false, false, 32) ; 135 | // and join to make 8 entries 136 | sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_TX) ; 137 | pio_sm_init(pio, sm, offset, &c); 138 | } 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/pio_ntsc_4bit.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- // 2 | // This file is autogenerated by pioasm; do not edit! // 3 | // -------------------------------------------------- // 4 | 5 | #pragma once 6 | 7 | #if !PICO_NO_HARDWARE 8 | #include "hardware/pio.h" 9 | #endif 10 | 11 | // --------- // 12 | // ntsc_sync // 13 | // --------- // 14 | 15 | #define ntsc_sync_wrap_target 1 16 | #define ntsc_sync_wrap 20 17 | 18 | static const uint16_t ntsc_sync_program_instructions[] = { 19 | 0x60c0, // 0: out isr, 32 20 | // .wrap_target 21 | 0xf800, // 1: set pins, 0 [24] 22 | 0xe101, // 2: set pins, 1 [1] 23 | 0xf800, // 3: set pins, 0 [24] 24 | 0xe101, // 4: set pins, 1 [1] 25 | 0xf600, // 5: set pins, 0 [22] 26 | 0xe035, // 6: set x, 21 27 | 0xa046, // 7: mov y, isr 28 | 0xe101, // 8: set pins, 1 [1] 29 | 0xe100, // 9: set pins, 0 [1] 30 | 0xf701, // 10: set pins, 1 [23] 31 | 0x0049, // 11: jmp x--, 9 32 | 0xe100, // 12: set pins, 0 [1] 33 | 0xe301, // 13: set pins, 1 [3] 34 | 0xc000, // 14: irq nowait 0 35 | 0xb242, // 15: nop [18] 36 | 0x008c, // 16: jmp y--, 12 37 | 0xe03d, // 17: set x, 29 38 | 0xe100, // 18: set pins, 0 [1] 39 | 0xf701, // 19: set pins, 1 [23] 40 | 0x0052, // 20: jmp x--, 18 41 | // .wrap 42 | }; 43 | 44 | #if !PICO_NO_HARDWARE 45 | static const struct pio_program ntsc_sync_program = { 46 | .instructions = ntsc_sync_program_instructions, 47 | .length = 21, 48 | .origin = -1, 49 | }; 50 | 51 | static inline pio_sm_config ntsc_sync_program_get_default_config(uint offset) { 52 | pio_sm_config c = pio_get_default_sm_config(); 53 | sm_config_set_wrap(&c, offset + ntsc_sync_wrap_target, offset + ntsc_sync_wrap); 54 | return c; 55 | } 56 | 57 | // this is a raw helper function for use by the user which sets up the GPIO output, 58 | // and configures the SM to output on a particular pin 59 | void ntsc_sync_program_init(PIO pio, uint sm, uint offset, uint pin) { 60 | pio_gpio_init(pio, pin); 61 | pio_gpio_init(pio, pin+1); 62 | // 63 | pio_sm_set_out_pins (pio, sm, pin, 2) ; 64 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, true); 65 | // 66 | pio_sm_config c = ntsc_sync_program_get_default_config(offset); 67 | // 68 | // Using 'out' and 'set' 69 | sm_config_set_set_pins(&c, pin, 2); 70 | // 71 | // JMP pin is specified separately as GPIO #, GPIO 4 72 | // sm_config_set_jmp_pin (&c, 4) ; 73 | // 74 | // the out FIFO, shift right, autopull, threshold 31 75 | sm_config_set_out_shift (&c, true, true, 31) ; 76 | pio_sm_init(pio, sm, offset, &c); 77 | } 78 | 79 | #endif 80 | 81 | // --------- // 82 | // ntsc_data // 83 | // --------- // 84 | 85 | #define ntsc_data_wrap_target 0 86 | #define ntsc_data_wrap 6 87 | 88 | static const uint16_t ntsc_data_program_instructions[] = { 89 | // .wrap_target 90 | 0x20c0, // 0: wait 1 irq, 0 91 | 0xe03f, // 1: set x, 31 92 | 0x80a0, // 2: pull block 93 | 0x7404, // 3: out pins, 4 [20] 94 | 0x00e3, // 4: jmp !osre, 3 95 | 0x0042, // 5: jmp x--, 2 96 | 0xe000, // 6: set pins, 0 97 | // .wrap 98 | }; 99 | 100 | #if !PICO_NO_HARDWARE 101 | static const struct pio_program ntsc_data_program = { 102 | .instructions = ntsc_data_program_instructions, 103 | .length = 7, 104 | .origin = -1, 105 | }; 106 | 107 | static inline pio_sm_config ntsc_data_program_get_default_config(uint offset) { 108 | pio_sm_config c = pio_get_default_sm_config(); 109 | sm_config_set_wrap(&c, offset + ntsc_data_wrap_target, offset + ntsc_data_wrap); 110 | return c; 111 | } 112 | 113 | // this is a raw helper function for use by the user which sets up the GPIO output, 114 | // and configures the SM to output on a particular pin 115 | void ntsc_data_program_init(PIO pio, uint sm, uint offset, uint pin) { 116 | //pio_gpio_init(pio, pin); 117 | pio_gpio_init(pio, pin+1); 118 | pio_gpio_init(pio, pin+2); 119 | pio_gpio_init(pio, pin+3); 120 | pio_gpio_init(pio, pin+4); 121 | // 122 | pio_sm_set_out_pins (pio, sm, pin+1, 4) ; 123 | pio_sm_set_set_pins (pio, sm, pin+1, 4) ; 124 | pio_sm_set_consecutive_pindirs(pio, sm, pin+1, 4, true); 125 | // 126 | pio_sm_config c = ntsc_data_program_get_default_config(offset); 127 | // 128 | // Using 'out' and 'set' 129 | sm_config_set_out_pins(&c, pin+1, 4); 130 | sm_config_set_set_pins(&c, pin+1, 4); 131 | // 132 | // JMP pin is specified separately as GPIO #, GPIO 4 133 | // sm_config_set_jmp_pin (&c, 4) ; 134 | // 135 | // the out FIFO, shift right, no autopull, threshold 31 136 | sm_config_set_out_shift (&c, false, false, 32) ; 137 | // and join to make 8 entries 138 | sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_TX) ; 139 | pio_sm_init(pio, sm, offset, &c); 140 | } 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/pio_ntsc_4bit.pio: -------------------------------------------------------------------------------- 1 | ; Bruce Land -- Aug 2021 2 | ; 3 | /* 4 | * NTSC sync and data 5 | * using fake progreassive scan 6 | * See https://sagargv.blogspot.com/2014/07/ntsc-demystified-color-demo-with.html 7 | * 8 | * 263 scan lines. 9 | * one line is exactly 63.555 microseconds long 10 | * H-sync pulse is zero volts for 4.7 uSec, but not too critical 11 | * V-sync pulse is zero volts for 29.4 uSec, but not too critical 12 | * if line 1 is V-synch then content can start on line 21 or so and continue 13 | * until line 260 14 | * LInes 1-20 and 261-263 should hold video data at zero 15 | * about 55 uSec of each active scan line can contain pixels 16 | * a 5 Mpixel/sec rate gives about 55*5 = 275 pixels/line, so use 256 pixels 17 | * 18 | * PIO implementation: 19 | * One line time should require waits of <32 cycles so that loops are not required. 20 | * Choose sync machine clock rate so that 27 cycles give 63.555 uSec, which is 21 | * 15,734 Hz. So machine clock should be 27*15734=424828 Hz 22 | * Ths colck choice means that an H-sync pulse is exactly two cycles and 23 | * V-sync is approx 13 cycles. 24 | * The clock divider is therfore 125e6/424828 = 294.2367 25 | */ 26 | 27 | .program ntsc_sync 28 | ; https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4.py 29 | ; 30 | ; get active line count 200 31 | ; and store for future use 32 | ; 3 sync + 30 blank + 200 active + 30 blank 33 | ; -- autopull block 34 | out isr 32 35 | .wrap_target 36 | ; start new frame with V-sync pulse for 3 line times of inverted sync 37 | ; low 2 then high for 25 clocks (to total 27 cycles) 38 | ; 27 cycles is the length of EVERY line 39 | ; v sync line 1 40 | set pins 0 [24] 41 | set pins 1 [1] 42 | ; 43 | ; v sync line 2 44 | set pins 0 [24] 45 | set pins 1 [1] 46 | ; 47 | ; v sync line 3 48 | set pins 0 [22] // 49 | set x 21 ; preload for 22 lines blank_loop 50 | mov y isr ; preload for data_loop 51 | set pins 1 [1] // 52 | ; 53 | ; write 22 blank lines (adjust this for vertical centering) 54 | blank_loop: 55 | ; 56 | set pins 0 [1] 57 | ; rest of line is high 58 | ; 23 wait + 1 set + 1 jmp + 2 cycles previous 59 | set pins 1 [23] 60 | jmp x-- blank_loop 61 | ; 62 | ; write 208 lines an trigger ntsc_data machine 63 | ; use 64 | data_loop: 65 | ; set sync low for 2 cycles 66 | set pins 0 [1] 67 | ; set sync high for 4 cycles ('back porch') 68 | set pins 1 [3] 69 | ; 70 | ; genearate signal for data machine 71 | irq nowait 0 72 | ; wait until end of line 2 set + 4 set + irq + jmp + 19 73 | nop [18] 74 | ; 75 | jmp y-- data_loop 76 | 77 | ; 78 | ; write blank lines (>=3) to make up 263 total lines 79 | ; 3 v_sync line + 22 blanks + 208 data lines = 233 lines so far 80 | ; so 30 more lines 81 | set x 29 82 | end_blank_loop: 83 | ; two cycles of zero 84 | set pins 0 [1] 85 | ; rest of line is high 86 | ; 23 wait + 1 set + 1 jmp + 2 previous 87 | set pins 1 [23] //15 88 | jmp x-- end_blank_loop 89 | .wrap 90 | 91 | % c-sdk { 92 | // this is a raw helper function for use by the user which sets up the GPIO output, 93 | // and configures the SM to output on a particular pin 94 | 95 | void ntsc_sync_program_init(PIO pio, uint sm, uint offset, uint pin) { 96 | 97 | pio_gpio_init(pio, pin); 98 | pio_gpio_init(pio, pin+1); 99 | // 100 | pio_sm_set_out_pins (pio, sm, pin, 2) ; 101 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, true); 102 | // 103 | pio_sm_config c = ntsc_sync_program_get_default_config(offset); 104 | // 105 | // Using 'out' and 'set' 106 | sm_config_set_set_pins(&c, pin, 2); 107 | // 108 | // JMP pin is specified separately as GPIO #, GPIO 4 109 | // sm_config_set_jmp_pin (&c, 4) ; 110 | // 111 | // the out FIFO, shift right, autopull, threshold 31 112 | sm_config_set_out_shift (&c, true, true, 31) ; 113 | 114 | pio_sm_init(pio, sm, offset, &c); 115 | } 116 | %} 117 | 118 | .program ntsc_data 119 | ; https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4.py 120 | ; 121 | .wrap_target 122 | ; wait for signal to start video line from sync machine 123 | wait 1 irq 0 124 | ; number of data words in a line-1 125 | set x 31 ; 126 | ; 127 | ; get 32 bits 128 | next_word: 129 | pull block 130 | more_bits: 131 | ; slow down the output to about 5 MHz 132 | out pins 4 [20] 133 | jmp!OSRE more_bits 134 | jmp x-- next_word 135 | ; zero the data lines 136 | set pins 0b0000 137 | .wrap 138 | 139 | % c-sdk { 140 | // this is a raw helper function for use by the user which sets up the GPIO output, 141 | // and configures the SM to output on a particular pin 142 | 143 | void ntsc_data_program_init(PIO pio, uint sm, uint offset, uint pin) { 144 | 145 | //pio_gpio_init(pio, pin); 146 | pio_gpio_init(pio, pin+1); 147 | pio_gpio_init(pio, pin+2); 148 | pio_gpio_init(pio, pin+3); 149 | pio_gpio_init(pio, pin+4); 150 | // 151 | pio_sm_set_out_pins (pio, sm, pin+1, 4) ; 152 | pio_sm_set_set_pins (pio, sm, pin+1, 4) ; 153 | pio_sm_set_consecutive_pindirs(pio, sm, pin+1, 4, true); 154 | // 155 | pio_sm_config c = ntsc_data_program_get_default_config(offset); 156 | // 157 | // Using 'out' and 'set' 158 | sm_config_set_out_pins(&c, pin+1, 4); 159 | sm_config_set_set_pins(&c, pin+1, 4); 160 | // 161 | // JMP pin is specified separately as GPIO #, GPIO 4 162 | // sm_config_set_jmp_pin (&c, 4) ; 163 | // 164 | // the out FIFO, shift right, no autopull, threshold 31 165 | sm_config_set_out_shift (&c, false, false, 32) ; 166 | // and join to make 8 entries 167 | sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_TX) ; 168 | 169 | pio_sm_init(pio, sm, offset, &c); 170 | } 171 | %} -------------------------------------------------------------------------------- /PIO/input_capture/PIO_input_capture_2a.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * input time capture using PIO as a timer/edge detector 3 | * 51 clock ticks per cycle at 1 MHz input 4 | * works to at least 10 MHz, but no usable accuracy above ~2 MHz 5 | * -- PIO has 8 instructions which implements a counter and edge state machine 6 | * -- core0 readback dumps up to 8 time stamps from the FIFO at high frequency 7 | * can run indefinitely at below a few KHz. 8 | */ 9 | // MBED 10 | #include "math.h" 11 | #include "rtos.h" 12 | #include "mbed.h" 13 | #include "USBSerial.h" 14 | // C-SDK 15 | #include "hardware/adc.h" 16 | #include "hardware/gpio.h" 17 | #include "hardware/dma.h" 18 | #include "hardware/pwm.h" 19 | #include "hardware/pio.h" 20 | #include "pico/multicore.h" 21 | #include "pico/time.h" 22 | #include "hardware/clocks.h" 23 | 24 | // multicore sync functions 25 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 16 to 31 OK 26 | // sycc.h also has get_cpuid function 27 | #include "hardware/sync.h" 28 | // 29 | // PIO program/setup include 30 | #include "pio_input_capture_2a.h" 31 | // 32 | using namespace rtos; // we will be using rtos::ThisThread 33 | using namespace mbed; 34 | 35 | // =========================================== 36 | // 37 | // core 0 will run the UI for PIO testing 38 | // and blinks an LED 39 | // set up PWM for 50% duty cycle up to 1 MHz 40 | // 8-slot FIFO acts as buffer for values at high speed 41 | 42 | // === CORE 0 setup ========================== 43 | // Declaring the Thread objects (MBED) 44 | // NOTE: threads hang until serial monitor is attached 45 | Thread led25_thread; 46 | Thread print_thread ; 47 | 48 | // locations to store edge time 49 | unsigned int PIO_time[8] ; 50 | 51 | // pio assembler link 52 | PIO pio = pio0; 53 | uint offset = pio_add_program(pio, &capture_program); 54 | // set up pio state machine 55 | void pio_test_start(PIO pio, uint sm, uint offset, uint pin) { 56 | capture_program_init(pio, sm, offset, pin); 57 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 58 | // drain the 4-slot input fifo 59 | // drain the transmit fifo 60 | pio_sm_clear_fifos (pio, sm); 61 | // pio_sm_drain_tx_fifo (pio, sm); 62 | // pio_sm_get (pio, sm); 63 | // pio_sm_get (pio, sm); 64 | //pio_sm_get (pio, sm); 65 | // pio_sm_get (pio, sm); 66 | // and turn on the state machine 67 | pio_sm_set_enabled(pio, sm, true); 68 | } 69 | 70 | // Thread function pin toggle (MBED) 71 | DigitalOut LED_p25(p25); 72 | // Thread functions DO NOT exit 73 | void led25_function() { 74 | while(1){ 75 | LED_p25 = 1; 76 | ThisThread::sleep_for(250); 77 | LED_p25 = 0; 78 | ThisThread::sleep_for(250); 79 | } 80 | } 81 | 82 | // thread to make a serial port and use it 83 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 84 | // Also try out ADC routines 85 | // Thread functions DO NOT exit 86 | USBSerial serial_port ; // (MBED object) 87 | // 88 | // SigitalOut pio_cntl_p4(p4); // MBED 89 | // 90 | // serial thead 91 | void serial_function() { 92 | // number of edge samples to grab 93 | int N=8 ; 94 | int PWM_cycles = 1200 ; 95 | int delta_time ; 96 | 97 | // the usual sart message 98 | serial_port.printf("Starting ... PIO\r\n") ; 99 | 100 | // === PWM channel === 101 | // pwm set up slice 0 pin 0 102 | #define pin6 6 103 | gpio_set_function(pin6, GPIO_FUNC_PWM); 104 | uint slice_num = pwm_gpio_to_slice_num(pin6); 105 | // want 1 mSec sq wave 106 | pwm_set_clkdiv(slice_num, 1.0f); 107 | pwm_set_clkdiv_mode(slice_num, PWM_DIV_FREE_RUNNING) ; 108 | // max count 109 | pwm_set_wrap(slice_num, 125); //62500 1KHz (with divider at 2) 110 | // initial to ~half 111 | pwm_set_chan_level(slice_num, PWM_CHAN_A , 60) ; // 31000 112 | // enable later 113 | pwm_set_enabled(slice_num, 0); 114 | 115 | // === 116 | while(1){ 117 | // test input and print 118 | // In PuTTY setup, Terminal panel: 119 | // set Local echo and Local editing to 'FORCE ON' 120 | serial_port.printf("PWM wrap cycles>") ; 121 | serial_port.scanf("%d", &PWM_cycles) ; 122 | // max count 123 | pwm_set_wrap(slice_num, PWM_cycles); //62500 1KHz 124 | // initial to ~half 125 | pwm_set_chan_level(slice_num, PWM_CHAN_A , (int)PWM_cycles/2) ; // 31000 126 | serial_port.printf("PWM_period=%6.2f uSec\n\r", (float)PWM_cycles/125 ) ; 127 | 128 | // reset the time stamp array 129 | for(int i=0; i=3) to make up 263 total lines 96 | ; v_sync line + 32 blanks + 200 data lines = 241 lines so far 97 | ; 98 | set x 29 99 | end_blank_loop: 100 | ; two cycles of zero 101 | set pins 0 [1] 102 | ; rest of line is high 103 | ; 23 wait + 1 set + 1 jmp + 2 previous 104 | set pins 1 [23] //15 105 | jmp x-- end_blank_loop 106 | .wrap 107 | 108 | % c-sdk { 109 | // this is a raw helper function for use by the user which sets up the GPIO output, 110 | // and configures the SM to output on a particular pin 111 | 112 | void ntsc_sync_program_init(PIO pio, uint sm, uint offset, uint pin) { 113 | 114 | pio_gpio_init(pio, pin); 115 | pio_gpio_init(pio, pin+1); 116 | // 117 | pio_sm_set_out_pins (pio, sm, pin, 2) ; 118 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 2, true); 119 | // 120 | pio_sm_config c = ntsc_sync_program_get_default_config(offset); 121 | // 122 | // Using 'out' and 'set' 123 | sm_config_set_set_pins(&c, pin, 2); 124 | // 125 | // JMP pin is specified separately as GPIO #, GPIO 4 126 | // sm_config_set_jmp_pin (&c, 4) ; 127 | // 128 | // the out FIFO, shift right, no autopull, threshold 31 129 | // sm_config_set_out_shift (&c, true, false, 31) ; 130 | 131 | pio_sm_init(pio, sm, offset, &c); 132 | } 133 | %} 134 | 135 | .program ntsc_data 136 | ; https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4.py 137 | ; 138 | .wrap_target 139 | ; wait for signal to start video line from sync machine 140 | wait 1 irq 0 141 | ; number of data words in a line-1 142 | set x 7 ; 7 143 | ; 144 | ; get 32 bits 145 | next_word: 146 | pull block 147 | more_bits: 148 | ; slow down the output to about 5 MHz 149 | out pins 1 [20] 150 | jmp!OSRE more_bits 151 | jmp x-- next_word 152 | ; zero the data line 153 | set pins 0 154 | .wrap 155 | 156 | % c-sdk { 157 | // this is a raw helper function for use by the user which sets up the GPIO output, 158 | // and configures the SM to output on a particular pin 159 | 160 | void ntsc_data_program_init(PIO pio, uint sm, uint offset, uint pin) { 161 | 162 | //pio_gpio_init(pio, pin); 163 | pio_gpio_init(pio, pin+1); 164 | // 165 | pio_sm_set_out_pins (pio, sm, pin+1, 1) ; 166 | pio_sm_set_set_pins (pio, sm, pin+1, 1) ; 167 | pio_sm_set_consecutive_pindirs(pio, sm, pin+1, 1, true); 168 | // 169 | pio_sm_config c = ntsc_data_program_get_default_config(offset); 170 | // 171 | // Using 'out' and 'set' 172 | sm_config_set_out_pins(&c, pin+1, 1); 173 | sm_config_set_set_pins(&c, pin+1, 1); 174 | // 175 | // JMP pin is specified separately as GPIO #, GPIO 4 176 | // sm_config_set_jmp_pin (&c, 4) ; 177 | // 178 | // the out FIFO, shift right, no autopull, threshold 31 179 | sm_config_set_out_shift (&c, false, false, 32) ; 180 | // and join to make 8 entries 181 | sm_config_set_fifo_join (&c, PIO_FIFO_JOIN_TX) ; 182 | 183 | pio_sm_init(pio, sm, offset, &c); 184 | } 185 | %} -------------------------------------------------------------------------------- /MBED-setup/Third_example.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A basic example to show multithreading in mbed OS. 3 | AND multicore operation, with no MBED on core1! 4 | * CORE 0 5 | * Two yielding threads, one timer ISR, and one input pin ISR 6 | * With two yielding threads and two ISR 7 | -- Timer ISR triggers p20 pulse 8 | -- p19 ISR input is connected to p20 pulse output 9 | latency ~10 uSec, jitter 1 uSec 10 | probably 5 uSec to get out of timer isr + 5 to get into p19 isr 11 | * with USB serial you MUST: 12 | -- check to make sure the board is correct (aviod compile error) 13 | -- open a PuTTY window with the correct COM port number and baud rate 14 | -- DO NOT choose the Pico+COM board entry 15 | (the arduino monitor does not work in this mode) 16 | * CORE 1 17 | -- GPIO ISR latency is <1 uSec and ~5 USec to return to main 18 | */ 19 | //#include 20 | #include "rtos.h" 21 | #include "mbed.h" 22 | #include "USBSerial.h" 23 | #include "hardware/adc.h" 24 | #include "hardware/gpio.h" 25 | #include "pico/multicore.h" 26 | #include "pico/time.h" 27 | // core sync functions 28 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 29 | // can use spin-lock 16 to 31 30 | // sycc.h also has get_cpuid function 31 | #include "hardware/sync.h" 32 | 33 | using namespace rtos; // we will be using rtos::ThisThread 34 | using namespace mbed; 35 | 36 | // some few cycle waits 37 | #define One_cycle asm ("nop"); 38 | #define Five_cycle_wait asm ("nop");asm ("nop");asm ("nop");asm ("nop");asm ("nop"); 39 | #define Ten_cycle_wait Five_cycle_wait;Five_cycle_wait 40 | 41 | // === global -- both cores ================== 42 | volatile int multicore_count ; 43 | volatile int core_num ; 44 | spin_lock_t * count_lock ; 45 | // =========================================== 46 | 47 | // === CORE 0 setup ========================== 48 | // Declaring the Thread objects 49 | // NOTE: threads hang until serial monitor is attached 50 | Thread led21_thread; 51 | Thread print_thread ; 52 | 53 | // Set up timer ISR ticker to toggle pin 20 54 | Ticker led20_ISR ; 55 | // set up an ISR input from p20 56 | InterruptIn int_p19(p19); 57 | 58 | // Declare i/o - 59 | // thread logic output to scope 60 | DigitalOut LED_p21(p21); 61 | // another thread logic output to scope + ISR 62 | DigitalOut LED_p20(p20); 63 | // ISR toggle logic to scope 64 | DigitalOut LED_p18(p18); 65 | 66 | // Make an ISR to be Triggered by timer to toggle p20 67 | void led20_function() { 68 | LED_p20 = 1 ; 69 | // stretch the pulse a little -- wait function has an overhead of 4 uSec! 70 | //wait_us(0) ; 71 | LED_p20 = 0 ; 72 | } 73 | 74 | // Make an interrupt routine for pin19 ISR to toggle pin18 75 | void ISR_function() { 76 | LED_p18 = 1 ; 77 | //wait_us(0) ; 78 | LED_p18 = 0 ; 79 | } 80 | 81 | // Thread function pin toggle 82 | // Thread functions DO NOT exit 83 | void led21_function() { 84 | while(1){ 85 | LED_p21 = 1; 86 | ThisThread::sleep_for(1); 87 | LED_p21 = 0; 88 | //yield(); 89 | ThisThread::sleep_for(1); 90 | } 91 | } 92 | 93 | // thread to make a serial port and use it 94 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 95 | // Also try out ADC routines 96 | // Thread functions DO NOT exit 97 | USBSerial serial_port ; 98 | void serial_function() { 99 | int count=0 ; 100 | int test_in ; 101 | int core1_count_local ; 102 | int save_irq ; 103 | 104 | // from C SDK section 4.1.1. hardware_adc 105 | // example code from manual 106 | adc_init(); 107 | adc_gpio_init(26); 108 | // p26 is ADC input 0 109 | adc_select_input(0); 110 | // 1000 cycles o 48 MHz clock 111 | adc_set_clkdiv (1000); 112 | // free run 113 | adc_run(1); 114 | // 115 | while(1){ 116 | // this thread counter 117 | count++ ; 118 | // get count from core1, with locking 119 | // 'unsafe' means that interrupts were not disabled 120 | save_irq = spin_lock_blocking(count_lock) ; 121 | core1_count_local = multicore_count ; 122 | spin_unlock(count_lock, save_irq); 123 | 124 | // test input and print 125 | // In PuTTY setup, Terminal panel: 126 | // set Local echo and Local editing to 'FORCE ON' 127 | // serial_port.scanf("%d", &test_in) ; 128 | // formatted output 129 | // read adc_hw directly to avoid ac_read overhead 130 | serial_port.printf("%d %d %d\n\r", count, adc_hw->result, core1_count_local) ; //adc_read()); 131 | // print once after we kow that core1 has started 132 | if (count==1) 133 | serial_port.printf("core = %d\n\r", core_num) ; 134 | ThisThread::sleep_for(1000); 135 | } 136 | } 137 | 138 | // ==== CORE 1 setup =========================== 139 | // an ISR -- use pin 0 interrupt connected to pin 1 output 140 | // to toggle pin 2 141 | void core1_gpio_isr(){ 142 | gpio_put(2, 1) ; 143 | Ten_cycle_wait ; // pulses better on white board 144 | gpio_put(2, 0) ; 145 | } 146 | 147 | //Test core1 launch 148 | // USE ONLY C-sdk library, no MBED on core1 149 | // toggles at ~28 MHz using SDK gpio_put 150 | void core1_main(){ 151 | int save_irq ; 152 | // I don't know why the casting is necessary 153 | gpio_set_irq_enabled_with_callback(0, GPIO_IRQ_EDGE_RISE, 1, (gpio_irq_callback_t)core1_gpio_isr); 154 | // note leadingn UNDERSCORE is different that default SDK 155 | _gpio_init(0) ; 156 | _gpio_init(1) ; 157 | _gpio_init(2) ; 158 | // 159 | gpio_set_dir(0, GPIO_IN) ; 160 | gpio_set_dir(1, GPIO_OUT) ; 161 | gpio_set_dir(2, GPIO_OUT) ; 162 | // 163 | gpio_put(1, 0) ; 164 | gpio_put(2, 0) ; 165 | 166 | // from sync.h 167 | // verify we are on core 1 168 | core_num = get_core_num(); 169 | 170 | while (1) { 171 | gpio_put(1, 1) ; 172 | Ten_cycle_wait ; 173 | // Take the interrupt here ... 174 | // 1 uSec to get in 5 to get out 175 | // witout interrupt, loop runs at 4 million/sec 176 | // with interrupt runs at ~160 KHz 177 | gpio_put(1, 0) ; 178 | Ten_cycle_wait ; 179 | // with just the following statments get ~14 million loops/sec 180 | save_irq = spin_lock_blocking(count_lock) ; 181 | multicore_count++ ; 182 | spin_unlock(count_lock, save_irq); 183 | } 184 | } 185 | 186 | // ==== core 0 MAIN: Start everything ============ 187 | // program entry 188 | int main() { 189 | // attach a function to the p19 instrrupt -- MBED 190 | int_p19.fall(&ISR_function) ; 191 | // attach a function to Ticker interrupt -- MBED 192 | // 20 uSec is near to the minimum for this method (thread timing is poor at this speed) 193 | // -- MBED 194 | led20_ISR.attach(&led20_function, std::chrono::microseconds(200)); 195 | // start blink thread -- MBED 196 | led21_thread.start(led21_function); 197 | // start print thread -- MBED 198 | print_thread.start(serial_function); 199 | // start core 1 -- C-SDK -- !! 200 | count_lock = spin_lock_init(31); 201 | multicore_reset_core1(); 202 | multicore_launch_core1(&core1_main); 203 | } 204 | // end //// 205 | /////////// 206 | -------------------------------------------------------------------------------- /PIO/Two_steppers/pio_stepper_two.pio: -------------------------------------------------------------------------------- 1 | ; Bruce Land -- 7/28/2021 2 | ; 3 | ; ====================================================== 4 | .program stepper 5 | ; ====================================================== 6 | ; Use 4 outputs to run a stepper motor pins 6 to 9 7 | ; get a pulse duration from cpu 32-bit 8 | ; get a step sequence from cpu (8-steps, 4 bits each) 9 | ; step through the sequence, each for the duration specified 10 | ; For full-step we get two sequences, for half-step get one 11 | ; The assumption is that this code will step at constant speed 12 | ; until signalled by the CPU. 13 | ; Counter machine signals new data on pin 4, but PIO finishes currect sequence 14 | ; then PIO ACKs on IRQ 0, read on CPU 15 | ; 16 | ; based on: 17 | ; https://github.com/tinkertechtrove/pico-pi-playing/blob/main/pio-steppers/test_motor4.py 18 | 19 | new_data: 20 | ; cpu will clear data-signal 21 | ; when it gets the ACK 22 | ; ACK the request 23 | irq nowait 0 rel ; set ACK irq register (not an actual interrupt!) 24 | ; 25 | ; autopull ON 26 | out isr 32 27 | ; 28 | ; autopull ON 29 | out y 32 30 | ; 31 | ;irq clear 0 ; ACK is cleared in the C pgm 32 | ; 33 | .wrap_target 34 | jmp !osre step ; all steps used? 35 | ; steps in buffer used up: 36 | ; only check for new data at end of full sequence 37 | jmp pin new_data ; counter sm signals when reached number of steps 38 | ; if no new data, reuse old 39 | mov osr, y ; if all used, put more in osr 40 | ; 41 | ; now set 4 pins and count duration 42 | step: 43 | out pins 4 ; get the next 4 bits onto pins 44 | mov x, isr ; retreive pulse length 45 | count_dur: ; and count the duration 46 | jmp x-- count_dur 47 | .wrap 48 | 49 | ; ====================================================== 50 | ; step counter program 51 | ; ====================================================== 52 | .program step_counter 53 | .side_set 1 opt 54 | .wrap_target 55 | signal: 56 | ; get the number of desired steps 57 | ; with autopull ON and set the handshake 58 | out x 32 side 1 59 | 60 | ; if input count is zero (motor stopped) jump to signal 61 | ; but always clear the signal bit 62 | jmp !x signal side 0 63 | 64 | ; count rising edges on gpio 9 65 | counting: 66 | ; pin gpio 9 is driven by other sm 67 | ; as one phase of the motor drive 68 | wait 0 gpio 9 69 | wait 1 gpio 9 70 | jmp x-- counting 71 | ; got to desired count so: 72 | ; signal the stepper state machine to stop on gpio 5 73 | ; (at beginning of program ) 74 | .wrap 75 | 76 | ; ====================================================== 77 | ; step counter1 program 78 | ; ====================================================== 79 | .program step_counter1 80 | .side_set 1 opt 81 | .wrap_target 82 | signal: 83 | ; get the number of desired steps 84 | ; with autopull ON and set the handshake 85 | out x 32 side 1 86 | 87 | ; if input count is zero (motor stopped) jump to signal 88 | ; but always clear the signal bit 89 | jmp !x signal side 0 90 | 91 | ; count rising edges on gpio 9 92 | counting: 93 | ; pin gpio 10 is driven by other sm 94 | ; as one phase of the motor drive 95 | wait 0 gpio 13 96 | wait 1 gpio 13 97 | jmp x-- counting 98 | ; got to desired count so: 99 | ; signal the stepper state machine to stop on gpio 5 100 | ; (at beginning of program ) 101 | .wrap 102 | 103 | 104 | % c-sdk { 105 | // ====================================================== 106 | // === stepper driver 0 config === 107 | // ====================================================== 108 | void stepper0_program_init(PIO pio, uint sm, uint offset, uint pin) { 109 | 110 | pio_gpio_init(pio, pin); 111 | pio_gpio_init(pio, pin+1); 112 | pio_gpio_init(pio, pin+2); 113 | pio_gpio_init(pio, pin+3); 114 | // 115 | // the step outputs 116 | pio_sm_set_out_pins (pio, sm, pin, 4) ; 117 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 4, true); 118 | // 119 | pio_sm_config c = stepper_program_get_default_config(offset); 120 | // 121 | // Using 'out' 122 | sm_config_set_out_pins(&c, pin, 4); 123 | // 124 | //JMP pin is specified separately as GPIO #, GPIO 5 125 | // driven by signal from other sm 126 | sm_config_set_jmp_pin (&c, 5) ; 127 | // 128 | // the out FIFO, shift right, autopull, threshold 31 129 | sm_config_set_out_shift (&c, true, true, 31) ; 130 | // 131 | pio_sm_init(pio, sm, offset, &c); 132 | } 133 | 134 | // ====================================================== 135 | // === stepper driver 1 config === 136 | // ====================================================== 137 | void stepper1_program_init(PIO pio, uint sm, uint offset, uint pin) { 138 | pio_gpio_init(pio, pin); 139 | pio_gpio_init(pio, pin+1); 140 | pio_gpio_init(pio, pin+2); 141 | pio_gpio_init(pio, pin+3); 142 | // 143 | // the step outputs 144 | pio_sm_set_out_pins (pio, sm, pin, 4) ; 145 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 4, true); 146 | // 147 | pio_sm_config c = stepper_program_get_default_config(offset); 148 | // 149 | // Using 'out' 150 | sm_config_set_out_pins(&c, pin, 4); 151 | // 152 | //JMP pin is specified separately as GPIO #, GPIO 14 153 | // driven by signal from other sm 154 | sm_config_set_jmp_pin (&c, 14) ; 155 | // 156 | // the out FIFO, shift right, autopull, threshold 31 157 | sm_config_set_out_shift (&c, true, true, 31) ; 158 | // 159 | pio_sm_init(pio, sm, offset, &c); 160 | } 161 | 162 | // ====================================================== 163 | // === stepper counter 0 config === 164 | // ====================================================== 165 | void step_counter0_program_init(PIO pio, uint sm, uint offset, uint pin) { 166 | 167 | pio_gpio_init(pio, 5); // signal pin 168 | pio_sm_set_consecutive_pindirs(pio, sm, 5, 1, true); // signal done output 169 | // 170 | pio_sm_config c = step_counter_program_get_default_config(offset); 171 | // 172 | // one pin, optional side, not pindir 173 | sm_config_set_sideset (&c, 2, true, false); 174 | sm_config_set_sideset_pins(&c, 5); // signal stepper machine 175 | //bool shift_right, bool autopull, uint pull_threshold) 176 | sm_config_set_out_shift (&c, true, true, 31) ; 177 | // 178 | pio_sm_init(pio, sm, offset, &c); 179 | } 180 | 181 | // ====================================================== 182 | // === stepper counter 1 config === 183 | // ====================================================== 184 | void step_counter1_program_init(PIO pio, uint sm, uint offset, uint pin) { 185 | pio_gpio_init(pio, 14); // signal pin 186 | pio_sm_set_consecutive_pindirs(pio, sm, 14, 1, true); // signal done output 187 | // 188 | pio_sm_config c = step_counter1_program_get_default_config(offset); 189 | // 190 | // one pin, optional side, not pindir 191 | sm_config_set_sideset (&c, 2, true, false); 192 | sm_config_set_sideset_pins(&c, 14); // signal stepper machine 193 | //bool shift_right, bool autopull, uint pull_threshold) 194 | sm_config_set_out_shift (&c, true, true, 31) ; 195 | // 196 | pio_sm_init(pio, sm, offset, &c); 197 | } 198 | // ====================================================== 199 | // 200 | %} -------------------------------------------------------------------------------- /PIO/Two_steppers/pio_stepper_two.h: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------- // 2 | // This file is autogenerated by pioasm; do not edit! // 3 | // -------------------------------------------------- // 4 | 5 | #pragma once 6 | 7 | #if !PICO_NO_HARDWARE 8 | #include "hardware/pio.h" 9 | #endif 10 | 11 | // ------- // 12 | // stepper // 13 | // ------- // 14 | 15 | #define stepper_wrap_target 3 16 | #define stepper_wrap 8 17 | 18 | static const uint16_t stepper_program_instructions[] = { 19 | 0xc010, // 0: irq nowait 0 rel 20 | 0x60c0, // 1: out isr, 32 21 | 0x6040, // 2: out y, 32 22 | // .wrap_target 23 | 0x00e6, // 3: jmp !osre, 6 24 | 0x00c0, // 4: jmp pin, 0 25 | 0xa0e2, // 5: mov osr, y 26 | 0x6004, // 6: out pins, 4 27 | 0xa026, // 7: mov x, isr 28 | 0x0048, // 8: jmp x--, 8 29 | // .wrap 30 | }; 31 | 32 | #if !PICO_NO_HARDWARE 33 | static const struct pio_program stepper_program = { 34 | .instructions = stepper_program_instructions, 35 | .length = 9, 36 | .origin = -1, 37 | }; 38 | 39 | static inline pio_sm_config stepper_program_get_default_config(uint offset) { 40 | pio_sm_config c = pio_get_default_sm_config(); 41 | sm_config_set_wrap(&c, offset + stepper_wrap_target, offset + stepper_wrap); 42 | return c; 43 | } 44 | #endif 45 | 46 | // ------------ // 47 | // step_counter // 48 | // ------------ // 49 | 50 | #define step_counter_wrap_target 0 51 | #define step_counter_wrap 4 52 | 53 | static const uint16_t step_counter_program_instructions[] = { 54 | // .wrap_target 55 | 0x7820, // 0: out x, 32 side 1 56 | 0x1020, // 1: jmp !x, 0 side 0 57 | 0x2009, // 2: wait 0 gpio, 9 58 | 0x2089, // 3: wait 1 gpio, 9 59 | 0x0042, // 4: jmp x--, 2 60 | // .wrap 61 | }; 62 | 63 | #if !PICO_NO_HARDWARE 64 | static const struct pio_program step_counter_program = { 65 | .instructions = step_counter_program_instructions, 66 | .length = 5, 67 | .origin = -1, 68 | }; 69 | 70 | static inline pio_sm_config step_counter_program_get_default_config(uint offset) { 71 | pio_sm_config c = pio_get_default_sm_config(); 72 | sm_config_set_wrap(&c, offset + step_counter_wrap_target, offset + step_counter_wrap); 73 | sm_config_set_sideset(&c, 2, true, false); 74 | return c; 75 | } 76 | #endif 77 | 78 | // ------------- // 79 | // step_counter1 // 80 | // ------------- // 81 | 82 | #define step_counter1_wrap_target 0 83 | #define step_counter1_wrap 4 84 | 85 | static const uint16_t step_counter1_program_instructions[] = { 86 | // .wrap_target 87 | 0x7820, // 0: out x, 32 side 1 88 | 0x1020, // 1: jmp !x, 0 side 0 89 | 0x200d, // 2: wait 0 gpio, 13 90 | 0x208d, // 3: wait 1 gpio, 13 91 | 0x0042, // 4: jmp x--, 2 92 | // .wrap 93 | }; 94 | 95 | #if !PICO_NO_HARDWARE 96 | static const struct pio_program step_counter1_program = { 97 | .instructions = step_counter1_program_instructions, 98 | .length = 5, 99 | .origin = -1, 100 | }; 101 | 102 | static inline pio_sm_config step_counter1_program_get_default_config(uint offset) { 103 | pio_sm_config c = pio_get_default_sm_config(); 104 | sm_config_set_wrap(&c, offset + step_counter1_wrap_target, offset + step_counter1_wrap); 105 | sm_config_set_sideset(&c, 2, true, false); 106 | return c; 107 | } 108 | 109 | // ====================================================== 110 | // === stepper driver 0 config === 111 | // ====================================================== 112 | void stepper0_program_init(PIO pio, uint sm, uint offset, uint pin) { 113 | pio_gpio_init(pio, pin); 114 | pio_gpio_init(pio, pin+1); 115 | pio_gpio_init(pio, pin+2); 116 | pio_gpio_init(pio, pin+3); 117 | // 118 | // the step outputs 119 | pio_sm_set_out_pins (pio, sm, pin, 4) ; 120 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 4, true); 121 | // 122 | pio_sm_config c = stepper_program_get_default_config(offset); 123 | // 124 | // Using 'out' 125 | sm_config_set_out_pins(&c, pin, 4); 126 | // 127 | //JMP pin is specified separately as GPIO #, GPIO 5 128 | // driven by signal from other sm 129 | sm_config_set_jmp_pin (&c, 5) ; 130 | // 131 | // the out FIFO, shift right, autopull, threshold 31 132 | sm_config_set_out_shift (&c, true, true, 31) ; 133 | // 134 | pio_sm_init(pio, sm, offset, &c); 135 | } 136 | // ====================================================== 137 | // === stepper driver 1 config === 138 | // ====================================================== 139 | void stepper1_program_init(PIO pio, uint sm, uint offset, uint pin) { 140 | pio_gpio_init(pio, pin); 141 | pio_gpio_init(pio, pin+1); 142 | pio_gpio_init(pio, pin+2); 143 | pio_gpio_init(pio, pin+3); 144 | // 145 | // the step outputs 146 | pio_sm_set_out_pins (pio, sm, pin, 4) ; 147 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 4, true); 148 | // 149 | pio_sm_config c = stepper_program_get_default_config(offset); 150 | // 151 | // Using 'out' 152 | sm_config_set_out_pins(&c, pin, 4); 153 | // 154 | //JMP pin is specified separately as GPIO #, GPIO 14 155 | // driven by signal from other sm 156 | sm_config_set_jmp_pin (&c, 14) ; 157 | // 158 | // the out FIFO, shift right, autopull, threshold 31 159 | sm_config_set_out_shift (&c, true, true, 31) ; 160 | // 161 | pio_sm_init(pio, sm, offset, &c); 162 | } 163 | // ====================================================== 164 | // === stepper counter 0 config === 165 | // ====================================================== 166 | void step_counter0_program_init(PIO pio, uint sm, uint offset, uint pin) { 167 | pio_gpio_init(pio, 5); // signal pin 168 | pio_sm_set_consecutive_pindirs(pio, sm, 5, 1, true); // signal done output 169 | // 170 | pio_sm_config c = step_counter_program_get_default_config(offset); 171 | // 172 | // one pin, optional side, not pindir 173 | sm_config_set_sideset (&c, 2, true, false); 174 | sm_config_set_sideset_pins(&c, 5); // signal stepper machine 175 | //bool shift_right, bool autopull, uint pull_threshold) 176 | sm_config_set_out_shift (&c, true, true, 31) ; 177 | // 178 | pio_sm_init(pio, sm, offset, &c); 179 | } 180 | // ====================================================== 181 | // === stepper counter 1 config === 182 | // ====================================================== 183 | void step_counter1_program_init(PIO pio, uint sm, uint offset, uint pin) { 184 | pio_gpio_init(pio, 14); // signal pin 185 | pio_sm_set_consecutive_pindirs(pio, sm, 14, 1, true); // signal done output 186 | // 187 | pio_sm_config c = step_counter1_program_get_default_config(offset); 188 | // 189 | // one pin, optional side, not pindir 190 | sm_config_set_sideset (&c, 2, true, false); 191 | sm_config_set_sideset_pins(&c, 14); // signal stepper machine 192 | //bool shift_right, bool autopull, uint pull_threshold) 193 | sm_config_set_out_shift (&c, true, true, 31) ; 194 | // 195 | pio_sm_init(pio, sm, offset, &c); 196 | } 197 | // ====================================================== 198 | // 199 | 200 | #endif 201 | -------------------------------------------------------------------------------- /PIO/Two_steppers/PIO_stepper_two_motor.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * --- Stepper motor sequencer --- 3 | * Two PIO state machine ae started 4 | * -- The first takes two input words from a thread to set step rate and pattern 5 | * -- The second is a counter which watches an input pin and increments on rising edge 6 | * This has the effect of counting full 4 or 8-step cycles. 7 | * 8 | */ 9 | // MBED 10 | #include "math.h" 11 | #include "rtos.h" 12 | #include "mbed.h" 13 | #include "USBSerial.h" 14 | // C-SDK 15 | //#include "pico/stdlib.h" 16 | #include "hardware/adc.h" 17 | #include "hardware/gpio.h" 18 | #include "hardware/dma.h" 19 | #include "hardware/pwm.h" 20 | #include "hardware/pio.h" 21 | #include "pico/multicore.h" 22 | #include "pico/time.h" 23 | #include "hardware/clocks.h" 24 | 25 | // multicore sync functions 26 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 16 to 31 OK 27 | // sycc.h also has get_cpuid function 28 | #include "hardware/sync.h" 29 | // 30 | // PIO program/setup include 31 | #include "pio_stepper_two.h" 32 | // 33 | using namespace rtos; // we will be using rtos::ThisThread 34 | using namespace mbed; 35 | 36 | // convenient constants 37 | #define spin_wait {} 38 | #define sys_freq 125000000 39 | // =========================================== 40 | // 41 | // core 0 will run the UI for PIO testing 42 | // and blinks an LED 43 | 44 | // === CORE 0 setup ========================== 45 | // Declaring the Thread objects (MBED) 46 | // NOTE: threads hang until serial monitor is attached 47 | Thread led25_thread; 48 | Thread print_thread ; 49 | 50 | // pio assembler links from included file 51 | PIO pio = pio0; 52 | // machine 0 -- stepper initialization 53 | uint offset0 = pio_add_program(pio, &stepper_program); 54 | // set up pio state machine 55 | void pio_stepper0_start(PIO pio, uint sm, uint offset, uint pin) { 56 | stepper0_program_init(pio, sm, offset, pin); 57 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 58 | // drain the FIFOs 59 | pio_sm_clear_fifos (pio, sm); 60 | pio_sm_set_enabled(pio, sm, true); 61 | } 62 | // machine 1 -- counter initialization 63 | uint offset1 = pio_add_program(pio, &step_counter_program); 64 | // set up pio state machine 65 | void pio_counter0_start(PIO pio, uint sm, uint offset, uint pin) { 66 | step_counter0_program_init(pio, sm, offset, pin); 67 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 68 | // drain the FIFOs 69 | pio_sm_clear_fifos (pio, sm); 70 | pio_sm_set_enabled(pio, sm, true); 71 | } 72 | 73 | // machine 2 -- stepper initialization 74 | uint offset2 = pio_add_program(pio, &stepper_program); 75 | // set up pio state machine 76 | void pio_stepper1_start(PIO pio, uint sm, uint offset, uint pin) { 77 | stepper1_program_init(pio, sm, offset, pin); 78 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 79 | // drain the FIFOs 80 | pio_sm_clear_fifos (pio, sm); 81 | pio_sm_set_enabled(pio, sm, true); 82 | } 83 | // machine 3 -- counter initialization 84 | uint offset3 = pio_add_program(pio, &step_counter1_program); 85 | // set up pio state machine 86 | void pio_counter1_start(PIO pio, uint sm, uint offset, uint pin) { 87 | step_counter1_program_init(pio, sm, offset, pin); 88 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 89 | // drain the FIFOs 90 | pio_sm_clear_fifos (pio, sm); 91 | pio_sm_set_enabled(pio, sm, true); 92 | } 93 | 94 | // Thread function pin toggle (MBED) 95 | DigitalOut LED_p25(p25); 96 | // Thread functions DO NOT exit 97 | void led25_function() { 98 | while(1){ 99 | LED_p25 = 1; 100 | ThisThread::sleep_for(250); 101 | LED_p25 = 0; 102 | ThisThread::sleep_for(250); 103 | } 104 | } 105 | 106 | // thread to make a serial port and use it 107 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 108 | // 109 | USBSerial serial_port ; // (MBED object) 110 | // 111 | // signal to tell machine 0 that the cpu has new data for it 112 | //DigitalOut pio_new_data_p4(p4) ; // MBED 113 | // 114 | // serial thead 115 | void serial_function() { 116 | // max counter because state machine call only decriment a count 117 | int step_count = 1, step_count1 = 1 ; 118 | // steps/sec 119 | int step_rate = 500, step_rate1 = 500 ; 120 | // step patterns 121 | // the stop pattern just toggle one line to tick out 122 | // the number of requested steps 123 | #define step_stop 0x80808080 // 0x00000000 124 | // bits 1-2-3-4-1-2-3-4 125 | #define step_full_forward 0x12481248 //0001 0010 0100 1000 0001 0010 0100 1000_// 126 | #define step_full_reverse 0x84218421 127 | // bits 1-12-2-23-3-34-4-41 128 | #define step_half_forward 0x13264c89 //0001 0011 0010 0110 0100 1100 1000 1001 0x13264c89 129 | #define step_half_reverse 0x98c46231 130 | unsigned int steps[5] = 131 | {step_stop, step_full_forward, step_full_reverse, step_half_forward, step_half_reverse} ; 132 | // set the initial pattern -- for production, this would be zero 133 | int pattern = 0, pattern1 = 0; 134 | 135 | // the usual sart message 136 | serial_port.printf("Starting...\n\r") ; 137 | 138 | // === start the PIO === 139 | // stepper0 outputs will be on gpio 6 7 8 9 140 | // counter 0 signals on pin 5 (not used in C, just in PIO assembler) 141 | #define gpio6 6 142 | // stepper1 outputs will be on gpio 10 11 12 13 143 | // counter 1 signals on pin 14 (not used in C, just in PIO assembler) 144 | #define gpio10 10 145 | // 146 | #define machine0 0 147 | #define machine1 1 148 | #define machine2 2 149 | #define machine3 3 150 | 151 | // start sm's running 152 | pio_counter0_start(pio, machine1, offset1, 0); 153 | pio_stepper0_start(pio, machine0, offset0, gpio6); 154 | pio_counter1_start(pio, machine3, offset3, 0); 155 | pio_stepper1_start(pio, machine2, offset2, gpio10); 156 | // 157 | 158 | // state machine 1 data -- count init to desired step count 159 | pio_sm_put_blocking (pio, machine1, step_count); 160 | // give the state machine 0 initial data -- rate, pattern 161 | pio_sm_put_blocking (pio, machine0, sys_freq/step_rate); 162 | // put this handshak clear between data-send because PIO so fast 163 | pio_sm_put_blocking (pio, machine0, steps[pattern]); 164 | //serial_port.printf("%d\n\r", pio0_hw->irq); 165 | // clear the IRQ bit set in SM by writing ONE 166 | pio0_hw->irq = 0b01 ; //clear bit 0 167 | //serial_port.printf("%d\n\r", pio0_hw->irq); 168 | 169 | // state machine 3 data -- count init to desired step count 170 | pio_sm_put_blocking (pio, machine3, step_count); 171 | // give the state machine 2 initial data -- rate, pattern 172 | pio_sm_put_blocking (pio, machine2, sys_freq/step_rate); 173 | // put this handshak clear between data-send because PIO so fast 174 | pio_sm_put_blocking (pio, machine2, steps[pattern]); 175 | //serial_port.printf("%d\n\r", pio0_hw->irq); 176 | // clear the IRQ bit set in SM by writing ONE 177 | // machine 2 signals on bit 2 178 | pio0_hw->irq = 0b100 ; //clear bit 2 179 | 180 | // === 181 | while(1){ 182 | // In PuTTY setup, Terminal panel: 183 | // set Local echo and Local editing to 'FORCE ON' 184 | serial_port.printf("step/sec, pattern, step count, step/sec1, pattern1, step count1\n\r") ; 185 | serial_port.scanf("%d %d %d %d %d %d", &step_rate, &pattern, &step_count, 186 | &step_rate1, &pattern1, &step_count1 ) ; 187 | if ((pattern < 0) || (pattern > 4)) pattern = 0; 188 | if ((pattern1 < 0) || (pattern1 > 4)) pattern1 = 0; 189 | // NOTE step count = 0 drops through counter regardless 190 | // of the step pattern. Used IFF pattern=0 191 | //if (pattern == 0) step_count = 0; 192 | //if (pattern1 == 0) step_count1 = 0; 193 | 194 | // now wait for ACK on IRQ 0 from stepper machine 195 | // need this because mahine may tak several mSec to get here 196 | // depending on step rate 197 | // serial_port.printf("%d\n\r", pio0_hw->irq); // testing 198 | // machine 0 signals on bit 0 199 | while ((pio0_hw->irq & 0b01) == 0) spin_wait ; 200 | // 201 | // send data to counter machine 202 | pio_sm_put_blocking (pio, machine1, step_count); 203 | // send data to stepper machine 204 | pio_sm_put_blocking (pio, machine0, sys_freq/step_rate); 205 | pio_sm_put_blocking (pio, machine0, steps[pattern]); 206 | // clear the IRQ bit by writing ONE 207 | pio0_hw->irq = 0b01 ; //clear bit 0 208 | 209 | // machine 2 signals on bit 2 210 | //serial_port.printf("%d\n\r", pio0_hw->irq); // testing 211 | while ((pio0_hw->irq & 0b100) == 0) spin_wait ; 212 | // 213 | // send data to counter machine 214 | pio_sm_put_blocking (pio, machine3, step_count1); 215 | // send data to stepper machine 216 | pio_sm_put_blocking (pio, machine2, sys_freq/step_rate1); 217 | pio_sm_put_blocking (pio, machine2, steps[pattern1]); 218 | // clear the IRQ bit by writing ONE 219 | pio0_hw->irq = 0b100 ; //clear bit 0 220 | // 221 | } 222 | } 223 | 224 | // ==== core 0 MAIN: Start everything ============ 225 | // program entry 226 | int main() { 227 | // start blink thread -- MBED 228 | led25_thread.start(led25_function); 229 | // start print thread -- MBED 230 | print_thread.start(serial_function); 231 | } 232 | // end //// 233 | /////////// -------------------------------------------------------------------------------- /DMA/DDS_ADC_PWM.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * A basic example to show multithreading in mbed OS. 3 | AND multicore operation, with no MBED on core 1! 4 | -- check to make sure the board is correct (aviod compile error) 5 | * CORE 0 6 | * Two threads: 7 | * -- The usual heartbeat 8 | -- USB serial with frequency set input and ADC running 9 | sends frequency to core1 using FIO 10 | starts ADC-DMA-PWM for input from core1 PWM output 11 | * with USB serial you MUST: 12 | -- open a PuTTY window with the correct COM port number and baud rate 13 | -- DO NOT choose the Pico+COM board entry 14 | (the arduino monitor does not work in this mode) 15 | * CORE 1 16 | -- sets up sine table, DMA and PWM 17 | -- Waits in MAIN for FIFO input from core0 18 | */ 19 | // MBED 20 | #include "math.h" 21 | #include "rtos.h" 22 | #include "mbed.h" 23 | #include "USBSerial.h" 24 | // C-SDK 25 | #include "hardware/adc.h" 26 | #include "hardware/gpio.h" 27 | #include "hardware/dma.h" 28 | #include "hardware/pwm.h" 29 | #include "pico/multicore.h" 30 | #include "pico/time.h" 31 | // multicore sync functions 32 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 16 to 31 OK 33 | // sycc.h also has get_cpuid function 34 | #include "hardware/sync.h" 35 | 36 | using namespace rtos; // we will be using rtos::ThisThread 37 | using namespace mbed; 38 | 39 | // =========================================== 40 | // 41 | // core 0 will run the UI for changing frequency and sends value to FIFO 42 | // and blinks an LED 43 | // and set up the ADC to full speed and start ADC-DMA-PWM output 44 | // 45 | // core 1 wil set up the DDS and start DMA-PWM sine synth 46 | // and wait for FIFO frequency data, then compute DMA frequency 47 | // 48 | // === CORE 0 setup ========================== 49 | // Declaring the Thread objects (MBED) 50 | // NOTE: threads hang until serial monitor is attached 51 | Thread led25_thread; 52 | Thread print_thread ; 53 | 54 | // Thread function pin toggle (MBED) 55 | DigitalOut LED_p25(p25); 56 | // Thread functions DO NOT exit 57 | void led25_function() { 58 | while(1){ 59 | LED_p25 = 1; 60 | ThisThread::sleep_for(250); 61 | LED_p25 = 0; 62 | //yield(); 63 | ThisThread::sleep_for(250); 64 | } 65 | } 66 | 67 | // thread to make a serial port and use it 68 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 69 | // Also try out ADC routines 70 | // Thread functions DO NOT exit 71 | USBSerial serial_port ; // (MBED object) 72 | // serial thead 73 | void serial_function() { 74 | int frequency = 1000 ; 75 | int ADC_sample_cycles = 96 ; 76 | int ADC_sample_freq = 500000 ; 77 | // === from C SDK section 4.1.1. hardware_adc 78 | // setup ADC 79 | adc_init(); 80 | adc_gpio_init(26); 81 | // p26 is ADC input 0 82 | adc_select_input(0); 83 | // 1000 cycles o 48 MHz clock 84 | adc_set_clkdiv (ADC_sample_cycles); 85 | // free run 86 | adc_run(1); 87 | // result is in adc_hw->result 88 | // but we are going to forward that to the FIFO 89 | //adc_fifo_setup(bool en, bool dreq_en, uint16_t dreq_thresh, bool err_in_fifo, bool byte_shift) 90 | // fifo_enable, dreq_enable, thresh=1, no error, shift to 8-bits(for 8-bit PWM) 91 | adc_fifo_setup(1,1,1,0,1); 92 | // 93 | //initial frequency -> to core1 94 | multicore_fifo_push_blocking(frequency); 95 | serial_port.printf("freq = %d \n\r", frequency) ; 96 | // 97 | // === pwm set up slice 7 pin 14 98 | #define pin14 14 99 | gpio_set_function(pin14, GPIO_FUNC_PWM); 100 | uint slice_num = pwm_gpio_to_slice_num(pin14); 101 | // full speed clock => 256 counts in 2 uSec 102 | pwm_set_clkdiv(slice_num, 1.0f); 103 | pwm_set_clkdiv_mode(slice_num, PWM_DIV_FREE_RUNNING) ; 104 | // max count 256 105 | pwm_set_wrap(slice_num, 256); 106 | // initial for testing to half 107 | pwm_set_chan_level(slice_num, PWM_CHAN_A , 128) ; 108 | pwm_set_enabled(slice_num, 1); 109 | // 110 | // === TWO DMA channels to send data to PWM 111 | // ctrl chanel resets data channel read addr 112 | int ctrl_chan = dma_claim_unused_channel(true); 113 | // data channel move sine taable to PWM 114 | int data_chan = dma_claim_unused_channel(true); 115 | 116 | // the control channel to restaart data channel 117 | // -- just does a NOP write, then chains to data channel 118 | dma_channel_config c = dma_channel_get_default_config(ctrl_chan); 119 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 120 | channel_config_set_read_increment(&c, false); 121 | channel_config_set_write_increment(&c, false); 122 | channel_config_set_irq_quiet(&c, true); 123 | channel_config_set_enable(&c, true); 124 | channel_config_set_chain_to(&c, data_chan) ; 125 | // 126 | dma_channel_configure (ctrl_chan, &c, 127 | &dma_hw->ch[data_chan].read_addr, // NOP write in other channel 128 | &dma_hw->ch[data_chan].read_addr, // NOP read_addr 129 | 1, // transfer count 130 | 0) ; // trigger 131 | 132 | // The acdtual data channel 133 | dma_channel_config c2 = dma_channel_get_default_config(data_chan); 134 | channel_config_set_transfer_data_size(&c2, DMA_SIZE_16); 135 | channel_config_set_read_increment(&c2, false); 136 | channel_config_set_write_increment(&c2, false); 137 | channel_config_set_irq_quiet(&c2, true); 138 | channel_config_set_enable(&c2, true); 139 | channel_config_set_chain_to(&c2, ctrl_chan) ; 140 | channel_config_set_dreq(&c2, DREQ_ADC); 141 | // 142 | dma_channel_configure(data_chan, &c2, 143 | &pwm_hw->slice[slice_num].cc, // write_addr, duty cycle of pwm 144 | &adc_hw->fifo, // read_addr, the table 145 | 1, // one ADC value, 146 | 1) ; // trigger 147 | // === 148 | while(1){ 149 | // test input and print 150 | // In PuTTY setup, Terminal panel: 151 | // set Local echo and Local editing to 'FORCE ON' 152 | serial_port.scanf("%d %d", &frequency, &ADC_sample_freq) ; 153 | // formatted output 154 | // read adc_hw directly to avoid ac_read overhead 155 | serial_port.printf("Freq=%d, ADC_freq=%d\n\r", frequency, ADC_sample_freq) ; //adc_read()); 156 | // 157 | //frequency => core1 158 | multicore_fifo_push_blocking(frequency); 159 | 160 | // set ADC divider -- max 16 bits on divider -- ADC clock is 48 MHz. 161 | // Therfore the minimum sample freq is around 740 Hz. 162 | ADC_sample_cycles = (int)(48e6 / (float)ADC_sample_freq) ; 163 | adc_set_clkdiv (ADC_sample_cycles) ; 164 | 165 | // not really necessary 166 | //ThisThread::sleep_for(50); 167 | } 168 | } 169 | 170 | // ==== CORE 1 setup =========================== 171 | // core1 launch 172 | // USE ONLY C-sdk library, no MBED on core1 173 | void core1_main(){ 174 | // freq from core0 via FIFO 175 | int frequency ; 176 | int ADC_sample_cycles ; 177 | 178 | // frequency computation see near the bottom of 179 | // https://people.ece.cornell.edu/land/courses/ece4760/RP2040/index_rp2040_Micropython.html 180 | float Fout, X0, dY, Fx ; 181 | // cpu clock ffreq 182 | float Fclk = 125e6 ; 183 | // length sine table 184 | float Ltable = 256 ; 185 | float two16 = 65536 ; 186 | float two32 = 4294967296 ; 187 | 188 | // === Define sine table 189 | // DMA ch0 source for PWM signed short 190 | short DMA_sine[256] ; 191 | // now define a pointer to the table-pointer 192 | int ptr_table ; 193 | ptr_table = (int)DMA_sine; 194 | // build the sine table for DMA-to-pwm 195 | int i = 0 ; 196 | while (i<256) { 197 | DMA_sine[i] = (short) (127 * sin(2*3.1416*i/256) + 128) ; 198 | i++ ; 199 | } 200 | 201 | // === pwm set up slice 0 pin 0 202 | #define pin0 0 203 | gpio_set_function(pin0, GPIO_FUNC_PWM); 204 | uint slice_num = pwm_gpio_to_slice_num(pin0); 205 | // full speed clock => 256 counts in 2 uSec 206 | pwm_set_clkdiv(slice_num, 1.0f); 207 | pwm_set_clkdiv_mode(slice_num, PWM_DIV_FREE_RUNNING) ; 208 | // max count 256 209 | pwm_set_wrap(slice_num, 256); 210 | // initial for testing to half 211 | pwm_set_chan_level(slice_num, PWM_CHAN_A , 128) ; 212 | pwm_set_enabled(slice_num, 1); 213 | 214 | // === TWO DMA channels to send table to PWM 215 | // ctrl chanel resets data channel read addr 216 | int ctrl_chan = dma_claim_unused_channel(true); 217 | // data channel move sine taable to PWM 218 | int data_chan = dma_claim_unused_channel(true); 219 | 220 | // the control channel to reset read addrs of data channel 221 | dma_channel_config c = dma_channel_get_default_config(ctrl_chan); 222 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 223 | channel_config_set_read_increment(&c, false); 224 | channel_config_set_write_increment(&c, false); 225 | channel_config_set_irq_quiet(&c, true); 226 | channel_config_set_enable(&c, true); 227 | channel_config_set_chain_to(&c, data_chan) ; 228 | // 229 | dma_channel_configure (ctrl_chan, &c, 230 | &dma_hw->ch[data_chan].read_addr, // write addr in other channel 231 | &ptr_table, // read_addr, pointer-to-ponter to table 232 | 1, // transfer count 233 | 0) ; // trigger 234 | 235 | // The acdtual data channel 236 | dma_channel_config c2 = dma_channel_get_default_config(data_chan); 237 | channel_config_set_transfer_data_size(&c2, DMA_SIZE_16); 238 | channel_config_set_read_increment(&c2, true); 239 | channel_config_set_write_increment(&c2, false); 240 | channel_config_set_irq_quiet(&c2, true); 241 | channel_config_set_enable(&c2, true); 242 | channel_config_set_chain_to(&c2, ctrl_chan) ; 243 | // timers sets the DDS frequency 244 | #define DREQ_TIMER0 0x3b 245 | channel_config_set_dreq(&c2, DREQ_TIMER0); 246 | // 247 | dma_channel_configure(data_chan, &c2, 248 | &pwm_hw->slice[slice_num].cc, // write_addr, duty cycle of pwm 249 | DMA_sine, // read_addr, the table 250 | 256, // len(DMA_sine), 251 | 1) ; // trigger 252 | 253 | //Set a non-zero default freq for testing 254 | //dma_hw->timer[0] = (0xffff | (0x8fff<<16) ) ; 255 | // === 256 | while (1) { 257 | // grab the frequency from core0 258 | frequency = multicore_fifo_pop_blocking(); 259 | // modify DMA timer for frequency set 260 | Fout = (float) frequency ; 261 | X0 = (Fout/Fclk) * Ltable * two16 ; 262 | Fx = Fclk*((int)X0)/(Ltable * two16) ; 263 | dY = (Fout-Fx) * Ltable * two32 /(Fclk * ((int)X0)) ; 264 | // hit the DMA pacing timer directly 265 | dma_hw->timer[0] = (0xffff-int(dY)) | (int(X0)<<16) ; 266 | } 267 | } 268 | 269 | // ==== core 0 MAIN: Start everything ============ 270 | // program entry 271 | int main() { 272 | // start blink thread -- MBED 273 | led25_thread.start(led25_function); 274 | // start print thread -- MBED 275 | print_thread.start(serial_function); 276 | // start core 1 -- C-SDK -- !! 277 | multicore_reset_core1(); 278 | multicore_launch_core1(&core1_main); 279 | } 280 | // end //// 281 | /////////// 282 | -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/PIO_NTSC_DMA_graphics.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * NTSC sync and data using DMA transfer 3 | * Useful raster is 256 wide by 204 pixels high 4 | * 5 | * using fake progreassive scan 6 | * See https://sagargv.blogspot.com/2014/07/ntsc-demystified-color-demo-with.html 7 | * 8 | * 263 scan lines. 9 | * one line is exactly 63.555 microseconds long 10 | * H-sync pulse is zero volts for 4.7 uSec, but not too critical 11 | * V-sync pulse is zero volts for 29.4 uSec, but not too critical 12 | * if line 1 is V-synch then content can start on line 21 or so and continue 13 | * until line 260 14 | * LInes 1-20 and 261-263 should hold video data at zero 15 | * about 55 uSec of each active scan line can contain pixels 16 | * a 5 Mpixel/sec rate gives about 55*5 = 275 pixels/line, so use 256 pixels 17 | * 18 | * PIO implementation: 19 | * One line time should require waits of <32 cycles so that loops are not required. 20 | * Choose sync machine clock rate so that 27 cycles give 63.555 uSec, which is 21 | * 15,734 Hz. So machine clock should be 27*15734=424828 Hz 22 | * Ths colck choice means that an H-sync pulse is exactly two cycles and 23 | * V-sync is approx 13 cycles. 24 | * The clock divider is therfore 125e6/424828 = 294.2367 25 | */ 26 | // MBED 27 | #include "math.h" 28 | #include "rtos.h" 29 | #include "mbed.h" 30 | #include "USBSerial.h" 31 | // C-SDK 32 | #include "hardware/adc.h" 33 | #include "hardware/gpio.h" 34 | #include "hardware/dma.h" 35 | #include "hardware/pwm.h" 36 | #include "hardware/pio.h" 37 | #include "pico/multicore.h" 38 | #include "pico/time.h" 39 | #include "hardware/clocks.h" 40 | 41 | // multicore sync functions 42 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 16 to 31 OK 43 | // sycc.h also has get_cpuid function 44 | #include "hardware/sync.h" 45 | // 46 | // PIO program/setup include 47 | #include "pio_ntsc.h" 48 | // 49 | using namespace rtos; // we will be using rtos::ThisThread 50 | using namespace mbed; 51 | 52 | // ========================================== 53 | // video formatiing variables 54 | // The screen buffer 55 | // 208 lines x 8 words/line 56 | int DMA_image_buffer[1664] ; 57 | // pointe to screen buffer 58 | int ptr_table ; 59 | 60 | //One bit masks for each bit of an INT 61 | unsigned int pos[32] = {0x80000000,0x40000000,0x20000000,0x10000000,0x08000000,0x04000000,0x02000000,0x01000000, 62 | 0x800000,0x400000,0x200000,0x100000,0x080000,0x040000,0x020000,0x010000, 63 | 0x8000,0x4000,0x2000,0x1000,0x0800,0x0400,0x0200,0x0100, 64 | 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; 65 | 66 | #include "ascii_characters.h" 67 | #define top 0 68 | #define left 0 69 | #define right 255 70 | #define bottom 204 71 | #define erase_screen memset(DMA_image_buffer,0,1664*4); 72 | 73 | //== plot a point ========================================== 74 | //plot one point 75 | //at x,y with color 1=white 0=black 2=invert 76 | #define word_per_line 8 77 | // force rnage check if commented out 78 | #define live_dangerously 79 | 80 | inline void video_pt(int x, int y, char c) { 81 | //each line has 18 bytes 82 | //calculate i based upon this and x,y 83 | // the word with the pixel in it 84 | // optional range check 85 | #ifndef live_dangerously 86 | if(xright || y>bottom) return ; 87 | #endif 88 | //int i = (x/32) + y*8 89 | int i = (x >> 5) + y * word_per_line ; 90 | if (c==1) 91 | DMA_image_buffer[i] = DMA_image_buffer[i] | pos[x & 0x1f]; 92 | else if (c==0) 93 | DMA_image_buffer[i] = DMA_image_buffer[i] & ~pos[x & 0x1f]; 94 | else 95 | DMA_image_buffer[i] = DMA_image_buffer[i] ^ pos[x & 0x1f]; 96 | } 97 | 98 | //============================================================================= 99 | //plot a line 100 | //at x1,y1 to x2,y2 with color 1=white 0=black 2=invert 101 | //NOTE: this function requires signed chars 102 | //Code is from David Rodgers, 103 | //"Procedural Elements of Computer Graphics",1985 104 | void video_line(int x1, int y1, int x2, int y2, char c) { 105 | int e; 106 | signed int dx,dy,j, temp; 107 | signed char s1,s2, xchange; 108 | signed int x,y; 109 | 110 | x = x1; 111 | y = y1; 112 | 113 | //take absolute value 114 | if (x2 < x1) { 115 | dx = x1 - x2; 116 | s1 = -1; 117 | } 118 | 119 | else if (x2 == x1) { 120 | dx = 0; 121 | s1 = 0; 122 | } 123 | 124 | else { 125 | dx = x2 - x1; 126 | s1 = 1; 127 | } 128 | 129 | if (y2 < y1) { 130 | dy = y1 - y2; 131 | s2 = -1; 132 | } 133 | 134 | else if (y2 == y1) { 135 | dy = 0; 136 | s2 = 0; 137 | } 138 | 139 | else { 140 | dy = y2 - y1; 141 | s2 = 1; 142 | } 143 | 144 | xchange = 0; 145 | 146 | if (dy>dx) { 147 | temp = dx; 148 | dx = dy; 149 | dy = temp; 150 | xchange = 1; 151 | } 152 | 153 | e = ((int)dy<<1) - dx; 154 | 155 | for (j=0; j<=dx; j++) { 156 | video_pt(x,y,c); 157 | 158 | if (e>=0) { 159 | if (xchange==1) x = x + s1; 160 | else y = y + s2; 161 | e = e - ((int)dx<<1); 162 | } 163 | 164 | if (xchange==1) y = y + s2; 165 | else x = x + s1; 166 | 167 | e = e + ((int)dy<<1); 168 | } 169 | } 170 | 171 | //============================================================= 172 | // put a string of big characters on the screen 173 | void video_string(int x, int y, char *str) { 174 | char char_num; 175 | int i; 176 | int y_pos; 177 | int j; 178 | for (char_num=0; str[char_num]!=0; char_num++) { 179 | for (i=0;i<7;i++) { 180 | y_pos = y + i; 181 | // get the template for the next character 182 | j = ascii[str[char_num]][i] ; 183 | video_pt(x, y_pos, (j & 0x80)==0x80); 184 | video_pt(x+1, y_pos, (j & 0x40)==0x40); 185 | video_pt(x+2, y_pos, (j & 0x20)==0x20); 186 | video_pt(x+3, y_pos, (j & 0x10)==0x10); 187 | video_pt(x+4, y_pos, (j & 0x08)==0x08); 188 | } 189 | x = x+6; 190 | } 191 | } 192 | 193 | // =========================================== 194 | // 195 | // core 0 will run the UI for PIO testing 196 | // and blinks an LED 197 | 198 | // === CORE 0 setup ========================== 199 | // Declaring the Thread objects (MBED) 200 | // NOTE: threads hang until serial monitor is attached 201 | Thread led25_thread; 202 | Thread print_thread ; 203 | 204 | // pio assembler link 205 | PIO pio = pio0; 206 | uint offset0 = pio_add_program(pio, &ntsc_sync_program); 207 | // set up pio state machine 208 | void pio_ntsc_sync_start(PIO pio, uint sm, uint offset, uint pin) { 209 | ntsc_sync_program_init(pio, sm, offset, pin); 210 | pio_sm_set_clkdiv (pio, sm, 294.0 ) ; // 294.2367 211 | // drain the FIFOs 212 | pio_sm_clear_fifos (pio, sm); 213 | pio_sm_set_enabled(pio, sm, true); 214 | } 215 | 216 | // pio assembler link 217 | uint offset1 = pio_add_program(pio, &ntsc_data_program); 218 | // set up pio state machine 219 | void pio_ntsc_data_start(PIO pio, uint sm, uint offset, uint pin) { 220 | ntsc_data_program_init(pio, sm, offset, pin); 221 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 222 | // drain the FIFOs 223 | pio_sm_clear_fifos (pio, sm); 224 | pio_sm_set_enabled(pio, sm, true); 225 | } 226 | // Thread function pin toggle (MBED) 227 | DigitalOut LED_p25(p25); 228 | // Thread functions DO NOT exit 229 | void led25_function() { 230 | while(1){ 231 | LED_p25 = 1; 232 | ThisThread::sleep_for(250); 233 | LED_p25 = 0; 234 | ThisThread::sleep_for(250); 235 | } 236 | } 237 | 238 | // thread to make a serial port and use it 239 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 240 | // 241 | // Thread functions DO NOT exit 242 | USBSerial serial_port ; // (MBED object) 243 | // 244 | // 245 | // serial thead 246 | void serial_function() { 247 | 248 | // the usual sart message 249 | serial_port.printf("Starting ...PIO\r\n") ; 250 | 251 | // === NTSC generation =========== 252 | // === start the PIO's 253 | // NOTE BENE! START PIO machines BEFORE DMA channels 254 | #define gpio16 16 255 | #define machine0 0 256 | #define machine1 1 257 | // start sm's running 258 | pio_ntsc_sync_start(pio, machine0, offset0, gpio16); 259 | pio_ntsc_data_start(pio, machine1, offset1, gpio16); 260 | // give the state machine initial data 261 | 262 | // 208 active lines to sync machine 263 | #define num_active_lines 207 264 | pio_sm_put_blocking (pio, machine0, num_active_lines); 265 | 266 | // === DMA channels setup ===================== // 267 | // TWO DMA channels to send data to sstate machine 1 268 | // ctrl chanel resets data channel read addr 269 | int ctrl_chan = dma_claim_unused_channel(true); 270 | // data channel move sine taable to PWM 271 | int data_chan = dma_claim_unused_channel(true); 272 | // 273 | ptr_table = (int)DMA_image_buffer; 274 | // 275 | // the control channel to reset read addrs of data channel 276 | dma_channel_config c = dma_channel_get_default_config(ctrl_chan); 277 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 278 | channel_config_set_read_increment(&c, false); 279 | channel_config_set_write_increment(&c, false); 280 | channel_config_set_irq_quiet(&c, true); 281 | channel_config_set_enable(&c, true); 282 | channel_config_set_chain_to(&c, data_chan) ; 283 | // 284 | dma_channel_configure (ctrl_chan, &c, 285 | &dma_hw->ch[data_chan].read_addr, // write addr in other channel 286 | &ptr_table, // read_addr, pointer-to-ponter to table 287 | 1, // transfer count 288 | 0) ; // trigger 289 | 290 | // The acdtual data channel 291 | dma_channel_config c2 = dma_channel_get_default_config(data_chan); 292 | channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); 293 | channel_config_set_read_increment(&c2, true); 294 | channel_config_set_write_increment(&c2, false); 295 | channel_config_set_irq_quiet(&c2, true); 296 | channel_config_set_enable(&c2, true); 297 | channel_config_set_chain_to(&c2, ctrl_chan) ; 298 | channel_config_set_dreq(&c2, DREQ_PIO0_TX1); 299 | // #define DREQ_TIMER0 0x3b 300 | //dma_hw->timer[0] = (0xffff | (0x0fff<<16) ) ; 301 | // channel_config_set_dreq(&c2, DREQ_TIMER0); 302 | // 303 | dma_channel_configure(data_chan, &c2, 304 | &pio->txf[1], // write_addr, sm1 TX fifo pio0_hw 305 | DMA_image_buffer, // read_addr, the table 306 | 1664, // len(DMA_image_buffer), 1664 307 | 1) ; // trigger 308 | 309 | // === 310 | // wait a frame or two to sync up buffers 311 | ThisThread::sleep_for(30); 312 | erase_screen ; 313 | char video_text[] = "Cornell ece4760 rp2040 NTSC video \0" ; 314 | 315 | video_line(left, bottom, right, bottom, 1); 316 | video_line(left, top, right, top, 1); 317 | video_line(left, top, left, bottom, 1); 318 | video_line(right, top, right, bottom, 1); 319 | 320 | int t_start = time_us_32(); 321 | for(int i=0; i<100; i++){ 322 | for(int j=0; j<100; j++){ 323 | video_pt(i+(j&1), j, i&1); 324 | } 325 | } 326 | //float dt = (float)(time_us_32()-t_start); 327 | //serial_port.printf("time time/point %f %f\r\n", dt, dt/(float)(100*100)) ; 328 | 329 | // test lines 330 | // void video_line(int x1, int y1, int x2, int y2, char c) 331 | for(int i=0; i<200; i+=10){ 332 | video_line(128, 100, right, i, 1); 333 | } 334 | for(int i=110; i<160; i+=4){ 335 | video_line(10, i, 60, i, 1); 336 | } 337 | for(int i=70; i<120; i+=3){ 338 | video_line(i, 110, i, 160, 1); 339 | } 340 | 341 | // test strings 342 | // void video_string(int x, int y, char *str) 343 | video_string(5, 195, video_text); 344 | 345 | //float dt = (float)(time_us_32()-t_start); 346 | //serial_port.printf("time time/point %f %f\r\n", dt, dt/(float)(100*100)) ; 347 | 348 | int pos = 120; 349 | while(1){ 350 | video_line(110, pos, 150, pos, 0); 351 | if(pos>190) pos = 120; 352 | else pos++ ; 353 | video_line(110, pos, 150, pos, 1); 354 | ThisThread::sleep_for(16); 355 | //busy_wait_us (50); 356 | } 357 | } 358 | 359 | // ==== core 0 MAIN: Start everything ============ 360 | // program entry 361 | int main() { 362 | // start blink thread -- MBED 363 | led25_thread.start(led25_function); 364 | // start print thread -- MBED 365 | print_thread.start(serial_function); 366 | } 367 | // end //// 368 | /////////// -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/PIO_NTSC_DMA_4_bit.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * NTSC sync and data using DMA transfer 3 | * Useful raster is 256 wide by 204 pixels high 4 | * 5 | * using fake progreassive scan 6 | * See https://sagargv.blogspot.com/2014/07/ntsc-demystified-color-demo-with.html 7 | * 8 | * 263 scan lines. 9 | * one line is exactly 63.555 microseconds long 10 | * H-sync pulse is zero volts for 4.7 uSec, but not too critical 11 | * V-sync pulse is zero volts for 29.4 uSec, but not too critical 12 | * if line 1 is V-synch then content can start on line 21 or so and continue 13 | * until line 260 14 | * LInes 1-20 and 261-263 should hold video data at zero 15 | * about 55 uSec of each active scan line can contain pixels 16 | * a 5 Mpixel/sec rate gives about 55*5 = 275 pixels/line, so use 256 pixels 17 | * 18 | * PIO implementation: 19 | * One line time should require waits of <32 cycles so that loops are not required. 20 | * Choose sync machine clock rate so that 27 cycles give 63.555 uSec, which is 21 | * 15,734 Hz. So machine clock should be 27*15734=424828 Hz 22 | * Ths colck choice means that an H-sync pulse is exactly two cycles and 23 | * V-sync is approx 13 cycles. 24 | * The clock divider is therfore 125e6/424828 = 294.2367 25 | * 26 | * The video DAC is driven by 5 lines (4 intensity + 1 sync bit) 27 | * All 5 Resistors are tied from their output pin to a 330 resistor 28 | * the other end of the 330 resistor goes to ground 29 | * video out is across the 330 ohm resistor. 30 | * the 5 resistors are: 31 | * sync_bit 470 ohms 32 | * high-bit intensity 330 33 | * next-bit 680 34 | * next-bit 1.5K 35 | * low-bit 3.3k 36 | */ 37 | // MBED 38 | #include "math.h" 39 | #include "stdlib.h" 40 | #include "rtos.h" 41 | #include "mbed.h" 42 | #include "USBSerial.h" 43 | // C-SDK 44 | #include "hardware/adc.h" 45 | #include "hardware/gpio.h" 46 | #include "hardware/dma.h" 47 | #include "hardware/pwm.h" 48 | #include "hardware/pio.h" 49 | #include "pico/multicore.h" 50 | #include "pico/time.h" 51 | #include "hardware/clocks.h" 52 | 53 | // multicore sync functions 54 | // NOTE -- SDK uses spin-locks 0-15 -- DO NOT use 16 to 31 OK 55 | // sycc.h also has get_cpuid function 56 | #include "hardware/sync.h" 57 | // 58 | // PIO program/setup include 59 | #include "pio_ntsc_4bit.h" 60 | // 61 | using namespace rtos; // we will be using rtos::ThisThread 62 | using namespace mbed; 63 | 64 | // ========================================== 65 | // video formatiing variables 66 | // The screen buffer 67 | // 208 lines x 32 words/line, (4-bits/pixel) 68 | int DMA_image_buffer[6656] ; 69 | // pointe to screen buffer 70 | int ptr_table ; 71 | 72 | #include "ascii_characters.h" 73 | #define top 0 74 | #define left 0 75 | #define right 255 76 | #define bottom 204 77 | #define erase_screen(c) memset(DMA_image_buffer,(c|(c<<4)),6656*4); 78 | 79 | //== plot a point ========================================== 80 | //plot one point 81 | //at x,y with color 1=white 0=black 2=invert 82 | #define word_per_line 32 83 | // force range check if commented out 84 | #define live_dangerously 85 | // 86 | inline void video_pt(int x, int y, char c) { 87 | // x in pixels, y in pixels, c 0 to 3 88 | //each line has 32 words 89 | //calculate i based upon this and x,y 90 | // the word with the pixel in it 91 | // optional range check 92 | #ifndef live_dangerously 93 | if(xright || y>bottom) return ; 94 | if(c<0 || c>3) return 95 | #endif 96 | //get the image word address int i = (x/8) + y*32 97 | int i = (x >> 3) + y * word_per_line ; 98 | // position of two bits within the word 99 | // high bit number is lower pixel x-cood, twhen shit into 32-bit word 100 | int j = (7-(x & 0x7))<<2 ; 101 | // clear and set two bits at position j 102 | DMA_image_buffer[i] = (DMA_image_buffer[i] & ~(0x0f<dx) { 155 | temp = dx; 156 | dx = dy; 157 | dy = temp; 158 | xchange = 1; 159 | } 160 | 161 | e = ((int)dy<<1) - dx; 162 | 163 | for (j=0; j<=dx; j++) { 164 | video_pt(x,y,c); 165 | 166 | if (e>=0) { 167 | if (xchange==1) x = x + s1; 168 | else y = y + s2; 169 | e = e - ((int)dx<<1); 170 | } 171 | 172 | if (xchange==1) y = y + s2; 173 | else x = x + s1; 174 | 175 | e = e + ((int)dy<<1); 176 | } 177 | } 178 | 179 | //============================================================= 180 | // put a string of big characters on the screen 181 | void video_string(int x, int y, char *str, int c, int cb) { 182 | char char_num; 183 | int i; 184 | int y_pos; 185 | int j; 186 | for (char_num=0; str[char_num]!=0; char_num++) { 187 | for (i=0;i<7;i++) { 188 | y_pos = y + i; 189 | // get the template for the next character 190 | j = ascii[str[char_num]][i] ; 191 | video_pt(x, y_pos, ((j & 0x80)==0x80)?c:cb); 192 | video_pt(x+1, y_pos, ((j & 0x40)==0x40)?c:cb); 193 | video_pt(x+2, y_pos, ((j & 0x20)==0x20)?c:cb); 194 | video_pt(x+3, y_pos, ((j & 0x10)==0x10)?c:cb); 195 | video_pt(x+4, y_pos, ((j & 0x08)==0x08)?c:cb); 196 | } 197 | x = x+6; 198 | } 199 | } 200 | 201 | // =========================================== 202 | // === draw recangle ========================= 203 | // The two corners x1,y1 and x2,y2, and color c 204 | void video_rect(int x1, int y1, int x2, int y2, char c, char cb){ 205 | int x_min = min(x2,x1); 206 | int x_max = max(x2,x1); 207 | int y_min = min(y2,y1); 208 | int y_max = max(y2,y1); 209 | for (int i=x_min; i<=x_max; i++){ 210 | for (int j=y_min; j<=y_max; j++){ 211 | if(i==x1 || i==x2 || j==y1 || j==y2) 212 | video_pt(i,j,cb); 213 | else 214 | video_pt(i,j,c); 215 | } 216 | } 217 | } 218 | 219 | 220 | // =========================================== 221 | // 222 | // core 0 will run the UI for PIO testing 223 | // and blinks an LED 224 | 225 | // === CORE 0 setup ========================== 226 | // Declaring the Thread objects (MBED) 227 | // NOTE: threads hang until serial monitor is attached 228 | Thread led25_thread; 229 | Thread print_thread ; 230 | 231 | // pio assembler link 232 | PIO pio = pio0; 233 | uint offset0 = pio_add_program(pio, &ntsc_sync_program); 234 | // set up pio state machine 235 | void pio_ntsc_sync_start(PIO pio, uint sm, uint offset, uint pin) { 236 | ntsc_sync_program_init(pio, sm, offset, pin); 237 | pio_sm_set_clkdiv (pio, sm, 294.0 ) ; // 294.2367 238 | // drain the FIFOs 239 | pio_sm_clear_fifos (pio, sm); 240 | pio_sm_set_enabled(pio, sm, true); 241 | } 242 | 243 | // pio assembler link 244 | uint offset1 = pio_add_program(pio, &ntsc_data_program); 245 | // set up pio state machine 246 | void pio_ntsc_data_start(PIO pio, uint sm, uint offset, uint pin) { 247 | ntsc_data_program_init(pio, sm, offset, pin); 248 | pio_sm_set_clkdiv (pio, sm, 1.0) ; 249 | // drain the FIFOs 250 | pio_sm_clear_fifos (pio, sm); 251 | pio_sm_set_enabled(pio, sm, true); 252 | } 253 | // Thread function pin toggle (MBED) 254 | DigitalOut LED_p25(p25); 255 | // Thread functions DO NOT exit 256 | void led25_function() { 257 | while(1){ 258 | LED_p25 = 1; 259 | ThisThread::sleep_for(250); 260 | LED_p25 = 0; 261 | ThisThread::sleep_for(250); 262 | } 263 | } 264 | 265 | // thread to make a serial port and use it 266 | // see https://os.mbed.com/docs/mbed-os/v6.10/apis/usbserial.html 267 | // 268 | // Thread functions DO NOT exit 269 | USBSerial serial_port ; // (MBED object) 270 | // 271 | // 272 | // serial thead 273 | void serial_function() { 274 | 275 | // the usual sart message 276 | serial_port.printf("Starting ...PIO\r\n") ; 277 | 278 | // === NTSC generation =========== 279 | // === start the PIO's 280 | // NOTE BENE! START PIO machines BEFORE DMA channels 281 | #define gpio16 16 282 | #define machine0 0 283 | #define machine1 1 284 | // start sm's running 285 | pio_ntsc_sync_start(pio, machine0, offset0, gpio16); 286 | pio_ntsc_data_start(pio, machine1, offset1, gpio16); 287 | // give the state machine initial data 288 | 289 | // 208 active lines to sync machine 290 | #define num_active_lines 207 291 | pio_sm_put_blocking (pio, machine0, num_active_lines); 292 | 293 | // === DMA channels setup ===================== // 294 | // TWO DMA channels to send data to sstate machine 1 295 | // ctrl chanel resets data channel read addr 296 | int ctrl_chan = dma_claim_unused_channel(true); 297 | // data channel move sine taable to PWM 298 | int data_chan = dma_claim_unused_channel(true); 299 | // 300 | ptr_table = (int)DMA_image_buffer; 301 | // 302 | // the control channel to reset read addrs of data channel 303 | dma_channel_config c = dma_channel_get_default_config(ctrl_chan); 304 | channel_config_set_transfer_data_size(&c, DMA_SIZE_32); 305 | channel_config_set_read_increment(&c, false); 306 | channel_config_set_write_increment(&c, false); 307 | channel_config_set_irq_quiet(&c, true); 308 | channel_config_set_enable(&c, true); 309 | channel_config_set_chain_to(&c, data_chan) ; 310 | // 311 | dma_channel_configure (ctrl_chan, &c, 312 | &dma_hw->ch[data_chan].read_addr, // write addr in other channel 313 | &ptr_table, // read_addr, pointer-to-ponter to table 314 | 1, // transfer count 315 | 0) ; // trigger 316 | 317 | // The acdtual data channel 318 | dma_channel_config c2 = dma_channel_get_default_config(data_chan); 319 | channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); 320 | channel_config_set_read_increment(&c2, true); 321 | channel_config_set_write_increment(&c2, false); 322 | channel_config_set_irq_quiet(&c2, true); 323 | channel_config_set_enable(&c2, true); 324 | channel_config_set_chain_to(&c2, ctrl_chan) ; 325 | channel_config_set_dreq(&c2, DREQ_PIO0_TX1); 326 | // #define DREQ_TIMER0 0x3b 327 | //dma_hw->timer[0] = (0xffff | (0x0fff<<16) ) ; 328 | // channel_config_set_dreq(&c2, DREQ_TIMER0); 329 | // 330 | dma_channel_configure(data_chan, &c2, 331 | &pio->txf[1], // write_addr, sm1 TX fifo pio0_hw 332 | DMA_image_buffer, // read_addr, the table 333 | 6656, // len(DMA_image_buffer), 1664 334 | 1) ; // trigger 335 | 336 | // === 337 | // wait a frame or two to sync up buffers 338 | ThisThread::sleep_for(30); 339 | 340 | // define some text to draw later 341 | char video_text[] = "Cornell ece4760 rp2040 NTSC video \0" ; 342 | char dim_text[] = "Bruce Land, Sept 2021 \0" ; 343 | char res_text[] = "255x204 4-bit gray scale \0" ; 344 | char gray_levels[] = "0 1 2 3 4 5 6 7 8 9 A B C D E F \0" ; 345 | 346 | // gray scale defs 347 | // gray range is zero to 15 348 | // below 3 is too dim to use, probably 349 | #define white 15 350 | #define black 0 351 | #define gray_100 15 352 | // levels less than 4 ar almost black 353 | // BUT this can change depending on the TV and details of the video DAC 354 | // gray_percent of full scale gray_18 is 18% 355 | #define gray_6 1 356 | #define gray_12 2 357 | #define gray_18 3 358 | #define gray_25 4 359 | #define gray_30 5 360 | #define gray_37 6 361 | #define gray_44 7 362 | #define gray_50 8 363 | #define gray_56 9 364 | #define gray_62 10 365 | #define gray_68 11 366 | #define gray_75 12 367 | #define gray_80 13 368 | #define gray_88 14 369 | #define gray_94 15 370 | 371 | erase_screen(gray_30) ; 372 | 373 | // bound the screen 374 | video_line(left, bottom, right, bottom, white); 375 | video_line(left, top, right, top, white); 376 | video_line(left, top, left, bottom, white); 377 | video_line(right, top, right, bottom, white); 378 | 379 | // draw 16 rectangles and fill them with the gray scale 380 | int j=0; 381 | for(int i=1; i<192; i+=12){ 382 | video_rect(i, 1, i+12, 60, j, j); 383 | j++; 384 | } 385 | // 386 | // label the rectangles 387 | video_string(3, 62, gray_levels, white, gray_30) ; 388 | 389 | //float dt = (float)(time_us_32()-t_start); 390 | //serial_port.printf("time time/point %f %f\r\n", dt, dt/(float)(100*100)) ; 391 | 392 | // test angled lines 393 | // void video_line(int x1, int y1, int x2, int y2, char c) 394 | j = 3; 395 | for(int i=0; i<200; i+=6){ 396 | video_line(128, 100, right, i, j); 397 | j++; 398 | if (j==16) j = 3 ; 399 | } 400 | 401 | // short horizontal lines 402 | for(int i=70; i<100; i+=4){ 403 | video_line(10, i, 60, i, 6); 404 | } 405 | for(int i=104; i<134; i+=4){ 406 | video_line(10, i, 60, i, 4); 407 | } 408 | for(int i=138; i<168; i+=4){ 409 | video_line(10, i, 60, i, 8); 410 | } 411 | 412 | // test rect outline 413 | video_rect(70, 120, 120, 150, gray_44, gray_88); 414 | 415 | // static test strings 416 | // void video_string(int x, int y, char *str) 417 | video_string(5, 175, video_text, white, gray_30); 418 | video_string(10, 195, dim_text, gray_62, gray_30); 419 | video_string(15, 185, res_text, white, gray_30); 420 | 421 | //float dt = (float)(time_us_32()-t_start); 422 | //serial_port.printf("time time/point %f %f\r\n", dt, dt/(float)(100*100)) ; 423 | 424 | int t=0 ; 425 | char video_str_buffer[16] ; 426 | while(1){ 427 | // print the time 428 | sprintf(video_str_buffer, "%5d \0", t++); 429 | video_string(220, 195, video_str_buffer, white, gray_30); 430 | ThisThread::sleep_for(1000); 431 | //busy_wait_us (50); 432 | } 433 | } 434 | 435 | // ==== core 0 MAIN: Start everything ============ 436 | // program entry 437 | int main() { 438 | // start blink thread -- MBED 439 | led25_thread.start(led25_function); 440 | // start print thread -- MBED 441 | print_thread.start(serial_function); 442 | } 443 | // end //// 444 | /////////// -------------------------------------------------------------------------------- /PIO/NTSC_1_bit/ascii_characters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: ascii_characters.h 3 | * Author: brl4 4 | * 5 | * Created on June 30, 2014, 9:52 AM 6 | */ 7 | 8 | #ifndef ASCII_CHARACTERS_H 9 | #define ASCII_CHARACTERS_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | //=============================================== 16 | // Full ascii 5x7 char set 17 | // Designed by: David Perez de la Cruz,and Ed Lau 18 | // see: http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2005/dp93/index.html 19 | 20 | static const char ascii[128][7] = { 21 | //0 22 | 0b00000000, 23 | 0b00000000, 24 | 0b00000000, 25 | 0b00000000, 26 | 0b00000000, 27 | 0b00000000, 28 | 0b00000000, 29 | //1 30 | 0b00000000, 31 | 0b00000000, 32 | 0b00000000, 33 | 0b00000000, 34 | 0b00000000, 35 | 0b00000000, 36 | 0b00000000, 37 | //2 38 | 0b00000000, 39 | 0b00000000, 40 | 0b00000000, 41 | 0b00000000, 42 | 0b00000000, 43 | 0b00000000, 44 | 0b00000000, 45 | //3 46 | 0b00000000, 47 | 0b00000000, 48 | 0b00000000, 49 | 0b00000000, 50 | 0b00000000, 51 | 0b00000000, 52 | 0b00000000, 53 | //4 54 | 0b00000000, 55 | 0b00000000, 56 | 0b00000000, 57 | 0b00000000, 58 | 0b00000000, 59 | 0b00000000, 60 | 0b00000000, 61 | //5 62 | 0b00000000, 63 | 0b00000000, 64 | 0b00000000, 65 | 0b00000000, 66 | 0b00000000, 67 | 0b00000000, 68 | 0b00000000, 69 | //6 70 | 0b00000000, 71 | 0b00000000, 72 | 0b00000000, 73 | 0b00000000, 74 | 0b00000000, 75 | 0b00000000, 76 | 0b00000000, 77 | //7 78 | 0b00000000, 79 | 0b00000000, 80 | 0b00000000, 81 | 0b00000000, 82 | 0b00000000, 83 | 0b00000000, 84 | 0b00000000, 85 | //8 86 | 0b00000000, 87 | 0b00000000, 88 | 0b00000000, 89 | 0b00000000, 90 | 0b00000000, 91 | 0b00000000, 92 | 0b00000000, 93 | //9 94 | 0b00000000, 95 | 0b00000000, 96 | 0b00000000, 97 | 0b00000000, 98 | 0b00000000, 99 | 0b00000000, 100 | 0b00000000, 101 | //10 102 | 0b00000000, 103 | 0b00000000, 104 | 0b00000000, 105 | 0b00000000, 106 | 0b00000000, 107 | 0b00000000, 108 | 0b00000000, 109 | //11 110 | 0b00000000, 111 | 0b00000000, 112 | 0b00000000, 113 | 0b00000000, 114 | 0b00000000, 115 | 0b00000000, 116 | 0b00000000, 117 | //12 118 | 0b00000000, 119 | 0b00000000, 120 | 0b00000000, 121 | 0b00000000, 122 | 0b00000000, 123 | 0b00000000, 124 | 0b00000000, 125 | //13 126 | 0b00000000, 127 | 0b00000000, 128 | 0b00000000, 129 | 0b00000000, 130 | 0b00000000, 131 | 0b00000000, 132 | 0b00000000, 133 | //14 134 | 0b00000000, 135 | 0b00000000, 136 | 0b00000000, 137 | 0b00000000, 138 | 0b00000000, 139 | 0b00000000, 140 | 0b00000000, 141 | //15 142 | 0b00000000, 143 | 0b00000000, 144 | 0b00000000, 145 | 0b00000000, 146 | 0b00000000, 147 | 0b00000000, 148 | 0b00000000, 149 | //16 150 | 0b00000000, 151 | 0b00000000, 152 | 0b00000000, 153 | 0b00000000, 154 | 0b00000000, 155 | 0b00000000, 156 | 0b00000000, 157 | //17 158 | 0b00000000, 159 | 0b00000000, 160 | 0b00000000, 161 | 0b00000000, 162 | 0b00000000, 163 | 0b00000000, 164 | 0b00000000, 165 | //18 166 | 0b00000000, 167 | 0b00000000, 168 | 0b00000000, 169 | 0b00000000, 170 | 0b00000000, 171 | 0b00000000, 172 | 0b00000000, 173 | //19 174 | 0b00000000, 175 | 0b00000000, 176 | 0b00000000, 177 | 0b00000000, 178 | 0b00000000, 179 | 0b00000000, 180 | 0b00000000, 181 | //20 182 | 0b00000000, 183 | 0b00000000, 184 | 0b00000000, 185 | 0b00000000, 186 | 0b00000000, 187 | 0b00000000, 188 | 0b00000000, 189 | //21 190 | 0b00000000, 191 | 0b00000000, 192 | 0b00000000, 193 | 0b00000000, 194 | 0b00000000, 195 | 0b00000000, 196 | 0b00000000, 197 | //22 198 | 0b00000000, 199 | 0b00000000, 200 | 0b00000000, 201 | 0b00000000, 202 | 0b00000000, 203 | 0b00000000, 204 | 0b00000000, 205 | //23 206 | 0b00000000, 207 | 0b00000000, 208 | 0b00000000, 209 | 0b00000000, 210 | 0b00000000, 211 | 0b00000000, 212 | 0b00000000, 213 | //24 214 | 0b00000000, 215 | 0b00000000, 216 | 0b00000000, 217 | 0b00000000, 218 | 0b00000000, 219 | 0b00000000, 220 | 0b00000000, 221 | //25 222 | 0b00000000, 223 | 0b00000000, 224 | 0b00000000, 225 | 0b00000000, 226 | 0b00000000, 227 | 0b00000000, 228 | 0b00000000, 229 | //26 230 | 0b00000000, 231 | 0b00000000, 232 | 0b00000000, 233 | 0b00000000, 234 | 0b00000000, 235 | 0b00000000, 236 | 0b00000000, 237 | //27 238 | 0b00000000, 239 | 0b00000000, 240 | 0b00000000, 241 | 0b00000000, 242 | 0b00000000, 243 | 0b00000000, 244 | 0b00000000, 245 | //28 246 | 0b00000000, 247 | 0b00000000, 248 | 0b00000000, 249 | 0b00000000, 250 | 0b00000000, 251 | 0b00000000, 252 | 0b00000000, 253 | //29 254 | 0b00000000, 255 | 0b00000000, 256 | 0b00000000, 257 | 0b00000000, 258 | 0b00000000, 259 | 0b00000000, 260 | 0b00000000, 261 | //30 262 | 0b00000000, 263 | 0b00000000, 264 | 0b00000000, 265 | 0b00000000, 266 | 0b00000000, 267 | 0b00000000, 268 | 0b00000000, 269 | //31 270 | 0b00000000, 271 | 0b00000000, 272 | 0b00000000, 273 | 0b00000000, 274 | 0b00000000, 275 | 0b00000000, 276 | 0b00000000, 277 | //32 Space 278 | 0b00000000, 279 | 0b00000000, 280 | 0b00000000, 281 | 0b00000000, 282 | 0b00000000, 283 | 0b00000000, 284 | 0b00000000, 285 | //33 Exclamation ! 286 | 0b01100000, 287 | 0b01100000, 288 | 0b01100000, 289 | 0b01100000, 290 | 0b00000000, 291 | 0b00000000, 292 | 0b01100000, 293 | //34 Quotes " 294 | 0b01010000, 295 | 0b01010000, 296 | 0b00000000, 297 | 0b00000000, 298 | 0b00000000, 299 | 0b00000000, 300 | 0b00000000, 301 | //35 Number # 302 | 0b00000000, 303 | 0b01010000, 304 | 0b11111000, 305 | 0b01010000, 306 | 0b11111000, 307 | 0b01010000, 308 | 0b00000000, 309 | //36 Dollars $ 310 | 0b01110000, 311 | 0b10100000, 312 | 0b10100000, 313 | 0b01110000, 314 | 0b00101000, 315 | 0b00101000, 316 | 0b01110000, 317 | //37 Percent % 318 | 0b01000000, 319 | 0b10101000, 320 | 0b01010000, 321 | 0b00100000, 322 | 0b01010000, 323 | 0b10101000, 324 | 0b00010000, 325 | //38 Ampersand & 326 | 0b00100000, 327 | 0b01010000, 328 | 0b10100000, 329 | 0b01000000, 330 | 0b10101000, 331 | 0b10010000, 332 | 0b01101000, 333 | //39 Single Quote ' 334 | 0b01000000, 335 | 0b01000000, 336 | 0b01000000, 337 | 0b00000000, 338 | 0b00000000, 339 | 0b00000000, 340 | 0b00000000, 341 | //40 Left Parenthesis ( 342 | 0b00010000, 343 | 0b00100000, 344 | 0b01000000, 345 | 0b01000000, 346 | 0b01000000, 347 | 0b00100000, 348 | 0b00010000, 349 | //41 Right Parenthesis ) 350 | 0b01000000, 351 | 0b00100000, 352 | 0b00010000, 353 | 0b00010000, 354 | 0b00010000, 355 | 0b00100000, 356 | 0b01000000, 357 | //42 Star * 358 | 0b00010000, 359 | 0b00111000, 360 | 0b00010000, 361 | 0b00000000, 362 | 0b00000000, 363 | 0b00000000, 364 | 0b00000000, 365 | //43 Plus + 366 | 0b00000000, 367 | 0b00100000, 368 | 0b00100000, 369 | 0b11111000, 370 | 0b00100000, 371 | 0b00100000, 372 | 0b00000000, 373 | //44 Comma , 374 | 0b00000000, 375 | 0b00000000, 376 | 0b00000000, 377 | 0b00000000, 378 | 0b00000000, 379 | 0b00010000, 380 | 0b00010000, 381 | //45 Minus - 382 | 0b00000000, 383 | 0b00000000, 384 | 0b00000000, 385 | 0b00000000, 386 | 0b11111000, 387 | 0b00000000, 388 | 0b00000000, 389 | //46 Period . 390 | 0b00000000, 391 | 0b00000000, 392 | 0b00000000, 393 | 0b00000000, 394 | 0b00000000, 395 | 0b00000000, 396 | 0b00010000, 397 | // 47 Backslash / 398 | 0b00000000, 399 | 0b00001000, 400 | 0b00010000, 401 | 0b00100000, 402 | 0b01000000, 403 | 0b10000000, 404 | 0b00000000, 405 | // 48 Zero 406 | 0b01110000, 407 | 0b10001000, 408 | 0b10011000, 409 | 0b10101000, 410 | 0b11001000, 411 | 0b10001000, 412 | 0b01110000, 413 | //49 One 414 | 0b00100000, 415 | 0b01100000, 416 | 0b00100000, 417 | 0b00100000, 418 | 0b00100000, 419 | 0b00100000, 420 | 0b01110000, 421 | //50 two 422 | 0b01110000, 423 | 0b10001000, 424 | 0b00001000, 425 | 0b00010000, 426 | 0b00100000, 427 | 0b01000000, 428 | 0b11111000, 429 | //51 Three 430 | 0b11111000, 431 | 0b00010000, 432 | 0b00100000, 433 | 0b00010000, 434 | 0b00001000, 435 | 0b10001000, 436 | 0b01110000, 437 | //52 Four 438 | 0b00010000, 439 | 0b00110000, 440 | 0b01010000, 441 | 0b10010000, 442 | 0b11111000, 443 | 0b00010000, 444 | 0b00010000, 445 | //53 Five 446 | 0b11111000, 447 | 0b10000000, 448 | 0b11110000, 449 | 0b00001000, 450 | 0b00001000, 451 | 0b10001000, 452 | 0b01110000, 453 | //54 Six 454 | 0b01000000, 455 | 0b10000000, 456 | 0b10000000, 457 | 0b11110000, 458 | 0b10001000, 459 | 0b10001000, 460 | 0b01110000, 461 | //55 Seven 462 | 0b11111000, 463 | 0b00001000, 464 | 0b00010000, 465 | 0b00100000, 466 | 0b01000000, 467 | 0b10000000, 468 | 0b10000000, 469 | //56 Eight 470 | 0b01110000, 471 | 0b10001000, 472 | 0b10001000, 473 | 0b01110000, 474 | 0b10001000, 475 | 0b10001000, 476 | 0b01110000, 477 | //57 Nine 478 | 0b01110000, 479 | 0b10001000, 480 | 0b10001000, 481 | 0b01111000, 482 | 0b00001000, 483 | 0b00001000, 484 | 0b00010000, 485 | //58 : 486 | 0b00000000, 487 | 0b00000000, 488 | 0b00100000, 489 | 0b00000000, 490 | 0b00000000, 491 | 0b00000000, 492 | 0b00100000, 493 | //59 ; 494 | 0b00000000, 495 | 0b00000000, 496 | 0b00100000, 497 | 0b00000000, 498 | 0b00000000, 499 | 0b00100000, 500 | 0b00100000, 501 | //60 < 502 | 0b00000000, 503 | 0b00011000, 504 | 0b01100000, 505 | 0b10000000, 506 | 0b01100000, 507 | 0b00011000, 508 | 0b00000000, 509 | //61 = 510 | 0b00000000, 511 | 0b00000000, 512 | 0b01111000, 513 | 0b00000000, 514 | 0b01111000, 515 | 0b00000000, 516 | 0b00000000, 517 | //62 > 518 | 0b00000000, 519 | 0b11000000, 520 | 0b00110000, 521 | 0b00001000, 522 | 0b00110000, 523 | 0b11000000, 524 | 0b00000000, 525 | //63 ? 526 | 0b00110000, 527 | 0b01001000, 528 | 0b00010000, 529 | 0b00100000, 530 | 0b00100000, 531 | 0b00000000, 532 | 0b00100000, 533 | //64 @ 534 | 0b01110000, 535 | 0b10001000, 536 | 0b10111000, 537 | 0b10101000, 538 | 0b10010000, 539 | 0b10001000, 540 | 0b01110000, 541 | //65 A 542 | 0b01110000, 543 | 0b10001000, 544 | 0b10001000, 545 | 0b10001000, 546 | 0b11111000, 547 | 0b10001000, 548 | 0b10001000, 549 | //B 550 | 0b11110000, 551 | 0b10001000, 552 | 0b10001000, 553 | 0b11110000, 554 | 0b10001000, 555 | 0b10001000, 556 | 0b11110000, 557 | //C 558 | 0b01110000, 559 | 0b10001000, 560 | 0b10000000, 561 | 0b10000000, 562 | 0b10000000, 563 | 0b10001000, 564 | 0b01110000, 565 | //D 566 | 0b11110000, 567 | 0b10001000, 568 | 0b10001000, 569 | 0b10001000, 570 | 0b10001000, 571 | 0b10001000, 572 | 0b11110000, 573 | //E 574 | 0b11111000, 575 | 0b10000000, 576 | 0b10000000, 577 | 0b11111000, 578 | 0b10000000, 579 | 0b10000000, 580 | 0b11111000, 581 | //F 582 | 0b11111000, 583 | 0b10000000, 584 | 0b10000000, 585 | 0b11111000, 586 | 0b10000000, 587 | 0b10000000, 588 | 0b10000000, 589 | //G 590 | 0b01110000, 591 | 0b10001000, 592 | 0b10000000, 593 | 0b10011000, 594 | 0b10001000, 595 | 0b10001000, 596 | 0b01110000, 597 | //H 598 | 0b10001000, 599 | 0b10001000, 600 | 0b10001000, 601 | 0b11111000, 602 | 0b10001000, 603 | 0b10001000, 604 | 0b10001000, 605 | //I 606 | 0b01110000, 607 | 0b00100000, 608 | 0b00100000, 609 | 0b00100000, 610 | 0b00100000, 611 | 0b00100000, 612 | 0b01110000, 613 | //J 614 | 0b00111000, 615 | 0b00010000, 616 | 0b00010000, 617 | 0b00010000, 618 | 0b00010000, 619 | 0b10010000, 620 | 0b01100000, 621 | //K 622 | 0b10001000, 623 | 0b10010000, 624 | 0b10100000, 625 | 0b11000000, 626 | 0b10100000, 627 | 0b10010000, 628 | 0b10001000, 629 | //L 630 | 0b10000000, 631 | 0b10000000, 632 | 0b10000000, 633 | 0b10000000, 634 | 0b10000000, 635 | 0b10000000, 636 | 0b11111000, 637 | //M 638 | 0b10001000, 639 | 0b11011000, 640 | 0b10101000, 641 | 0b10101000, 642 | 0b10001000, 643 | 0b10001000, 644 | 0b10001000, 645 | //N 646 | 0b10001000, 647 | 0b10001000, 648 | 0b11001000, 649 | 0b10101000, 650 | 0b10011000, 651 | 0b10001000, 652 | 0b10001000, 653 | //O 654 | 0b01110000, 655 | 0b10001000, 656 | 0b10001000, 657 | 0b10001000, 658 | 0b10001000, 659 | 0b10001000, 660 | 0b01110000, 661 | //P 662 | 0b11110000, 663 | 0b10001000, 664 | 0b10001000, 665 | 0b11110000, 666 | 0b10000000, 667 | 0b10000000, 668 | 0b10000000, 669 | //Q 670 | 0b01110000, 671 | 0b10001000, 672 | 0b10001000, 673 | 0b10001000, 674 | 0b10101000, 675 | 0b10010000, 676 | 0b01101000, 677 | //R 678 | 0b11110000, 679 | 0b10001000, 680 | 0b10001000, 681 | 0b11110000, 682 | 0b10100000, 683 | 0b10010000, 684 | 0b10001000, 685 | //S 686 | 0b01111000, 687 | 0b10000000, 688 | 0b10000000, 689 | 0b01110000, 690 | 0b00001000, 691 | 0b00001000, 692 | 0b11110000, 693 | //T 694 | 0b11111000, 695 | 0b00100000, 696 | 0b00100000, 697 | 0b00100000, 698 | 0b00100000, 699 | 0b00100000, 700 | 0b00100000, 701 | //U 702 | 0b10001000, 703 | 0b10001000, 704 | 0b10001000, 705 | 0b10001000, 706 | 0b10001000, 707 | 0b10001000, 708 | 0b01110000, 709 | //V 710 | 0b10001000, 711 | 0b10001000, 712 | 0b10001000, 713 | 0b10001000, 714 | 0b10001000, 715 | 0b01010000, 716 | 0b00100000, 717 | //W 718 | 0b10001000, 719 | 0b10001000, 720 | 0b10001000, 721 | 0b10101000, 722 | 0b10101000, 723 | 0b10101000, 724 | 0b01010000, 725 | //X 726 | 0b10001000, 727 | 0b10001000, 728 | 0b01010000, 729 | 0b00100000, 730 | 0b01010000, 731 | 0b10001000, 732 | 0b10001000, 733 | //Y 734 | 0b10001000, 735 | 0b10001000, 736 | 0b10001000, 737 | 0b01010000, 738 | 0b00100000, 739 | 0b00100000, 740 | 0b00100000, 741 | //Z 742 | 0b11111000, 743 | 0b00001000, 744 | 0b00010000, 745 | 0b00100000, 746 | 0b01000000, 747 | 0b10000000, 748 | 0b11111000, 749 | //91 [ 750 | 0b11100000, 751 | 0b10000000, 752 | 0b10000000, 753 | 0b10000000, 754 | 0b10000000, 755 | 0b10000000, 756 | 0b11100000, 757 | //92 (backslash) 758 | 0b00000000, 759 | 0b10000000, 760 | 0b01000000, 761 | 0b00100000, 762 | 0b00010000, 763 | 0b00001000, 764 | 0b00000000, 765 | //93 ] 766 | 0b00111000, 767 | 0b00001000, 768 | 0b00001000, 769 | 0b00001000, 770 | 0b00001000, 771 | 0b00001000, 772 | 0b00111000, 773 | //94 ^ 774 | 0b00100000, 775 | 0b01010000, 776 | 0b00000000, 777 | 0b00000000, 778 | 0b00000000, 779 | 0b00000000, 780 | 0b00000000, 781 | //95 _ 782 | 0b00000000, 783 | 0b00000000, 784 | 0b00000000, 785 | 0b00000000, 786 | 0b00000000, 787 | 0b00000000, 788 | 0b11111000, 789 | //96 ` 790 | 0b10000000, 791 | 0b01000000, 792 | 0b00000000, 793 | 0b00000000, 794 | 0b00000000, 795 | 0b00000000, 796 | 0b00000000, 797 | //97 a 798 | 0b00000000, 799 | 0b01100000, 800 | 0b00010000, 801 | 0b01110000, 802 | 0b10010000, 803 | 0b01100000, 804 | 0b00000000, 805 | //98 b 806 | 0b10000000, 807 | 0b10000000, 808 | 0b11100000, 809 | 0b10010000, 810 | 0b10010000, 811 | 0b11100000, 812 | 0b00000000, 813 | //99 c 814 | 0b00000000, 815 | 0b00000000, 816 | 0b01110000, 817 | 0b10000000, 818 | 0b10000000, 819 | 0b01110000, 820 | 0b00000000, 821 | // 100 d 822 | 0b00010000, 823 | 0b00010000, 824 | 0b01110000, 825 | 0b10010000, 826 | 0b10010000, 827 | 0b01110000, 828 | 0b00000000, 829 | //101 e 830 | 0b00000000, 831 | 0b01100000, 832 | 0b10010000, 833 | 0b11110000, 834 | 0b10000000, 835 | 0b01110000, 836 | 0b00000000, 837 | //102 f 838 | 0b00110000, 839 | 0b01000000, 840 | 0b11100000, 841 | 0b01000000, 842 | 0b01000000, 843 | 0b01000000, 844 | 0b00000000, 845 | //103 g 846 | 0b00000000, 847 | 0b01100000, 848 | 0b10010000, 849 | 0b01110000, 850 | 0b00010000, 851 | 0b00010000, 852 | 0b01100000, 853 | //104 h 854 | 0b10000000, 855 | 0b10000000, 856 | 0b11100000, 857 | 0b10010000, 858 | 0b10010000, 859 | 0b10010000, 860 | 0b00000000, 861 | //105 i 862 | 0b00000000, 863 | 0b00100000, 864 | 0b00000000, 865 | 0b00100000, 866 | 0b00100000, 867 | 0b00100000, 868 | 0b00000000, 869 | //106 j 870 | 0b00000000, 871 | 0b00010000, 872 | 0b00000000, 873 | 0b00010000, 874 | 0b00010000, 875 | 0b00010000, 876 | 0b01100000, 877 | //107 k 878 | 0b10000000, 879 | 0b10010000, 880 | 0b10100000, 881 | 0b11000000, 882 | 0b10100000, 883 | 0b10010000, 884 | 0b00000000, 885 | //108 l 886 | 0b00100000, 887 | 0b00100000, 888 | 0b00100000, 889 | 0b00100000, 890 | 0b00100000, 891 | 0b00100000, 892 | 0b00000000, 893 | //109 m 894 | 0b00000000, 895 | 0b00000000, 896 | 0b01010000, 897 | 0b10101000, 898 | 0b10101000, 899 | 0b10101000, 900 | 0b00000000, 901 | //110 n 902 | 0b00000000, 903 | 0b00000000, 904 | 0b01100000, 905 | 0b10010000, 906 | 0b10010000, 907 | 0b10010000, 908 | 0b00000000, 909 | //111 o 910 | 0b00000000, 911 | 0b00000000, 912 | 0b01100000, 913 | 0b10010000, 914 | 0b10010000, 915 | 0b01100000, 916 | 0b00000000, 917 | //112 p 918 | 0b00000000, 919 | 0b00000000, 920 | 0b01100000, 921 | 0b10010000, 922 | 0b11110000, 923 | 0b10000000, 924 | 0b10000000, 925 | //113 q 926 | 0b00000000, 927 | 0b00000000, 928 | 0b01100000, 929 | 0b10010000, 930 | 0b11110000, 931 | 0b00010000, 932 | 0b00010000, 933 | //114 r 934 | 0b00000000, 935 | 0b00000000, 936 | 0b10111000, 937 | 0b01000000, 938 | 0b01000000, 939 | 0b01000000, 940 | 0b00000000, 941 | //115 s 942 | 0b00000000, 943 | 0b00000000, 944 | 0b01110000, 945 | 0b01000000, 946 | 0b00010000, 947 | 0b01110000, 948 | 0b00000000, 949 | //116 t 950 | 0b01000000, 951 | 0b01000000, 952 | 0b11100000, 953 | 0b01000000, 954 | 0b01000000, 955 | 0b01000000, 956 | 0b00000000, 957 | // 117u 958 | 0b00000000, 959 | 0b00000000, 960 | 0b10010000, 961 | 0b10010000, 962 | 0b10010000, 963 | 0b01100000, 964 | 0b00000000, 965 | //118 v 966 | 0b00000000, 967 | 0b00000000, 968 | 0b10001000, 969 | 0b10001000, 970 | 0b01010000, 971 | 0b00100000, 972 | 0b00000000, 973 | //119 w 974 | 0b00000000, 975 | 0b00000000, 976 | 0b10101000, 977 | 0b10101000, 978 | 0b01010000, 979 | 0b01010000, 980 | 0b00000000, 981 | //120 x 982 | 0b00000000, 983 | 0b00000000, 984 | 0b10010000, 985 | 0b01100000, 986 | 0b01100000, 987 | 0b10010000, 988 | 0b00000000, 989 | //121 y 990 | 0b00000000, 991 | 0b00000000, 992 | 0b10010000, 993 | 0b10010000, 994 | 0b01100000, 995 | 0b01000000, 996 | 0b10000000, 997 | //122 z 998 | 0b00000000, 999 | 0b00000000, 1000 | 0b11110000, 1001 | 0b00100000, 1002 | 0b01000000, 1003 | 0b11110000, 1004 | 0b00000000, 1005 | //123 { 1006 | 0b00100000, 1007 | 0b01000000, 1008 | 0b01000000, 1009 | 0b10000000, 1010 | 0b01000000, 1011 | 0b01000000, 1012 | 0b00100000, 1013 | //124 | 1014 | 0b00100000, 1015 | 0b00100000, 1016 | 0b00100000, 1017 | 0b00100000, 1018 | 0b00100000, 1019 | 0b00100000, 1020 | 0b00100000, 1021 | //125 } 1022 | 0b00100000, 1023 | 0b00010000, 1024 | 0b00010000, 1025 | 0b00001000, 1026 | 0b00010000, 1027 | 0b00010000, 1028 | 0b00100000, 1029 | //126 ~ 1030 | 0b00000000, 1031 | 0b00000000, 1032 | 0b01000000, 1033 | 0b10101000, 1034 | 0b00010000, 1035 | 0b00000000, 1036 | 0b00000000, 1037 | //127 DEL 1038 | 0b00000000, 1039 | 0b00000000, 1040 | 0b00000000, 1041 | 0b00000000, 1042 | 0b00000000, 1043 | 0b00000000, 1044 | 0b00000000 1045 | }; 1046 | 1047 | 1048 | #ifdef __cplusplus 1049 | } 1050 | #endif 1051 | 1052 | #endif /* ASCII_CHARACTERS_H */ 1053 | 1054 | -------------------------------------------------------------------------------- /PIO/NTSC_4_bit/ascii_characters.h: -------------------------------------------------------------------------------- 1 | /* 2 | * File: ascii_characters.h 3 | * Author: brl4 4 | * 5 | * Created on June 30, 2014, 9:52 AM 6 | */ 7 | 8 | #ifndef ASCII_CHARACTERS_H 9 | #define ASCII_CHARACTERS_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | //=============================================== 16 | // Full ascii 5x7 char set 17 | // Designed by: David Perez de la Cruz,and Ed Lau 18 | // see: http://instruct1.cit.cornell.edu/courses/ee476/FinalProjects/s2005/dp93/index.html 19 | 20 | static const char ascii[128][7] = { 21 | //0 22 | 0b00000000, 23 | 0b00000000, 24 | 0b00000000, 25 | 0b00000000, 26 | 0b00000000, 27 | 0b00000000, 28 | 0b00000000, 29 | //1 30 | 0b00000000, 31 | 0b00000000, 32 | 0b00000000, 33 | 0b00000000, 34 | 0b00000000, 35 | 0b00000000, 36 | 0b00000000, 37 | //2 38 | 0b00000000, 39 | 0b00000000, 40 | 0b00000000, 41 | 0b00000000, 42 | 0b00000000, 43 | 0b00000000, 44 | 0b00000000, 45 | //3 46 | 0b00000000, 47 | 0b00000000, 48 | 0b00000000, 49 | 0b00000000, 50 | 0b00000000, 51 | 0b00000000, 52 | 0b00000000, 53 | //4 54 | 0b00000000, 55 | 0b00000000, 56 | 0b00000000, 57 | 0b00000000, 58 | 0b00000000, 59 | 0b00000000, 60 | 0b00000000, 61 | //5 62 | 0b00000000, 63 | 0b00000000, 64 | 0b00000000, 65 | 0b00000000, 66 | 0b00000000, 67 | 0b00000000, 68 | 0b00000000, 69 | //6 70 | 0b00000000, 71 | 0b00000000, 72 | 0b00000000, 73 | 0b00000000, 74 | 0b00000000, 75 | 0b00000000, 76 | 0b00000000, 77 | //7 78 | 0b00000000, 79 | 0b00000000, 80 | 0b00000000, 81 | 0b00000000, 82 | 0b00000000, 83 | 0b00000000, 84 | 0b00000000, 85 | //8 86 | 0b00000000, 87 | 0b00000000, 88 | 0b00000000, 89 | 0b00000000, 90 | 0b00000000, 91 | 0b00000000, 92 | 0b00000000, 93 | //9 94 | 0b00000000, 95 | 0b00000000, 96 | 0b00000000, 97 | 0b00000000, 98 | 0b00000000, 99 | 0b00000000, 100 | 0b00000000, 101 | //10 102 | 0b00000000, 103 | 0b00000000, 104 | 0b00000000, 105 | 0b00000000, 106 | 0b00000000, 107 | 0b00000000, 108 | 0b00000000, 109 | //11 110 | 0b00000000, 111 | 0b00000000, 112 | 0b00000000, 113 | 0b00000000, 114 | 0b00000000, 115 | 0b00000000, 116 | 0b00000000, 117 | //12 118 | 0b00000000, 119 | 0b00000000, 120 | 0b00000000, 121 | 0b00000000, 122 | 0b00000000, 123 | 0b00000000, 124 | 0b00000000, 125 | //13 126 | 0b00000000, 127 | 0b00000000, 128 | 0b00000000, 129 | 0b00000000, 130 | 0b00000000, 131 | 0b00000000, 132 | 0b00000000, 133 | //14 134 | 0b00000000, 135 | 0b00000000, 136 | 0b00000000, 137 | 0b00000000, 138 | 0b00000000, 139 | 0b00000000, 140 | 0b00000000, 141 | //15 142 | 0b00000000, 143 | 0b00000000, 144 | 0b00000000, 145 | 0b00000000, 146 | 0b00000000, 147 | 0b00000000, 148 | 0b00000000, 149 | //16 150 | 0b00000000, 151 | 0b00000000, 152 | 0b00000000, 153 | 0b00000000, 154 | 0b00000000, 155 | 0b00000000, 156 | 0b00000000, 157 | //17 158 | 0b00000000, 159 | 0b00000000, 160 | 0b00000000, 161 | 0b00000000, 162 | 0b00000000, 163 | 0b00000000, 164 | 0b00000000, 165 | //18 166 | 0b00000000, 167 | 0b00000000, 168 | 0b00000000, 169 | 0b00000000, 170 | 0b00000000, 171 | 0b00000000, 172 | 0b00000000, 173 | //19 174 | 0b00000000, 175 | 0b00000000, 176 | 0b00000000, 177 | 0b00000000, 178 | 0b00000000, 179 | 0b00000000, 180 | 0b00000000, 181 | //20 182 | 0b00000000, 183 | 0b00000000, 184 | 0b00000000, 185 | 0b00000000, 186 | 0b00000000, 187 | 0b00000000, 188 | 0b00000000, 189 | //21 190 | 0b00000000, 191 | 0b00000000, 192 | 0b00000000, 193 | 0b00000000, 194 | 0b00000000, 195 | 0b00000000, 196 | 0b00000000, 197 | //22 198 | 0b00000000, 199 | 0b00000000, 200 | 0b00000000, 201 | 0b00000000, 202 | 0b00000000, 203 | 0b00000000, 204 | 0b00000000, 205 | //23 206 | 0b00000000, 207 | 0b00000000, 208 | 0b00000000, 209 | 0b00000000, 210 | 0b00000000, 211 | 0b00000000, 212 | 0b00000000, 213 | //24 214 | 0b00000000, 215 | 0b00000000, 216 | 0b00000000, 217 | 0b00000000, 218 | 0b00000000, 219 | 0b00000000, 220 | 0b00000000, 221 | //25 222 | 0b00000000, 223 | 0b00000000, 224 | 0b00000000, 225 | 0b00000000, 226 | 0b00000000, 227 | 0b00000000, 228 | 0b00000000, 229 | //26 230 | 0b00000000, 231 | 0b00000000, 232 | 0b00000000, 233 | 0b00000000, 234 | 0b00000000, 235 | 0b00000000, 236 | 0b00000000, 237 | //27 238 | 0b00000000, 239 | 0b00000000, 240 | 0b00000000, 241 | 0b00000000, 242 | 0b00000000, 243 | 0b00000000, 244 | 0b00000000, 245 | //28 246 | 0b00000000, 247 | 0b00000000, 248 | 0b00000000, 249 | 0b00000000, 250 | 0b00000000, 251 | 0b00000000, 252 | 0b00000000, 253 | //29 254 | 0b00000000, 255 | 0b00000000, 256 | 0b00000000, 257 | 0b00000000, 258 | 0b00000000, 259 | 0b00000000, 260 | 0b00000000, 261 | //30 262 | 0b00000000, 263 | 0b00000000, 264 | 0b00000000, 265 | 0b00000000, 266 | 0b00000000, 267 | 0b00000000, 268 | 0b00000000, 269 | //31 270 | 0b00000000, 271 | 0b00000000, 272 | 0b00000000, 273 | 0b00000000, 274 | 0b00000000, 275 | 0b00000000, 276 | 0b00000000, 277 | //32 Space 278 | 0b00000000, 279 | 0b00000000, 280 | 0b00000000, 281 | 0b00000000, 282 | 0b00000000, 283 | 0b00000000, 284 | 0b00000000, 285 | //33 Exclamation ! 286 | 0b01100000, 287 | 0b01100000, 288 | 0b01100000, 289 | 0b01100000, 290 | 0b00000000, 291 | 0b00000000, 292 | 0b01100000, 293 | //34 Quotes " 294 | 0b01010000, 295 | 0b01010000, 296 | 0b00000000, 297 | 0b00000000, 298 | 0b00000000, 299 | 0b00000000, 300 | 0b00000000, 301 | //35 Number # 302 | 0b00000000, 303 | 0b01010000, 304 | 0b11111000, 305 | 0b01010000, 306 | 0b11111000, 307 | 0b01010000, 308 | 0b00000000, 309 | //36 Dollars $ 310 | 0b01110000, 311 | 0b10100000, 312 | 0b10100000, 313 | 0b01110000, 314 | 0b00101000, 315 | 0b00101000, 316 | 0b01110000, 317 | //37 Percent % 318 | 0b01000000, 319 | 0b10101000, 320 | 0b01010000, 321 | 0b00100000, 322 | 0b01010000, 323 | 0b10101000, 324 | 0b00010000, 325 | //38 Ampersand & 326 | 0b00100000, 327 | 0b01010000, 328 | 0b10100000, 329 | 0b01000000, 330 | 0b10101000, 331 | 0b10010000, 332 | 0b01101000, 333 | //39 Single Quote ' 334 | 0b01000000, 335 | 0b01000000, 336 | 0b01000000, 337 | 0b00000000, 338 | 0b00000000, 339 | 0b00000000, 340 | 0b00000000, 341 | //40 Left Parenthesis ( 342 | 0b00010000, 343 | 0b00100000, 344 | 0b01000000, 345 | 0b01000000, 346 | 0b01000000, 347 | 0b00100000, 348 | 0b00010000, 349 | //41 Right Parenthesis ) 350 | 0b01000000, 351 | 0b00100000, 352 | 0b00010000, 353 | 0b00010000, 354 | 0b00010000, 355 | 0b00100000, 356 | 0b01000000, 357 | //42 Star * 358 | 0b00010000, 359 | 0b00111000, 360 | 0b00010000, 361 | 0b00000000, 362 | 0b00000000, 363 | 0b00000000, 364 | 0b00000000, 365 | //43 Plus + 366 | 0b00000000, 367 | 0b00100000, 368 | 0b00100000, 369 | 0b11111000, 370 | 0b00100000, 371 | 0b00100000, 372 | 0b00000000, 373 | //44 Comma , 374 | 0b00000000, 375 | 0b00000000, 376 | 0b00000000, 377 | 0b00000000, 378 | 0b00000000, 379 | 0b00010000, 380 | 0b00010000, 381 | //45 Minus - 382 | 0b00000000, 383 | 0b00000000, 384 | 0b00000000, 385 | 0b00000000, 386 | 0b11111000, 387 | 0b00000000, 388 | 0b00000000, 389 | //46 Period . 390 | 0b00000000, 391 | 0b00000000, 392 | 0b00000000, 393 | 0b00000000, 394 | 0b00000000, 395 | 0b00000000, 396 | 0b00010000, 397 | // 47 Backslash / 398 | 0b00000000, 399 | 0b00001000, 400 | 0b00010000, 401 | 0b00100000, 402 | 0b01000000, 403 | 0b10000000, 404 | 0b00000000, 405 | // 48 Zero 406 | 0b01110000, 407 | 0b10001000, 408 | 0b10011000, 409 | 0b10101000, 410 | 0b11001000, 411 | 0b10001000, 412 | 0b01110000, 413 | //49 One 414 | 0b00100000, 415 | 0b01100000, 416 | 0b00100000, 417 | 0b00100000, 418 | 0b00100000, 419 | 0b00100000, 420 | 0b01110000, 421 | //50 two 422 | 0b01110000, 423 | 0b10001000, 424 | 0b00001000, 425 | 0b00010000, 426 | 0b00100000, 427 | 0b01000000, 428 | 0b11111000, 429 | //51 Three 430 | 0b11111000, 431 | 0b00010000, 432 | 0b00100000, 433 | 0b00010000, 434 | 0b00001000, 435 | 0b10001000, 436 | 0b01110000, 437 | //52 Four 438 | 0b00010000, 439 | 0b00110000, 440 | 0b01010000, 441 | 0b10010000, 442 | 0b11111000, 443 | 0b00010000, 444 | 0b00010000, 445 | //53 Five 446 | 0b11111000, 447 | 0b10000000, 448 | 0b11110000, 449 | 0b00001000, 450 | 0b00001000, 451 | 0b10001000, 452 | 0b01110000, 453 | //54 Six 454 | 0b01000000, 455 | 0b10000000, 456 | 0b10000000, 457 | 0b11110000, 458 | 0b10001000, 459 | 0b10001000, 460 | 0b01110000, 461 | //55 Seven 462 | 0b11111000, 463 | 0b00001000, 464 | 0b00010000, 465 | 0b00100000, 466 | 0b01000000, 467 | 0b10000000, 468 | 0b10000000, 469 | //56 Eight 470 | 0b01110000, 471 | 0b10001000, 472 | 0b10001000, 473 | 0b01110000, 474 | 0b10001000, 475 | 0b10001000, 476 | 0b01110000, 477 | //57 Nine 478 | 0b01110000, 479 | 0b10001000, 480 | 0b10001000, 481 | 0b01111000, 482 | 0b00001000, 483 | 0b00001000, 484 | 0b00010000, 485 | //58 : 486 | 0b00000000, 487 | 0b00000000, 488 | 0b00100000, 489 | 0b00000000, 490 | 0b00000000, 491 | 0b00000000, 492 | 0b00100000, 493 | //59 ; 494 | 0b00000000, 495 | 0b00000000, 496 | 0b00100000, 497 | 0b00000000, 498 | 0b00000000, 499 | 0b00100000, 500 | 0b00100000, 501 | //60 < 502 | 0b00000000, 503 | 0b00011000, 504 | 0b01100000, 505 | 0b10000000, 506 | 0b01100000, 507 | 0b00011000, 508 | 0b00000000, 509 | //61 = 510 | 0b00000000, 511 | 0b00000000, 512 | 0b01111000, 513 | 0b00000000, 514 | 0b01111000, 515 | 0b00000000, 516 | 0b00000000, 517 | //62 > 518 | 0b00000000, 519 | 0b11000000, 520 | 0b00110000, 521 | 0b00001000, 522 | 0b00110000, 523 | 0b11000000, 524 | 0b00000000, 525 | //63 ? 526 | 0b00110000, 527 | 0b01001000, 528 | 0b00010000, 529 | 0b00100000, 530 | 0b00100000, 531 | 0b00000000, 532 | 0b00100000, 533 | //64 @ 534 | 0b01110000, 535 | 0b10001000, 536 | 0b10111000, 537 | 0b10101000, 538 | 0b10010000, 539 | 0b10001000, 540 | 0b01110000, 541 | //65 A 542 | 0b01110000, 543 | 0b10001000, 544 | 0b10001000, 545 | 0b10001000, 546 | 0b11111000, 547 | 0b10001000, 548 | 0b10001000, 549 | //B 550 | 0b11110000, 551 | 0b10001000, 552 | 0b10001000, 553 | 0b11110000, 554 | 0b10001000, 555 | 0b10001000, 556 | 0b11110000, 557 | //C 558 | 0b01110000, 559 | 0b10001000, 560 | 0b10000000, 561 | 0b10000000, 562 | 0b10000000, 563 | 0b10001000, 564 | 0b01110000, 565 | //D 566 | 0b11110000, 567 | 0b10001000, 568 | 0b10001000, 569 | 0b10001000, 570 | 0b10001000, 571 | 0b10001000, 572 | 0b11110000, 573 | //E 574 | 0b11111000, 575 | 0b10000000, 576 | 0b10000000, 577 | 0b11111000, 578 | 0b10000000, 579 | 0b10000000, 580 | 0b11111000, 581 | //F 582 | 0b11111000, 583 | 0b10000000, 584 | 0b10000000, 585 | 0b11111000, 586 | 0b10000000, 587 | 0b10000000, 588 | 0b10000000, 589 | //G 590 | 0b01110000, 591 | 0b10001000, 592 | 0b10000000, 593 | 0b10011000, 594 | 0b10001000, 595 | 0b10001000, 596 | 0b01110000, 597 | //H 598 | 0b10001000, 599 | 0b10001000, 600 | 0b10001000, 601 | 0b11111000, 602 | 0b10001000, 603 | 0b10001000, 604 | 0b10001000, 605 | //I 606 | 0b01110000, 607 | 0b00100000, 608 | 0b00100000, 609 | 0b00100000, 610 | 0b00100000, 611 | 0b00100000, 612 | 0b01110000, 613 | //J 614 | 0b00111000, 615 | 0b00010000, 616 | 0b00010000, 617 | 0b00010000, 618 | 0b00010000, 619 | 0b10010000, 620 | 0b01100000, 621 | //K 622 | 0b10001000, 623 | 0b10010000, 624 | 0b10100000, 625 | 0b11000000, 626 | 0b10100000, 627 | 0b10010000, 628 | 0b10001000, 629 | //L 630 | 0b10000000, 631 | 0b10000000, 632 | 0b10000000, 633 | 0b10000000, 634 | 0b10000000, 635 | 0b10000000, 636 | 0b11111000, 637 | //M 638 | 0b10001000, 639 | 0b11011000, 640 | 0b10101000, 641 | 0b10101000, 642 | 0b10001000, 643 | 0b10001000, 644 | 0b10001000, 645 | //N 646 | 0b10001000, 647 | 0b10001000, 648 | 0b11001000, 649 | 0b10101000, 650 | 0b10011000, 651 | 0b10001000, 652 | 0b10001000, 653 | //O 654 | 0b01110000, 655 | 0b10001000, 656 | 0b10001000, 657 | 0b10001000, 658 | 0b10001000, 659 | 0b10001000, 660 | 0b01110000, 661 | //P 662 | 0b11110000, 663 | 0b10001000, 664 | 0b10001000, 665 | 0b11110000, 666 | 0b10000000, 667 | 0b10000000, 668 | 0b10000000, 669 | //Q 670 | 0b01110000, 671 | 0b10001000, 672 | 0b10001000, 673 | 0b10001000, 674 | 0b10101000, 675 | 0b10010000, 676 | 0b01101000, 677 | //R 678 | 0b11110000, 679 | 0b10001000, 680 | 0b10001000, 681 | 0b11110000, 682 | 0b10100000, 683 | 0b10010000, 684 | 0b10001000, 685 | //S 686 | 0b01111000, 687 | 0b10000000, 688 | 0b10000000, 689 | 0b01110000, 690 | 0b00001000, 691 | 0b00001000, 692 | 0b11110000, 693 | //T 694 | 0b11111000, 695 | 0b00100000, 696 | 0b00100000, 697 | 0b00100000, 698 | 0b00100000, 699 | 0b00100000, 700 | 0b00100000, 701 | //U 702 | 0b10001000, 703 | 0b10001000, 704 | 0b10001000, 705 | 0b10001000, 706 | 0b10001000, 707 | 0b10001000, 708 | 0b01110000, 709 | //V 710 | 0b10001000, 711 | 0b10001000, 712 | 0b10001000, 713 | 0b10001000, 714 | 0b10001000, 715 | 0b01010000, 716 | 0b00100000, 717 | //W 718 | 0b10001000, 719 | 0b10001000, 720 | 0b10001000, 721 | 0b10101000, 722 | 0b10101000, 723 | 0b10101000, 724 | 0b01010000, 725 | //X 726 | 0b10001000, 727 | 0b10001000, 728 | 0b01010000, 729 | 0b00100000, 730 | 0b01010000, 731 | 0b10001000, 732 | 0b10001000, 733 | //Y 734 | 0b10001000, 735 | 0b10001000, 736 | 0b10001000, 737 | 0b01010000, 738 | 0b00100000, 739 | 0b00100000, 740 | 0b00100000, 741 | //Z 742 | 0b11111000, 743 | 0b00001000, 744 | 0b00010000, 745 | 0b00100000, 746 | 0b01000000, 747 | 0b10000000, 748 | 0b11111000, 749 | //91 [ 750 | 0b11100000, 751 | 0b10000000, 752 | 0b10000000, 753 | 0b10000000, 754 | 0b10000000, 755 | 0b10000000, 756 | 0b11100000, 757 | //92 (backslash) 758 | 0b00000000, 759 | 0b10000000, 760 | 0b01000000, 761 | 0b00100000, 762 | 0b00010000, 763 | 0b00001000, 764 | 0b00000000, 765 | //93 ] 766 | 0b00111000, 767 | 0b00001000, 768 | 0b00001000, 769 | 0b00001000, 770 | 0b00001000, 771 | 0b00001000, 772 | 0b00111000, 773 | //94 ^ 774 | 0b00100000, 775 | 0b01010000, 776 | 0b00000000, 777 | 0b00000000, 778 | 0b00000000, 779 | 0b00000000, 780 | 0b00000000, 781 | //95 _ 782 | 0b00000000, 783 | 0b00000000, 784 | 0b00000000, 785 | 0b00000000, 786 | 0b00000000, 787 | 0b00000000, 788 | 0b11111000, 789 | //96 ` 790 | 0b10000000, 791 | 0b01000000, 792 | 0b00000000, 793 | 0b00000000, 794 | 0b00000000, 795 | 0b00000000, 796 | 0b00000000, 797 | //97 a 798 | 0b00000000, 799 | 0b01100000, 800 | 0b00010000, 801 | 0b01110000, 802 | 0b10010000, 803 | 0b01100000, 804 | 0b00000000, 805 | //98 b 806 | 0b10000000, 807 | 0b10000000, 808 | 0b11100000, 809 | 0b10010000, 810 | 0b10010000, 811 | 0b11100000, 812 | 0b00000000, 813 | //99 c 814 | 0b00000000, 815 | 0b00000000, 816 | 0b01110000, 817 | 0b10000000, 818 | 0b10000000, 819 | 0b01110000, 820 | 0b00000000, 821 | // 100 d 822 | 0b00010000, 823 | 0b00010000, 824 | 0b01110000, 825 | 0b10010000, 826 | 0b10010000, 827 | 0b01110000, 828 | 0b00000000, 829 | //101 e 830 | 0b00000000, 831 | 0b01100000, 832 | 0b10010000, 833 | 0b11110000, 834 | 0b10000000, 835 | 0b01110000, 836 | 0b00000000, 837 | //102 f 838 | 0b00110000, 839 | 0b01000000, 840 | 0b11100000, 841 | 0b01000000, 842 | 0b01000000, 843 | 0b01000000, 844 | 0b00000000, 845 | //103 g 846 | 0b00000000, 847 | 0b01100000, 848 | 0b10010000, 849 | 0b01110000, 850 | 0b00010000, 851 | 0b00010000, 852 | 0b01100000, 853 | //104 h 854 | 0b10000000, 855 | 0b10000000, 856 | 0b11100000, 857 | 0b10010000, 858 | 0b10010000, 859 | 0b10010000, 860 | 0b00000000, 861 | //105 i 862 | 0b00000000, 863 | 0b00100000, 864 | 0b00000000, 865 | 0b00100000, 866 | 0b00100000, 867 | 0b00100000, 868 | 0b00000000, 869 | //106 j 870 | 0b00000000, 871 | 0b00010000, 872 | 0b00000000, 873 | 0b00010000, 874 | 0b00010000, 875 | 0b00010000, 876 | 0b01100000, 877 | //107 k 878 | 0b10000000, 879 | 0b10010000, 880 | 0b10100000, 881 | 0b11000000, 882 | 0b10100000, 883 | 0b10010000, 884 | 0b00000000, 885 | //108 l 886 | 0b00100000, 887 | 0b00100000, 888 | 0b00100000, 889 | 0b00100000, 890 | 0b00100000, 891 | 0b00100000, 892 | 0b00000000, 893 | //109 m 894 | 0b00000000, 895 | 0b00000000, 896 | 0b01010000, 897 | 0b10101000, 898 | 0b10101000, 899 | 0b10101000, 900 | 0b00000000, 901 | //110 n 902 | 0b00000000, 903 | 0b00000000, 904 | 0b01100000, 905 | 0b10010000, 906 | 0b10010000, 907 | 0b10010000, 908 | 0b00000000, 909 | //111 o 910 | 0b00000000, 911 | 0b00000000, 912 | 0b01100000, 913 | 0b10010000, 914 | 0b10010000, 915 | 0b01100000, 916 | 0b00000000, 917 | //112 p 918 | 0b00000000, 919 | 0b00000000, 920 | 0b01100000, 921 | 0b10010000, 922 | 0b11110000, 923 | 0b10000000, 924 | 0b10000000, 925 | //113 q 926 | 0b00000000, 927 | 0b00000000, 928 | 0b01100000, 929 | 0b10010000, 930 | 0b11110000, 931 | 0b00010000, 932 | 0b00010000, 933 | //114 r 934 | 0b00000000, 935 | 0b00000000, 936 | 0b10111000, 937 | 0b01000000, 938 | 0b01000000, 939 | 0b01000000, 940 | 0b00000000, 941 | //115 s 942 | 0b00000000, 943 | 0b00000000, 944 | 0b01110000, 945 | 0b01000000, 946 | 0b00010000, 947 | 0b01110000, 948 | 0b00000000, 949 | //116 t 950 | 0b01000000, 951 | 0b01000000, 952 | 0b11100000, 953 | 0b01000000, 954 | 0b01000000, 955 | 0b01000000, 956 | 0b00000000, 957 | // 117u 958 | 0b00000000, 959 | 0b00000000, 960 | 0b10010000, 961 | 0b10010000, 962 | 0b10010000, 963 | 0b01100000, 964 | 0b00000000, 965 | //118 v 966 | 0b00000000, 967 | 0b00000000, 968 | 0b10001000, 969 | 0b10001000, 970 | 0b01010000, 971 | 0b00100000, 972 | 0b00000000, 973 | //119 w 974 | 0b00000000, 975 | 0b00000000, 976 | 0b10101000, 977 | 0b10101000, 978 | 0b01010000, 979 | 0b01010000, 980 | 0b00000000, 981 | //120 x 982 | 0b00000000, 983 | 0b00000000, 984 | 0b10010000, 985 | 0b01100000, 986 | 0b01100000, 987 | 0b10010000, 988 | 0b00000000, 989 | //121 y 990 | 0b00000000, 991 | 0b00000000, 992 | 0b10010000, 993 | 0b10010000, 994 | 0b01100000, 995 | 0b01000000, 996 | 0b10000000, 997 | //122 z 998 | 0b00000000, 999 | 0b00000000, 1000 | 0b11110000, 1001 | 0b00100000, 1002 | 0b01000000, 1003 | 0b11110000, 1004 | 0b00000000, 1005 | //123 { 1006 | 0b00100000, 1007 | 0b01000000, 1008 | 0b01000000, 1009 | 0b10000000, 1010 | 0b01000000, 1011 | 0b01000000, 1012 | 0b00100000, 1013 | //124 | 1014 | 0b00100000, 1015 | 0b00100000, 1016 | 0b00100000, 1017 | 0b00100000, 1018 | 0b00100000, 1019 | 0b00100000, 1020 | 0b00100000, 1021 | //125 } 1022 | 0b00100000, 1023 | 0b00010000, 1024 | 0b00010000, 1025 | 0b00001000, 1026 | 0b00010000, 1027 | 0b00010000, 1028 | 0b00100000, 1029 | //126 ~ 1030 | 0b00000000, 1031 | 0b00000000, 1032 | 0b01000000, 1033 | 0b10101000, 1034 | 0b00010000, 1035 | 0b00000000, 1036 | 0b00000000, 1037 | //127 DEL 1038 | 0b00000000, 1039 | 0b00000000, 1040 | 0b00000000, 1041 | 0b00000000, 1042 | 0b00000000, 1043 | 0b00000000, 1044 | 0b00000000 1045 | }; 1046 | 1047 | 1048 | #ifdef __cplusplus 1049 | } 1050 | #endif 1051 | 1052 | #endif /* ASCII_CHARACTERS_H */ 1053 | 1054 | --------------------------------------------------------------------------------