diff --git a/README.md b/README.md index 51152c7..26898a4 100644 --- a/README.md +++ b/README.md @@ -118,6 +118,7 @@ App|Description [differential_manchester](pio/differential_manchester)| Send and receive differential Manchester-encoded serial (BMC). [hub75](pio/hub75)| Display an image on a 128x64 HUB75 RGB LED matrix. [i2c](pio/i2c)| Scan an I2C bus. +[ir_nec](pio/ir_nec)| Sending and receiving IR (infra-red) codes using the PIO. [logic_analyser](pio/logic_analyser)| Use PIO and DMA to capture a logic trace of some GPIOs, whilst a PWM unit is driving them. [manchester_encoding](pio/manchester_encoding)| Send and receive Manchester-encoded serial. [pio_blink](pio/pio_blink)| Set up some PIO state machines to blink LEDs at different frequencies, according to delay counts pushed into their FIFOs. @@ -125,6 +126,7 @@ App|Description [spi](pio/spi)| Use PIO to erase, program and read an external SPI flash chip. A second example runs a loopback test with all four CPHA/CPOL combinations. [squarewave](pio/squarewave)| Drive a fast square wave onto a GPIO. This example accesses low-level PIO registers directly, instead of using the SDK functions. [st7789_lcd](pio/st7789_lcd)| Set up PIO for 62.5 Mbps serial output, and use this to display a spinning image on a ST7789 serial LCD. +[quadrature_encoder](pio/quadrature_encoder)| A quadrature encoder using PIO to maintain counts independent of the CPU. [uart_rx](pio/uart_rx)| Implement the receive component of a UART serial port. Attach it to the spare Arm UART to see it receive characters. [uart_tx](pio/uart_tx)| Implement the transmit component of a UART serial port, and print hello world. [ws2812](pio/ws2812)| Examples of driving WS2812 addressable RGB LEDs. diff --git a/i2c/bmp280_i2c/bmp280_i2c.c b/i2c/bmp280_i2c/bmp280_i2c.c index d7e17fd..004608d 100644 --- a/i2c/bmp280_i2c/bmp280_i2c.c +++ b/i2c/bmp280_i2c/bmp280_i2c.c @@ -209,14 +209,14 @@ void bmp280_get_calib_params(struct bmp280_calib_param* params) { int main() { stdio_init_all(); - // useful information for picotool - bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); - bi_decl(bi_program_description("BMP280 I2C example for the Raspberry Pi Pico")); - #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) #warning i2c / bmp280_i2c example requires a board with I2C pins puts("Default I2C pins were not defined"); #else + // useful information for picotool + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); + bi_decl(bi_program_description("BMP280 I2C example for the Raspberry Pi Pico")); + printf("Hello, BMP280! Reading temperaure and pressure values from sensor...\n"); // I2C is "open drain", pull ups to keep signal high when no data is being sent diff --git a/i2c/mcp9808_i2c/mcp9808_i2c.c b/i2c/mcp9808_i2c/mcp9808_i2c.c index 5b9519d..f1dda7c 100644 --- a/i2c/mcp9808_i2c/mcp9808_i2c.c +++ b/i2c/mcp9808_i2c/mcp9808_i2c.c @@ -66,7 +66,7 @@ float mcp9808_convert_temp(uint8_t upper_byte, uint8_t lower_byte) { return temperature; } - +#ifdef i2c_default void mcp9808_set_limits() { //Set an upper limit of 30°C for the temperature @@ -97,7 +97,7 @@ void mcp9808_set_limits() { buf[2] = crit_temp_lsb;; i2c_write_blocking(i2c_default, ADDRESS, buf, 3, false); } - +#endif int main() { @@ -117,7 +117,6 @@ int main() { gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN); // Make the I2C pins available to picotool bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); -#endif mcp9808_set_limits(); @@ -144,4 +143,5 @@ int main() { sleep_ms(1000); } +#endif } diff --git a/i2c/mma8451_i2c/mma8451_i2c.c b/i2c/mma8451_i2c/mma8451_i2c.c index c2f9529..453f5f3 100644 --- a/i2c/mma8451_i2c/mma8451_i2c.c +++ b/i2c/mma8451_i2c/mma8451_i2c.c @@ -56,11 +56,13 @@ float mma8451_convert_accel(uint16_t raw_accel) { return acceleration; } +#ifdef i2c_default void mma8451_set_state(uint8_t state) { buf[0] = REG_CTRL_REG1; buf[1] = state; // Set RST bit to 1 i2c_write_blocking(i2c_default, ADDRESS, buf, 2, false); } +#endif int main() { stdio_init_all(); diff --git a/i2c/oled_i2c/oled_i2c.c b/i2c/oled_i2c/oled_i2c.c index 1fabe4f..8a455b5 100644 --- a/i2c/oled_i2c/oled_i2c.c +++ b/i2c/oled_i2c/oled_i2c.c @@ -231,14 +231,14 @@ void render(uint8_t *buf, struct render_area *area) { int main() { stdio_init_all(); - // useful information for picotool - bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); - bi_decl(bi_program_description("OLED I2C example for the Raspberry Pi Pico")); - #if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN) #warning i2c / oled_i2d example requires a board with I2C pins puts("Default I2C pins were not defined"); #else + // useful information for picotool + bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C)); + bi_decl(bi_program_description("OLED I2C example for the Raspberry Pi Pico")); + printf("Hello, OLED display! Look at my raspberries..\n"); // I2C is "open drain", pull ups to keep signal high when no data is being diff --git a/i2c/pcf8523_i2c/pcf8523_i2c.c b/i2c/pcf8523_i2c/pcf8523_i2c.c index a7bbf28..31046a3 100644 --- a/i2c/pcf8523_i2c/pcf8523_i2c.c +++ b/i2c/pcf8523_i2c/pcf8523_i2c.c @@ -20,11 +20,11 @@ GND (physical pin 38) -> GND on PCF8520 board */ +#ifdef i2c_default + // By default these devices are on bus address 0x68 static int addr = 0x68; -#ifdef i2c_default - static void pcf8520_reset() { // Two byte reset. First byte register, second byte data // There are a load more options to set up the device in different ways that could be added here @@ -90,8 +90,6 @@ void pcf8520_set_alarm() { } } -#endif - void pcf8520_check_alarm() { // Check bit 3 of control register 2 for alarm flags uint8_t status[1]; @@ -106,6 +104,7 @@ void pcf8520_check_alarm() { } } + void pcf8520_convert_time(int conv_time[7], const uint8_t raw_time[7]) { // Convert raw data into time conv_time[0] = (10 * (int) ((raw_time[0] & 0x70) >> 4)) + ((int) (raw_time[0] & 0x0F)); @@ -116,6 +115,7 @@ void pcf8520_convert_time(int conv_time[7], const uint8_t raw_time[7]) { conv_time[5] = (10 * (int) ((raw_time[5] & 0x10) >> 4)) + ((int) (raw_time[5] & 0x0F)); conv_time[6] = (10 * (int) ((raw_time[6] & 0xF0) >> 4)) + ((int) (raw_time[6] & 0x0F)); } +#endif int main() { stdio_init_all(); @@ -158,7 +158,7 @@ int main() { // Clear terminal printf("\e[1;1H\e[2J"); } +#endif return 0; } -#endif \ No newline at end of file diff --git a/pio/addition/addition.pio b/pio/addition/addition.pio index f369333..8eddd0e 100644 --- a/pio/addition/addition.pio +++ b/pio/addition/addition.pio @@ -1,3 +1,9 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + .program addition ; Pop two 32 bit integers from the TX FIFO, add them together, and push the diff --git a/pio/hub75/hub75.pio b/pio/hub75/hub75.pio index 2d685ef..a6fb619 100644 --- a/pio/hub75/hub75.pio +++ b/pio/hub75/hub75.pio @@ -1,3 +1,9 @@ +; +; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. +; +; SPDX-License-Identifier: BSD-3-Clause +; + .program hub75_row ; side-set pin 0 is LATCH diff --git a/pio/ir_nec/ir_loopback/ir_loopback.c b/pio/ir_nec/ir_loopback/ir_loopback.c index fd79c4f..02910d8 100644 --- a/pio/ir_nec/ir_loopback/ir_loopback.c +++ b/pio/ir_nec/ir_loopback/ir_loopback.c @@ -25,43 +25,37 @@ int main() { uint rx_gpio = 15; // choose which GPIO pin is connected to the IR detector // configure and enable the state machines - // - int tx_sm = nec_tx_init (pio, tx_gpio); // uses two state machines, 16 instructions and one IRQ - int rx_sm = nec_rx_init (pio, rx_gpio); // uses one state machine and 9 instructions + int tx_sm = nec_tx_init(pio, tx_gpio); // uses two state machines, 16 instructions and one IRQ + int rx_sm = nec_rx_init(pio, rx_gpio); // uses one state machine and 9 instructions if (tx_sm == -1 || rx_sm == -1) { - printf ("could not configure PIO\n"); + printf("could not configure PIO\n"); return -1; } // transmit and receive frames - // uint8_t tx_address = 0x00, tx_data = 0x00, rx_address, rx_data; while (true) { - // create a 32-bit frame and add it to the transmit FIFO - // - uint32_t tx_frame = nec_encode_frame (tx_address, tx_data); - pio_sm_put (pio, tx_sm, tx_frame); - printf ("\nsent: %02x, %02x", tx_address, tx_data); + uint32_t tx_frame = nec_encode_frame(tx_address, tx_data); + pio_sm_put(pio, tx_sm, tx_frame); + printf("\nsent: %02x, %02x", tx_address, tx_data); // allow time for the frame to be transmitted (optional) - // - sleep_ms (100); + sleep_ms(100); // display any frames in the receive FIFO - // - while (!pio_sm_is_rx_fifo_empty (pio, rx_sm)) { - uint32_t rx_frame = pio_sm_get (pio, rx_sm); + while (!pio_sm_is_rx_fifo_empty(pio, rx_sm)) { + uint32_t rx_frame = pio_sm_get(pio, rx_sm); - if (nec_decode_frame (rx_frame, &rx_address, &rx_data)) { - printf ("\treceived: %02x, %02x", rx_address, rx_data); + if (nec_decode_frame(rx_frame, &rx_address, &rx_data)) { + printf("\treceived: %02x, %02x", rx_address, rx_data); } else { - printf ("\treceived: %08x", rx_frame); + printf("\treceived: %08x", rx_frame); } } - sleep_ms (900); + sleep_ms(900); tx_data += 1; } } diff --git a/pio/ir_nec/nec_receive_library/nec_receive.c b/pio/ir_nec/nec_receive_library/nec_receive.c index b6fb673..b034284 100644 --- a/pio/ir_nec/nec_receive_library/nec_receive.c +++ b/pio/ir_nec/nec_receive_library/nec_receive.c @@ -4,56 +4,40 @@ * SPDX-License-Identifier: BSD-3-Clause */ - // SDK types and declarations -// #include "pico/stdlib.h" #include "hardware/pio.h" #include "hardware/clocks.h" // for clock_get_hz() - -// declare the public API functions -// #include "nec_receive.h" - // import the assembled PIO state machine program -// #include "nec_receive.pio.h" - -// define the public API functions -// - // Claim an unused state machine on the specified PIO and configure it // to receive NEC IR frames on the given GPIO pin. // // Returns: the state machine number on success, otherwise -1 -// int nec_rx_init(PIO pio, uint pin_num) { // disable pull-up and pull-down on gpio pin - // - gpio_disable_pulls (pin_num); + gpio_disable_pulls(pin_num); // install the program in the PIO shared instruction space - // uint offset; - if (pio_can_add_program (pio, &nec_receive_program)) { - offset = pio_add_program (pio, &nec_receive_program); + if (pio_can_add_program(pio, &nec_receive_program)) { + offset = pio_add_program(pio, &nec_receive_program); } else { return -1; // the program could not be added } // claim an unused state machine on this PIO - // - int sm = pio_claim_unused_sm (pio, true); + int sm = pio_claim_unused_sm(pio, true); if (sm == -1) { return -1; // we were unable to claim a state machine } // configure and enable the state machine - // nec_receive_program_init(pio, sm, offset, pin_num); return sm; @@ -64,8 +48,7 @@ int nec_rx_init(PIO pio, uint pin_num) { // provided. // // Returns: `true` if the frame was valid, otherwise `false` -// -bool nec_decode_frame (uint32_t frame, uint8_t *p_address, uint8_t *p_data) { +bool nec_decode_frame(uint32_t frame, uint8_t *p_address, uint8_t *p_data) { // access the frame data as four 8-bit fields // @@ -83,14 +66,12 @@ bool nec_decode_frame (uint32_t frame, uint8_t *p_address, uint8_t *p_data) { // a valid (non-extended) 'NEC' frame should contain 8 bit // address, inverted address, data and inverted data - // if (f.address != (f.inverted_address ^ 0xff) || f.data != (f.inverted_data ^ 0xff)) { return false; } // store the validated address and data - // *p_address = f.address; *p_data = f.data; diff --git a/pio/ir_nec/nec_receive_library/nec_receive.h b/pio/ir_nec/nec_receive_library/nec_receive.h index c2e17b8..fdd6e23 100644 --- a/pio/ir_nec/nec_receive_library/nec_receive.h +++ b/pio/ir_nec/nec_receive_library/nec_receive.h @@ -1,7 +1,13 @@ +/** + * Copyright (c) 2021 mjcross + * + * SPDX-License-Identifier: BSD-3-Clause + */ + #include "pico/stdlib.h" #include "hardware/pio.h" // public API -// -int nec_rx_init (PIO pio, uint pin); -bool nec_decode_frame (uint32_t sm, uint8_t *p_address, uint8_t *p_data); + +int nec_rx_init(PIO pio, uint pin); +bool nec_decode_frame(uint32_t sm, uint8_t *p_address, uint8_t *p_data); diff --git a/pio/ir_nec/nec_transmit_library/nec_transmit.c b/pio/ir_nec/nec_transmit_library/nec_transmit.c index 1ef3f8d..03ea932 100644 --- a/pio/ir_nec/nec_transmit_library/nec_transmit.c +++ b/pio/ir_nec/nec_transmit_library/nec_transmit.c @@ -10,48 +10,34 @@ #include "pico/stdlib.h" #include "hardware/pio.h" #include "hardware/clocks.h" // for clock_get_hz() - - -// declare the public API functions -// #include "nec_transmit.h" - // import the assembled PIO state machine programs -// #include "nec_carrier_burst.pio.h" #include "nec_carrier_control.pio.h" - -// define the public API functions -// - // Claim an unused state machine on the specified PIO and configure it // to transmit NEC IR frames on the specificied GPIO pin. // // Returns: on success, the number of the carrier_control state machine // otherwise -1 -// int nec_tx_init(PIO pio, uint pin_num) { // install the carrier_burst program in the PIO shared instruction space - // uint carrier_burst_offset; - if (pio_can_add_program (pio, &nec_carrier_burst_program)) { + if (pio_can_add_program(pio, &nec_carrier_burst_program)) { carrier_burst_offset = pio_add_program(pio, &nec_carrier_burst_program); } else { return -1; } // claim an unused state machine on this PIO - // int carrier_burst_sm = pio_claim_unused_sm(pio, true); if (carrier_burst_sm == -1) { return -1; } // configure and enable the state machine - // nec_carrier_burst_program_init(pio, carrier_burst_sm, carrier_burst_offset, @@ -59,27 +45,24 @@ int nec_tx_init(PIO pio, uint pin_num) { 38.222e3); // 38.222 kHz carrier // install the carrier_control program in the PIO shared instruction space - // uint carrier_control_offset; - if (pio_can_add_program (pio, &nec_carrier_control_program)) { + if (pio_can_add_program(pio, &nec_carrier_control_program)) { carrier_control_offset = pio_add_program(pio, &nec_carrier_control_program); } else { return -1; } // claim an unused state machine on this PIO - // int carrier_control_sm = pio_claim_unused_sm(pio, true); if (carrier_control_sm == -1) { return -1; } // configure and enable the state machine - // nec_carrier_control_program_init(pio, carrier_control_sm, carrier_control_offset, - 2 * (1 / 562.5e-6), // 2 ticks per 562.5us carrier burst + 2 * (1 / 562.5e-6f), // 2 ticks per 562.5us carrier burst 32); // 32 bits per frame return carrier_control_sm; @@ -89,9 +72,7 @@ int nec_tx_init(PIO pio, uint pin_num) { // Create a frame in `NEC` format from the provided 8-bit address and data // // Returns: a 32-bit encoded frame -// -uint32_t nec_encode_frame (uint8_t address, uint8_t data) { +uint32_t nec_encode_frame(uint8_t address, uint8_t data) { // a normal 32-bit frame is encoded as address, inverted address, data, inverse data, - // return address | (address ^ 0xff) << 8 | data << 16 | (data ^ 0xff) << 24; } diff --git a/pio/ir_nec/nec_transmit_library/nec_transmit.h b/pio/ir_nec/nec_transmit_library/nec_transmit.h index 0704346..39c4937 100644 --- a/pio/ir_nec/nec_transmit_library/nec_transmit.h +++ b/pio/ir_nec/nec_transmit_library/nec_transmit.h @@ -1,7 +1,13 @@ +/** + * Copyright (c) 2021 mjcross + * + * SPDX-License-Identifier: BSD-3-Clause + */ + #include "pico/stdlib.h" #include "hardware/pio.h" // public API -// + int nec_tx_init(PIO pio, uint pin); -uint32_t nec_encode_frame (uint8_t address, uint8_t data); +uint32_t nec_encode_frame(uint8_t address, uint8_t data); diff --git a/pio/quadrature_encoder/quadrature_encoder.c b/pio/quadrature_encoder/quadrature_encoder.c index de119d9..a11ab89 100644 --- a/pio/quadrature_encoder/quadrature_encoder.c +++ b/pio/quadrature_encoder/quadrature_encoder.c @@ -1,3 +1,9 @@ +/** + * Copyright (c) 2021 pmarques-dev @ github + * + * SPDX-License-Identifier: BSD-3-Clause + */ + #include #include "pico/stdlib.h" #include "hardware/pio.h" @@ -13,7 +19,7 @@ // updated. At any point, the main code can query the current count by using // the quadrature_encoder_*_count functions. The counter is kept in a full // 32 bit register that just wraps around. Two's complement arithmetic means -// that it can be interpreted as a 32 bit signed or unsigned value and it will +// that it can be interpreted as a 32-bit signed or unsigned value, and it will // work anyway. // // As an example, a two wheel robot being controlled at 100Hz, can use two @@ -26,34 +32,30 @@ // encoder count updated and because of that it supports very high step rates. // +int main() { + int new_value, delta, old_value = 0; -int main() -{ - int new_value, delta, old_value = 0; + // Base pin to connect the A phase of the encoder. + // The B phase must be connected to the next pin + const uint PIN_AB = 10; - // Base pin to connect the A phase of the encoder. - // The B phase must be connected to the next pin - const uint PIN_AB = 10; + stdio_init_all(); - stdio_init_all(); + PIO pio = pio0; + const uint sm = 0; - PIO pio = pio0; - const uint sm = 0; + uint offset = pio_add_program(pio, &quadrature_encoder_program); + quadrature_encoder_program_init(pio, sm, offset, PIN_AB, 0); - uint offset = pio_add_program(pio, &quadrature_encoder_program); - quadrature_encoder_program_init(pio, sm, offset, PIN_AB, 0); + while (1) { + // note: thanks to two's complement arithmetic delta will always + // be correct even when new_value wraps around MAXINT / MININT + new_value = quadrature_encoder_get_count(pio, sm); + delta = new_value - old_value; + old_value = new_value; - while (1) { - // note: thanks to two's complement arithmetic delta will always - // be correct even when new_value wraps around MAXINT / MININT - new_value = quadrature_encoder_get_count(pio, sm); - delta = new_value - old_value; - old_value = new_value; - - printf("position %8d, delta %6d\n", new_value, delta); - sleep_ms(100); - } - - return 0; + printf("position %8d, delta %6d\n", new_value, delta); + sleep_ms(100); + } } diff --git a/pio/quadrature_encoder/quadrature_encoder.pio b/pio/quadrature_encoder/quadrature_encoder.pio index 34ff9e6..b2a0b82 100644 --- a/pio/quadrature_encoder/quadrature_encoder.pio +++ b/pio/quadrature_encoder/quadrature_encoder.pio @@ -1,3 +1,8 @@ +; +; Copyright (c) 2021 pmarques-dev @ github +; +; SPDX-License-Identifier: BSD-3-Clause +; .program quadrature_encoder @@ -6,7 +11,7 @@ .origin 0 -; the code works by running a loop that contiously shifts the 2 phase pins into +; the code works by running a loop that continuously shifts the 2 phase pins into ; ISR and looks at the lower 4 bits to do a computed jump to an instruction that ; does the proper "do nothing" | "increment" | "decrement" action for that pin ; state change (or no change) diff --git a/pio/ws2812/ws2812.c b/pio/ws2812/ws2812.c index 0763f53..2ee6c42 100644 --- a/pio/ws2812/ws2812.c +++ b/pio/ws2812/ws2812.c @@ -14,9 +14,12 @@ #define IS_RGBW true #define NUM_PIXELS 150 -#ifndef PICO_DEFAULT_WS2812_PIN -#warning "no WS2812 default PIN defined for board, please check if pin 2 is okay" -#define PICO_DEFAULT_WS2812_PIN 2 + +#ifdef PICO_DEFAULT_WS2812_PIN +#define WS2812_PIN PICO_DEFAULT_WS2812_PIN +#else +// default to pin 2 if the board doesn't have a default WS2812 pin defined +#define WS2812_PIN 2 #endif static inline void put_pixel(uint32_t pixel_grb) { @@ -81,14 +84,14 @@ const struct { int main() { //set_sys_clock_48(); stdio_init_all(); - puts("WS2812 Smoke Test"); + printf("WS2812 Smoke Test, using pin %d", WS2812_PIN); // todo get free sm PIO pio = pio0; int sm = 0; uint offset = pio_add_program(pio, &ws2812_program); - ws2812_program_init(pio, sm, offset, PICO_DEFAULT_WS2812_PIN, 800000, IS_RGBW); + ws2812_program_init(pio, sm, offset, WS2812_PIN, 800000, IS_RGBW); int t = 0; while (1) {