Compare commits
10 Commits
2a11e9f8f2
...
c347cb78e0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c347cb78e0 | ||
|
|
41c6e68fc4 | ||
|
|
ba4caba4f0 | ||
|
|
fbc9e9f4fb | ||
|
|
7f12071e51 | ||
|
|
ca6eb091a6 | ||
|
|
61f891433f | ||
|
|
a7ad17156b | ||
|
|
01e8128953 | ||
|
|
a7ce7007ff |
14
.gitignore
vendored
@ -5,3 +5,17 @@ cmake-*
|
||||
build
|
||||
.DS_Store
|
||||
*.pdf
|
||||
|
||||
CMakeFiles/
|
||||
Makefile
|
||||
*.bin
|
||||
*.dis
|
||||
*.elf
|
||||
*.elf.map
|
||||
*.hex
|
||||
*.uf2
|
||||
cmake_install.cmake
|
||||
elf2uf2/
|
||||
/generated
|
||||
/pico-sdk
|
||||
/CMakeCache.txt
|
||||
|
||||
@ -11,43 +11,19 @@ if (PICO_SDK_VERSION_STRING VERSION_LESS "1.3.0")
|
||||
message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.3.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}")
|
||||
endif()
|
||||
|
||||
set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})
|
||||
|
||||
# Initialize the SDK
|
||||
pico_sdk_init()
|
||||
|
||||
include(example_auto_set_url.cmake)
|
||||
# Add blink example
|
||||
add_subdirectory(blink)
|
||||
|
||||
# Add hello world example
|
||||
add_subdirectory(hello_world)
|
||||
|
||||
add_compile_options(-Wall
|
||||
-Wno-format # int != int32_t as far as the compiler is concerned because gcc has int32_t as long int
|
||||
-Wno-unused-function # we have some for the docs that aren't called
|
||||
#-Wno-unused-function # we have some for the docs that aren't called
|
||||
-Wno-maybe-uninitialized
|
||||
-Wno-comment
|
||||
)
|
||||
|
||||
# Hardware-specific examples in subdirectories:
|
||||
add_subdirectory(adc)
|
||||
add_subdirectory(clocks)
|
||||
add_subdirectory(cmake)
|
||||
add_subdirectory(divider)
|
||||
add_subdirectory(dma)
|
||||
add_subdirectory(flash)
|
||||
add_subdirectory(gpio)
|
||||
add_subdirectory(i2c)
|
||||
add_subdirectory(interp)
|
||||
add_subdirectory(multicore)
|
||||
add_subdirectory(picoboard)
|
||||
add_subdirectory(pio)
|
||||
add_subdirectory(pwm)
|
||||
add_subdirectory(reset)
|
||||
add_subdirectory(rtc)
|
||||
add_subdirectory(spi)
|
||||
add_subdirectory(system)
|
||||
add_subdirectory(timer)
|
||||
add_subdirectory(uart)
|
||||
add_subdirectory(usb)
|
||||
add_subdirectory(watchdog)
|
||||
# add all project dirs
|
||||
add_subdirectory(radio)
|
||||
|
||||
26
README.md
@ -108,6 +108,32 @@ App|Description
|
||||
[blinky](picoboard/blinky)| Blink "hello, world" in Morse code on Pico's LED
|
||||
[button](picoboard/button)| Use Pico's BOOTSEL button as a regular button input, by temporarily suspending flash access.
|
||||
|
||||
### Pico W Networking
|
||||
|
||||
These eaxmples are for the Pico W, and are only available for `PICO_BOARD=pico_w`
|
||||
|
||||
App|Description
|
||||
---|---
|
||||
[picow_access_point](pico_w/access_point)| Starts a WiFi access point, and fields DHCP requests.
|
||||
[picow_blink](pico_w/blink)| Blinks the on-board LED (which is connected via the WiFi chip).
|
||||
[picow_iperf_server](pico_w/iperf)| Runs an "iperf" server for WiFi speed testing.
|
||||
[picow_ntp_client](pico_w/ntp_client)| Connects to an NTP server to fetch and display the current time.
|
||||
[picow_tcp_client](pico_w/tcp_client)| A simple TCP client. You can run [python_test_tcp_server.py](pico_w/python_test_tcp/python_test_tcp_server.py) for it to connect to.
|
||||
[picow_tcp_server](pico_w/tcp_server)| A simple TCP server. You can use [python_test_tcp_client.py](pico_w/python_test_tcp/python_test_tcp_client.py) to connect to it.
|
||||
[picow_wifi_scan](pico_w/wifi_scan)| Scans for WiFi networks and prints the results.
|
||||
|
||||
#### FreeRTOS examples
|
||||
|
||||
These are examples of integrating Pico W networking under FreeRTOS, and require you to set the `FREERTOS_KERNEL_PATH`
|
||||
to point to the FreeRTOS Kernel.
|
||||
|
||||
App|Description
|
||||
---|---
|
||||
[picow_freertos_iperf_server_nosys](pico_w/freertos/iperf)| Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=1 mode. The LED is blinked in another task
|
||||
[picow_freertos_iperf_server_sys](pico_w/freertos/iperf)| Runs an "iperf" server for WiFi speed testing under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The LED is blinked in another task
|
||||
[picow_freertos_ping_nosys](pico_w/freertos/ping)| Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=1 mode.
|
||||
[picow_freertos_iperf_server_sys](pico_w/freertos/iperf)| Runs the lwip-contrib/apps/ping test app under FreeRTOS in NO_SYS=0 (i.e. full FreeRTOS integration) mode. The test app uses the lwIP \em socket API in this case.
|
||||
|
||||
### PIO
|
||||
|
||||
App|Description
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(adc_console)
|
||||
add_subdirectory(dma_capture)
|
||||
add_subdirectory(hello_adc)
|
||||
add_subdirectory(joystick_display)
|
||||
add_subdirectory(onboard_temperature)
|
||||
add_subdirectory(microphone_adc)
|
||||
endif ()
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(adc_console
|
||||
adc_console.c
|
||||
)
|
||||
|
||||
target_link_libraries(adc_console pico_stdlib hardware_adc)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(adc_console)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(adc_console)
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
#define N_SAMPLES 1000
|
||||
uint16_t sample_buf[N_SAMPLES];
|
||||
|
||||
void printhelp() {
|
||||
puts("\nCommands:");
|
||||
puts("c0, ...\t: Select ADC channel n");
|
||||
puts("s\t: Sample once");
|
||||
puts("S\t: Sample many");
|
||||
puts("w\t: Wiggle pins");
|
||||
}
|
||||
|
||||
void __not_in_flash_func(adc_capture)(uint16_t *buf, size_t count) {
|
||||
adc_fifo_setup(true, false, 0, false, false);
|
||||
adc_run(true);
|
||||
for (int i = 0; i < count; i = i + 1)
|
||||
buf[i] = adc_fifo_get_blocking();
|
||||
adc_run(false);
|
||||
adc_fifo_drain();
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
stdio_init_all();
|
||||
adc_init();
|
||||
adc_set_temp_sensor_enabled(true);
|
||||
|
||||
// Set all pins to input (as far as SIO is concerned)
|
||||
gpio_set_dir_all_bits(0);
|
||||
for (int i = 2; i < 30; ++i) {
|
||||
gpio_set_function(i, GPIO_FUNC_SIO);
|
||||
if (i >= 26) {
|
||||
gpio_disable_pulls(i);
|
||||
gpio_set_input_enabled(i, false);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n===========================\n");
|
||||
printf("RP2040 ADC and Test Console\n");
|
||||
printf("===========================\n");
|
||||
printhelp();
|
||||
|
||||
while (1) {
|
||||
char c = getchar();
|
||||
printf("%c", c);
|
||||
switch (c) {
|
||||
case 'c':
|
||||
c = getchar();
|
||||
printf("%c\n", c);
|
||||
if (c < '0' || c > '7') {
|
||||
printf("Unknown input channel\n");
|
||||
printhelp();
|
||||
} else {
|
||||
adc_select_input(c - '0');
|
||||
printf("Switched to channel %c\n", c);
|
||||
}
|
||||
break;
|
||||
case 's': {
|
||||
uint32_t result = adc_read();
|
||||
const float conversion_factor = 3.3f / (1 << 12);
|
||||
printf("\n0x%03x -> %f V\n", result, result * conversion_factor);
|
||||
break;
|
||||
}
|
||||
case 'S': {
|
||||
printf("\nStarting capture\n");
|
||||
adc_capture(sample_buf, N_SAMPLES);
|
||||
printf("Done\n");
|
||||
for (int i = 0; i < N_SAMPLES; i = i + 1)
|
||||
printf("%03x\n", sample_buf[i]);
|
||||
break;
|
||||
}
|
||||
case 'w':
|
||||
printf("\nPress any key to stop wiggling\n");
|
||||
int i = 1;
|
||||
gpio_set_dir_all_bits(-1);
|
||||
while (getchar_timeout_us(0) == PICO_ERROR_TIMEOUT) {
|
||||
// Pattern: Flash all pins for a cycle,
|
||||
// Then scan along pins for one cycle each
|
||||
i = i ? i << 1 : 1;
|
||||
gpio_put_all(i ? i : ~0);
|
||||
}
|
||||
gpio_set_dir_all_bits(0);
|
||||
printf("Wiggling halted.\n");
|
||||
break;
|
||||
case '\n':
|
||||
case '\r':
|
||||
break;
|
||||
case 'h':
|
||||
printhelp();
|
||||
break;
|
||||
default:
|
||||
printf("\nUnrecognised command: %c\n", c);
|
||||
printhelp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
add_executable(adc_dma_capture
|
||||
dma_capture.c
|
||||
)
|
||||
|
||||
pico_generate_pio_header(adc_dma_capture ${CMAKE_CURRENT_LIST_DIR}/resistor_dac.pio)
|
||||
|
||||
target_link_libraries(adc_dma_capture
|
||||
pico_stdlib
|
||||
hardware_adc
|
||||
hardware_dma
|
||||
# For the dummy output:
|
||||
hardware_pio
|
||||
pico_multicore
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(adc_dma_capture)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(adc_dma_capture)
|
||||
@ -1,136 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
// For ADC input:
|
||||
#include "hardware/adc.h"
|
||||
#include "hardware/dma.h"
|
||||
// For resistor DAC output:
|
||||
#include "pico/multicore.h"
|
||||
#include "hardware/pio.h"
|
||||
#include "resistor_dac.pio.h"
|
||||
|
||||
// This example uses the DMA to capture many samples from the ADC.
|
||||
//
|
||||
// - We are putting the ADC in free-running capture mode at 0.5 Msps
|
||||
//
|
||||
// - A DMA channel will be attached to the ADC sample FIFO
|
||||
//
|
||||
// - Configure the ADC to right-shift samples to 8 bits of significance, so we
|
||||
// can DMA into a byte buffer
|
||||
//
|
||||
// This could be extended to use the ADC's round robin feature to sample two
|
||||
// channels concurrently at 0.25 Msps each.
|
||||
//
|
||||
// It would be nice to have some analog samples to measure! This example also
|
||||
// drives waves out through a 5-bit resistor DAC, as found on the reference
|
||||
// VGA board. If you have that board, you can take an M-F jumper wire from
|
||||
// GPIO 26 to the Green pin on the VGA connector (top row, next-but-rightmost
|
||||
// hole). Or you can ignore that part of the code and connect your own signal
|
||||
// to the ADC input.
|
||||
|
||||
// Channel 0 is GPIO26
|
||||
#define CAPTURE_CHANNEL 0
|
||||
#define CAPTURE_DEPTH 1000
|
||||
|
||||
uint8_t capture_buf[CAPTURE_DEPTH];
|
||||
|
||||
void core1_main();
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Send core 1 off to start driving the "DAC" whilst we configure the ADC.
|
||||
multicore_launch_core1(core1_main);
|
||||
|
||||
// Init GPIO for analogue use: hi-Z, no pulls, disable digital input buffer.
|
||||
adc_gpio_init(26 + CAPTURE_CHANNEL);
|
||||
|
||||
adc_init();
|
||||
adc_select_input(CAPTURE_CHANNEL);
|
||||
adc_fifo_setup(
|
||||
true, // Write each completed conversion to the sample FIFO
|
||||
true, // Enable DMA data request (DREQ)
|
||||
1, // DREQ (and IRQ) asserted when at least 1 sample present
|
||||
false, // We won't see the ERR bit because of 8 bit reads; disable.
|
||||
true // Shift each sample to 8 bits when pushing to FIFO
|
||||
);
|
||||
|
||||
// Divisor of 0 -> full speed. Free-running capture with the divider is
|
||||
// equivalent to pressing the ADC_CS_START_ONCE button once per `div + 1`
|
||||
// cycles (div not necessarily an integer). Each conversion takes 96
|
||||
// cycles, so in general you want a divider of 0 (hold down the button
|
||||
// continuously) or > 95 (take samples less frequently than 96 cycle
|
||||
// intervals). This is all timed by the 48 MHz ADC clock.
|
||||
adc_set_clkdiv(0);
|
||||
|
||||
printf("Arming DMA\n");
|
||||
sleep_ms(1000);
|
||||
// Set up the DMA to start transferring data as soon as it appears in FIFO
|
||||
uint dma_chan = dma_claim_unused_channel(true);
|
||||
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
|
||||
|
||||
// Reading from constant address, writing to incrementing byte addresses
|
||||
channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
|
||||
channel_config_set_read_increment(&cfg, false);
|
||||
channel_config_set_write_increment(&cfg, true);
|
||||
|
||||
// Pace transfers based on availability of ADC samples
|
||||
channel_config_set_dreq(&cfg, DREQ_ADC);
|
||||
|
||||
dma_channel_configure(dma_chan, &cfg,
|
||||
capture_buf, // dst
|
||||
&adc_hw->fifo, // src
|
||||
CAPTURE_DEPTH, // transfer count
|
||||
true // start immediately
|
||||
);
|
||||
|
||||
printf("Starting capture\n");
|
||||
adc_run(true);
|
||||
|
||||
// Once DMA finishes, stop any new conversions from starting, and clean up
|
||||
// the FIFO in case the ADC was still mid-conversion.
|
||||
dma_channel_wait_for_finish_blocking(dma_chan);
|
||||
printf("Capture finished\n");
|
||||
adc_run(false);
|
||||
adc_fifo_drain();
|
||||
|
||||
// Print samples to stdout so you can display them in pyplot, excel, matlab
|
||||
for (int i = 0; i < CAPTURE_DEPTH; ++i) {
|
||||
printf("%-3d, ", capture_buf[i]);
|
||||
if (i % 10 == 9)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Code for driving the "DAC" output for us to measure
|
||||
|
||||
// Core 1 is just going to sit and drive samples out continously. PIO provides
|
||||
// consistent sample frequency.
|
||||
|
||||
#define OUTPUT_FREQ_KHZ 5
|
||||
#define SAMPLE_WIDTH 5
|
||||
// This is the green channel on the VGA board
|
||||
#define DAC_PIN_BASE 6
|
||||
|
||||
void core1_main() {
|
||||
PIO pio = pio0;
|
||||
uint sm = pio_claim_unused_sm(pio0, true);
|
||||
uint offset = pio_add_program(pio0, &resistor_dac_5bit_program);
|
||||
resistor_dac_5bit_program_init(pio0, sm, offset,
|
||||
OUTPUT_FREQ_KHZ * 1000 * 2 * (1 << SAMPLE_WIDTH), DAC_PIN_BASE);
|
||||
while (true) {
|
||||
// Triangle wave
|
||||
for (int i = 0; i < (1 << SAMPLE_WIDTH); ++i)
|
||||
pio_sm_put_blocking(pio, sm, i);
|
||||
for (int i = 0; i < (1 << SAMPLE_WIDTH); ++i)
|
||||
pio_sm_put_blocking(pio, sm, (1 << SAMPLE_WIDTH) - 1 - i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
;
|
||||
; Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program resistor_dac_5bit
|
||||
|
||||
; Drive one of the 5-bit resistor DACs on the VGA reference board. (this isn't
|
||||
; a good way to do VGA -- just want a nice sawtooth for the ADC example!)
|
||||
|
||||
out pins, 5
|
||||
|
||||
|
||||
|
||||
% c-sdk {
|
||||
#include "hardware/clocks.h"
|
||||
static inline void resistor_dac_5bit_program_init(PIO pio, uint sm, uint offset,
|
||||
uint sample_rate_hz, uint pin_base) {
|
||||
|
||||
pio_sm_set_pins_with_mask(pio, sm, 0, 0x1fu << pin_base);
|
||||
pio_sm_set_pindirs_with_mask(pio, sm, ~0u, 0x1fu << pin_base);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
pio_gpio_init(pio, pin_base + i);
|
||||
|
||||
pio_sm_config c = resistor_dac_5bit_program_get_default_config(offset);
|
||||
sm_config_set_out_pins(&c, pin_base, 5);
|
||||
// Shift to right, autopull threshold 5
|
||||
sm_config_set_out_shift(&c, true, true, 5);
|
||||
// Deeper FIFO as we're not doing any RX
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
float div = (float)clock_get_hz(clk_sys) / sample_rate_hz;
|
||||
sm_config_set_clkdiv(&c, div);
|
||||
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
@ -1,11 +0,0 @@
|
||||
add_executable(hello_adc
|
||||
hello_adc.c
|
||||
)
|
||||
|
||||
target_link_libraries(hello_adc pico_stdlib hardware_adc)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_adc)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_adc)
|
||||
@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
printf("ADC Example, measuring GPIO26\n");
|
||||
|
||||
adc_init();
|
||||
|
||||
// Make sure GPIO is high-impedance, no pullups etc
|
||||
adc_gpio_init(26);
|
||||
// Select ADC input 0 (GPIO26)
|
||||
adc_select_input(0);
|
||||
|
||||
while (1) {
|
||||
// 12-bit conversion, assume max value == ADC_VREF == 3.3 V
|
||||
const float conversion_factor = 3.3f / (1 << 12);
|
||||
uint16_t result = adc_read();
|
||||
printf("Raw value: 0x%03x, voltage: %f V\n", result, result * conversion_factor);
|
||||
sleep_ms(500);
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
add_executable(joystick_display
|
||||
joystick_display.c
|
||||
)
|
||||
|
||||
target_link_libraries(joystick_display pico_stdlib hardware_adc)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(joystick_display)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(joystick_display)
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
adc_init();
|
||||
// Make sure GPIO is high-impedance, no pullups etc
|
||||
adc_gpio_init(26);
|
||||
adc_gpio_init(27);
|
||||
|
||||
while (1) {
|
||||
adc_select_input(0);
|
||||
uint adc_x_raw = adc_read();
|
||||
adc_select_input(1);
|
||||
uint adc_y_raw = adc_read();
|
||||
|
||||
// Display the joystick position something like this:
|
||||
// X: [ o ] Y: [ o ]
|
||||
const uint bar_width = 40;
|
||||
const uint adc_max = (1 << 12) - 1;
|
||||
uint bar_x_pos = adc_x_raw * bar_width / adc_max;
|
||||
uint bar_y_pos = adc_y_raw * bar_width / adc_max;
|
||||
printf("\rX: [");
|
||||
for (int i = 0; i < bar_width; ++i)
|
||||
putchar( i == bar_x_pos ? 'o' : ' ');
|
||||
printf("] Y: [");
|
||||
for (int i = 0; i < bar_width; ++i)
|
||||
putchar( i == bar_y_pos ? 'o' : ' ');
|
||||
printf("]");
|
||||
sleep_ms(50);
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(microphone_adc
|
||||
microphone_adc.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and adc hardware support
|
||||
target_link_libraries(microphone_adc pico_stdlib hardware_adc)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(microphone_adc)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(microphone_adc)
|
||||
@ -1,48 +0,0 @@
|
||||
= Attaching a microphone using the ADC
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico with a standard analog microphone via the onboard analog to digital converter (ADC). In this example, we use an ICS-40180 breakout board by SparkFun but any analog microphone should be compatible with this tutorial. SparkFun have https://learn.sparkfun.com/tutorials/mems-microphone-hookup-guide[written a guide] for this board that goes into more detail about the board and how it works.
|
||||
|
||||
[TIP]
|
||||
======
|
||||
An analog to digital converter (ADC) is responsible for reading continually varying input signals that may range from 0 to a specified reference voltage (in the Pico's case this reference voltage is set by the supply voltage and can be measured on pin 35, ADC_VREF) and converting them into binary, i.e. a number that can be digitally stored.
|
||||
======
|
||||
|
||||
The Pico has a 12-bit ADC (ENOB of 8.7-bit, see https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf[RP2040 datasheet section 4.9.3 for more details]), meaning that a read operation will return a number ranging from 0 to 4095 (2^12 - 1) for a total of 4096 possible values. Therefore, the resolution of the ADC is 3.3/4096, so roughly steps of 0.8 millivolts. The SparkFun breakout uses an OPA344 operational amplifier to boost the signal coming from the microphone to voltage levels that can be easily read by the ADC. An important side effect is that a bias of 0.5*Vcc is added to the signal, even when the microphone is not picking up any sound.
|
||||
|
||||
The ADC provides us with a raw voltage value but when dealing with sound, we're more interested in the amplitude of the audio signal. This is defined as one half the peak-to-peak amplitude. Included with this example is a very simple Python script that will plot the voltage values it receives via the serial port. By tweaking the sampling rates, and various other parameters, the data from the microphone can be analysed in various ways, such as in a Fast Fourier Transform to see what frequencies make up the signal.
|
||||
|
||||
[[microphone_adc_plotter_image]]
|
||||
[pdfwidth=75%]
|
||||
.Example output from included Python script
|
||||
image::microphone_adc_plotter.png[]
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 3 jumpers, to connect VCC (3.3v), GND, and AOUT. The example here uses ADC0, which is GP26. Power is supplied from the 3.3V pin.
|
||||
|
||||
WARNING: Most boards will take a range of VCC voltages from the Pico's default 3.3V to the 5 volts commonly seen on other microcontrollers. Ensure your board doesn't output an analogue signal greater than 3.3V as this may result in permanent damage to the Pico's ADC.
|
||||
|
||||
[[ics-40180-adc_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for ICS-40180 microphone breakout board.
|
||||
image::microphone_adc_bb.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree.
|
||||
microphone_adc.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[ics-40180-adc-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| ICS-40180 microphone breakout board or similar | 1 | https://www.sparkfun.com/products/18011[From SparkFun]
|
||||
| M/M Jumper wires | 3 | generic part
|
||||
|===
|
||||
|
||||
|
||||
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
#include "hardware/adc.h"
|
||||
#include "hardware/uart.h"
|
||||
#include "pico/binary_info.h"
|
||||
|
||||
/* Example code to extract analog values from a microphone using the ADC
|
||||
with accompanying Python file to plot these values
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO 26/ADC0 (pin 31)-> AOUT or AUD on microphone board
|
||||
3.3v (pin 36) -> VCC on microphone board
|
||||
GND (pin 38) -> GND on microphone board
|
||||
*/
|
||||
|
||||
#define ADC_NUM 0
|
||||
#define ADC_PIN (26 + ADC_NUM)
|
||||
#define ADC_VREF 3.3
|
||||
#define ADC_RANGE (1 << 12)
|
||||
#define ADC_CONVERT (ADC_VREF / (ADC_RANGE - 1))
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
printf("Beep boop, listening...\n");
|
||||
|
||||
bi_decl(bi_program_description("Analog microphone example for Raspberry Pi Pico")); // for picotool
|
||||
bi_decl(bi_1pin_with_name(ADC_PIN, "ADC input pin"));
|
||||
|
||||
adc_init();
|
||||
adc_gpio_init( ADC_PIN);
|
||||
adc_select_input( ADC_NUM);
|
||||
|
||||
uint adc_raw;
|
||||
while (1) {
|
||||
adc_raw = adc_read(); // raw voltage from ADC
|
||||
printf("%.2f\n", adc_raw * ADC_CONVERT);
|
||||
sleep_ms(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 59 KiB |
@ -1,77 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Grabs raw data from the Pico's UART and plots it as received
|
||||
|
||||
# Install dependencies:
|
||||
# python3 -m pip install pyserial matplotlib
|
||||
|
||||
# Usage: python3 plotter <port>
|
||||
# eg. python3 plotter /dev/ttyACM0
|
||||
|
||||
# see matplotlib animation API for more: https://matplotlib.org/stable/api/animation_api.html
|
||||
|
||||
import serial
|
||||
import sys
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.animation as animation
|
||||
from matplotlib.lines import Line2D
|
||||
|
||||
# disable toolbar
|
||||
plt.rcParams['toolbar'] = 'None'
|
||||
|
||||
class Plotter:
|
||||
def __init__(self, ax):
|
||||
self.ax = ax
|
||||
self.maxt = 250
|
||||
self.tdata = [0]
|
||||
self.ydata = [3.3/2]
|
||||
self.line = Line2D(self.tdata, self.ydata)
|
||||
|
||||
self.ax.add_line(self.line)
|
||||
self.ax.set_ylim(0, 3.3)
|
||||
self.ax.set_xlim(0, self.maxt)
|
||||
|
||||
def update(self, y):
|
||||
lastt = self.tdata[-1]
|
||||
if lastt - self.tdata[0] >= self.maxt: # drop old frames
|
||||
self.tdata = self.tdata[1:]
|
||||
self.ydata = self.ydata[1:]
|
||||
self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt)
|
||||
|
||||
t = lastt + 1
|
||||
self.tdata.append(t)
|
||||
self.ydata.append(y)
|
||||
self.line.set_data(self.tdata, self.ydata)
|
||||
return self.line,
|
||||
|
||||
|
||||
def serial_getter():
|
||||
# grab fresh ADC values
|
||||
# note sometimes UART drops chars so we try a max of 5 times
|
||||
# to get proper data
|
||||
while True:
|
||||
for i in range(5):
|
||||
line = ser.readline()
|
||||
try:
|
||||
line = float(line)
|
||||
except ValueError:
|
||||
continue
|
||||
break
|
||||
yield line
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
raise Exception("Ruh roh..no port specified!")
|
||||
|
||||
ser = serial.Serial(sys.argv[1], 115200, timeout=1)
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
plotter = Plotter(ax)
|
||||
|
||||
ani = animation.FuncAnimation(fig, plotter.update, serial_getter, interval=1,
|
||||
blit=True, cache_frame_data=False)
|
||||
|
||||
ax.set_xlabel("Samples")
|
||||
ax.set_ylabel("Voltage (V)")
|
||||
fig.canvas.manager.set_window_title('Microphone ADC example')
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
@ -1,14 +0,0 @@
|
||||
add_executable(onboard_temperature onboard_temperature.c)
|
||||
|
||||
target_link_libraries(onboard_temperature pico_stdlib hardware_adc)
|
||||
|
||||
# enable uart output, disable usb output
|
||||
pico_enable_stdio_uart(onboard_temperature 1)
|
||||
pico_enable_stdio_usb(onboard_temperature 0)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(onboard_temperature)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(onboard_temperature)
|
||||
|
||||
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/adc.h"
|
||||
|
||||
/* Choose 'C' for Celsius or 'F' for Fahrenheit. */
|
||||
#define TEMPERATURE_UNITS 'C'
|
||||
|
||||
/* References for this implementation:
|
||||
* raspberry-pi-pico-c-sdk.pdf, Section '4.1.1. hardware_adc'
|
||||
* pico-examples/adc/adc_console/adc_console.c */
|
||||
float read_onboard_temperature(const char unit) {
|
||||
|
||||
/* 12-bit conversion, assume max value == ADC_VREF == 3.3 V */
|
||||
const float conversionFactor = 3.3f / (1 << 12);
|
||||
|
||||
float adc = (float)adc_read() * conversionFactor;
|
||||
float tempC = 27.0f - (adc - 0.706f) / 0.001721f;
|
||||
|
||||
if (unit == 'C') {
|
||||
return tempC;
|
||||
} else if (unit == 'F') {
|
||||
return tempC * 9 / 5 + 32;
|
||||
}
|
||||
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
|
||||
/* Initialize hardware AD converter, enable onboard temperature sensor and
|
||||
* select its channel (do this once for efficiency, but beware that this
|
||||
* is a global operation). */
|
||||
adc_init();
|
||||
adc_set_temp_sensor_enabled(true);
|
||||
adc_select_input(4);
|
||||
|
||||
while (true) {
|
||||
float temperature = read_onboard_temperature(TEMPERATURE_UNITS);
|
||||
printf("Onboard temperature = %.02f %c\n", temperature, TEMPERATURE_UNITS);
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 1);
|
||||
sleep_ms(10);
|
||||
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 0);
|
||||
#endif
|
||||
sleep_ms(990);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -7,6 +7,3 @@ target_link_libraries(blink pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(blink)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(blink)
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(detached_clk_peri)
|
||||
add_subdirectory(hello_48MHz)
|
||||
add_subdirectory(hello_gpout)
|
||||
add_subdirectory(hello_resus)
|
||||
endif ()
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(clocks_detached_clk_peri
|
||||
detached_clk_peri.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(clocks_detached_clk_peri pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(clocks_detached_clk_peri)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(clocks_detached_clk_peri)
|
||||
@ -1,86 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// By default, clk_peri (which drives the serial parts of SPI and UART) is
|
||||
// attached directly to clk_sys, so varies if the system clock is scaled up
|
||||
// and down. clk_peri has a multiplexer (though no divider) which allows it to
|
||||
// be attached to other sources.
|
||||
//
|
||||
// If set_sys_clock_khz is called (here setting the system clock to 133 MHz),
|
||||
// this automatically attaches clk_peri to the USB PLL, which by default we run
|
||||
// at 48 MHz. As long as you call this *before* configuring your UART etc, the
|
||||
// UART baud rate will then not be affected by subsequent changes to clk_sys.
|
||||
//
|
||||
// However, dropping clk_peri to 48 MHz limits the maximum serial frequencies
|
||||
// that can be attained by UART and particularly SPI. This example shows how
|
||||
// clk_peri can be attached directly to the system PLL, and the clk_sys
|
||||
// divider can then be varied to scale the system (CPUs, DMA, bus fabric etc)
|
||||
// frequency up and down whilst keeping clk_peri at a constant 133 MHz.
|
||||
//
|
||||
// The complete list of clock sources available on clk_peri can be found in
|
||||
// hardware/regs/clocks.h:
|
||||
//
|
||||
// Field : CLOCKS_CLK_PERI_CTRL_AUXSRC
|
||||
// 0x0 -> clk_sys
|
||||
// 0x1 -> clksrc_pll_sys
|
||||
// 0x2 -> clksrc_pll_usb
|
||||
// 0x3 -> rosc_clksrc_ph
|
||||
// 0x4 -> xosc_clksrc
|
||||
// 0x5 -> clksrc_gpin0
|
||||
// 0x6 -> clksrc_gpin1
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
#define PLL_SYS_KHZ (133 * 1000)
|
||||
|
||||
int main() {
|
||||
// Set the system frequency to 133 MHz. vco_calc.py from the SDK tells us
|
||||
// this is exactly attainable at the PLL from a 12 MHz crystal: FBDIV =
|
||||
// 133 (so VCO of 1596 MHz), PD1 = 6, PD2 = 2. This function will set the
|
||||
// system PLL to 133 MHz and set the clk_sys divisor to 1.
|
||||
set_sys_clock_khz(PLL_SYS_KHZ, true);
|
||||
|
||||
// The previous line automatically detached clk_peri from clk_sys, and
|
||||
// attached it to pll_usb, so that clk_peri won't be disturbed by future
|
||||
// changes to system clock or system PLL. If we need higher clk_peri
|
||||
// frequencies, we can attach clk_peri directly back to system PLL (no
|
||||
// divider available) and then use the clk_sys divider to scale clk_sys
|
||||
// independently of clk_peri.
|
||||
clock_configure(
|
||||
clk_peri,
|
||||
0, // No glitchless mux
|
||||
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS, // System PLL on AUX mux
|
||||
PLL_SYS_KHZ * 1000, // Input frequency
|
||||
PLL_SYS_KHZ * 1000 // Output (must be same as no divider)
|
||||
);
|
||||
|
||||
// The serial clock won't vary from this point onward, so we can configure
|
||||
// the UART etc.
|
||||
stdio_init_all();
|
||||
|
||||
puts("Peripheral clock is attached directly to system PLL.");
|
||||
puts("We can vary the system clock divisor while printing from the UART:");
|
||||
|
||||
for (uint div = 1; div <= 10; ++div) {
|
||||
printf("Setting system clock divisor to %u\n", div);
|
||||
clock_configure(
|
||||
clk_sys,
|
||||
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
|
||||
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
|
||||
PLL_SYS_KHZ,
|
||||
PLL_SYS_KHZ / div
|
||||
);
|
||||
printf("Measuring system clock with frequency counter:");
|
||||
// Note that the numbering of frequency counter sources is not the
|
||||
// same as the numbering of clock slice register blocks. (If we passed
|
||||
// the clk_sys enum here we would actually end up measuring XOSC.)
|
||||
printf("%u kHz\n", frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(hello_48MHz
|
||||
hello_48MHz.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional clocks hardware support
|
||||
target_link_libraries(hello_48MHz pico_stdlib hardware_clocks)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_48MHz)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_48MHz)
|
||||
@ -1,68 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/pll.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/structs/pll.h"
|
||||
#include "hardware/structs/clocks.h"
|
||||
|
||||
void measure_freqs(void) {
|
||||
uint f_pll_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_SYS_CLKSRC_PRIMARY);
|
||||
uint f_pll_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_PLL_USB_CLKSRC_PRIMARY);
|
||||
uint f_rosc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_ROSC_CLKSRC);
|
||||
uint f_clk_sys = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_SYS);
|
||||
uint f_clk_peri = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_PERI);
|
||||
uint f_clk_usb = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_USB);
|
||||
uint f_clk_adc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_ADC);
|
||||
uint f_clk_rtc = frequency_count_khz(CLOCKS_FC0_SRC_VALUE_CLK_RTC);
|
||||
|
||||
printf("pll_sys = %dkHz\n", f_pll_sys);
|
||||
printf("pll_usb = %dkHz\n", f_pll_usb);
|
||||
printf("rosc = %dkHz\n", f_rosc);
|
||||
printf("clk_sys = %dkHz\n", f_clk_sys);
|
||||
printf("clk_peri = %dkHz\n", f_clk_peri);
|
||||
printf("clk_usb = %dkHz\n", f_clk_usb);
|
||||
printf("clk_adc = %dkHz\n", f_clk_adc);
|
||||
printf("clk_rtc = %dkHz\n", f_clk_rtc);
|
||||
|
||||
// Can't measure clk_ref / xosc as it is the ref
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
printf("Hello, world!\n");
|
||||
|
||||
measure_freqs();
|
||||
|
||||
// Change clk_sys to be 48MHz. The simplest way is to take this from PLL_USB
|
||||
// which has a source frequency of 48MHz
|
||||
clock_configure(clk_sys,
|
||||
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
|
||||
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
|
||||
48 * MHZ,
|
||||
48 * MHZ);
|
||||
|
||||
// Turn off PLL sys for good measure
|
||||
pll_deinit(pll_sys);
|
||||
|
||||
// CLK peri is clocked from clk_sys so need to change clk_peri's freq
|
||||
clock_configure(clk_peri,
|
||||
0,
|
||||
CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS,
|
||||
48 * MHZ,
|
||||
48 * MHZ);
|
||||
|
||||
// Re init uart now that clk_peri has changed
|
||||
stdio_init_all();
|
||||
|
||||
measure_freqs();
|
||||
printf("Hello, 48MHz");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(hello_gpout
|
||||
hello_gpout.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_gpout pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_gpout)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_gpout)
|
||||
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/clocks.h"
|
||||
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
printf("Hello gpout\n");
|
||||
|
||||
// Output clk_sys / 10 to gpio 21, etc...
|
||||
clock_gpio_init(21, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_SYS, 10);
|
||||
clock_gpio_init(23, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_USB, 10);
|
||||
clock_gpio_init(24, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_ADC, 10);
|
||||
clock_gpio_init(26, CLOCKS_CLK_GPOUT0_CTRL_AUXSRC_VALUE_CLK_RTC, 10);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(hello_resus
|
||||
hello_resus.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_resus pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_resus)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_resus)
|
||||
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/pll.h"
|
||||
|
||||
volatile bool seen_resus;
|
||||
|
||||
void resus_callback(void) {
|
||||
// Reconfigure PLL sys back to the default state of 1500 / 6 / 2 = 125MHz
|
||||
pll_init(pll_sys, 1, 1500 * MHZ, 6, 2);
|
||||
|
||||
// CLK SYS = PLL SYS (125MHz) / 1 = 125MHz
|
||||
clock_configure(clk_sys,
|
||||
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
|
||||
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_SYS,
|
||||
125 * MHZ,
|
||||
125 * MHZ);
|
||||
|
||||
// Reconfigure uart as clocks have changed
|
||||
stdio_init_all();
|
||||
printf("Resus event fired\n");
|
||||
|
||||
// Wait for uart output to finish
|
||||
uart_default_tx_wait_blocking();
|
||||
|
||||
seen_resus = true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
printf("Hello resus\n");
|
||||
|
||||
seen_resus = false;
|
||||
|
||||
clocks_enable_resus(&resus_callback);
|
||||
// Break PLL sys
|
||||
pll_deinit(pll_sys);
|
||||
|
||||
while(!seen_resus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(hello_divider
|
||||
hello_divider.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_divider pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_divider)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_divider)
|
||||
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/divider.h"
|
||||
|
||||
/// \tag::hello_divider[]
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
printf("Hello, divider!\n");
|
||||
|
||||
// This is the basic hardware divider function
|
||||
int32_t dividend = 123456;
|
||||
int32_t divisor = -321;
|
||||
divmod_result_t result = hw_divider_divmod_s32(dividend, divisor);
|
||||
|
||||
printf("%d/%d = %d remainder %d\n", dividend, divisor, to_quotient_s32(result), to_remainder_s32(result));
|
||||
|
||||
// Is it right?
|
||||
|
||||
printf("Working backwards! Result %d should equal %d!\n\n",
|
||||
to_quotient_s32(result) * divisor + to_remainder_s32(result), dividend);
|
||||
|
||||
// This is the recommended unsigned fast divider for general use.
|
||||
int32_t udividend = 123456;
|
||||
int32_t udivisor = 321;
|
||||
divmod_result_t uresult = hw_divider_divmod_u32(udividend, udivisor);
|
||||
|
||||
printf("%d/%d = %d remainder %d\n", udividend, udivisor, to_quotient_u32(uresult), to_remainder_u32(uresult));
|
||||
|
||||
// Is it right?
|
||||
|
||||
printf("Working backwards! Result %d should equal %d!\n\n",
|
||||
to_quotient_u32(result) * divisor + to_remainder_u32(result), dividend);
|
||||
|
||||
// You can also do divides asynchronously. Divides will be complete after 8 cyles.
|
||||
|
||||
hw_divider_divmod_s32_start(dividend, divisor);
|
||||
|
||||
// Do something for 8 cycles!
|
||||
|
||||
// In this example, our results function will wait for completion.
|
||||
// Use hw_divider_result_nowait() if you don't want to wait, but are sure you have delayed at least 8 cycles
|
||||
|
||||
result = hw_divider_result_wait();
|
||||
|
||||
printf("Async result %d/%d = %d remainder %d\n", dividend, divisor, to_quotient_s32(result),
|
||||
to_remainder_s32(result));
|
||||
|
||||
// For a really fast divide, you can use the inlined versions... the / involves a function call as / always does
|
||||
// when using the ARM AEABI, so if you really want the best performance use the inlined versions.
|
||||
// Note that the / operator function DOES use the hardware divider by default, although you can change
|
||||
// that behavior by calling pico_set_divider_implementation in the cmake build for your target.
|
||||
printf("%d / %d = (by operator %d) (inlined %d)\n", dividend, divisor,
|
||||
dividend / divisor, hw_divider_s32_quotient_inlined(dividend, divisor));
|
||||
|
||||
// Note however you must manually save/restore the divider state if you call the inlined methods from within an IRQ
|
||||
// handler.
|
||||
hw_divider_state_t state;
|
||||
hw_divider_divmod_s32_start(dividend, divisor);
|
||||
hw_divider_save_state(&state);
|
||||
|
||||
hw_divider_divmod_s32_start(123, 7);
|
||||
printf("inner %d / %d = %d\n", 123, 7, hw_divider_s32_quotient_wait());
|
||||
|
||||
hw_divider_restore_state(&state);
|
||||
int32_t tmp = hw_divider_s32_quotient_wait();
|
||||
printf("outer divide %d / %d = %d\n", dividend, divisor, tmp);
|
||||
return 0;
|
||||
}
|
||||
/// \end::hello_divider[]
|
||||
@ -1,5 +0,0 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(channel_irq)
|
||||
add_subdirectory(control_blocks)
|
||||
add_subdirectory(hello_dma)
|
||||
endif ()
|
||||
@ -1,18 +0,0 @@
|
||||
add_executable(dma_channel_irq
|
||||
channel_irq.c
|
||||
)
|
||||
|
||||
pico_generate_pio_header(dma_channel_irq ${CMAKE_CURRENT_LIST_DIR}/pio_serialiser.pio)
|
||||
|
||||
target_link_libraries(dma_channel_irq
|
||||
pico_stdlib
|
||||
hardware_dma
|
||||
hardware_irq
|
||||
hardware_pio
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(dma_channel_irq)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(dma_channel_irq)
|
||||
@ -1,94 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Show how to reconfigure and restart a channel in a channel completion
|
||||
// interrupt handler.
|
||||
//
|
||||
// Our DMA channel will transfer data to a PIO state machine, which is
|
||||
// configured to serialise the raw bits that we push, one by one. We're going
|
||||
// to use this to do some crude LED PWM by repeatedly sending values with the
|
||||
// right balance of 1s and 0s. (note there are better ways to do PWM with PIO
|
||||
// -- see the PIO PWM example).
|
||||
//
|
||||
// Once the channel has sent a predetermined amount of data, it will halt, and
|
||||
// raise an interrupt flag. The processor will enter the interrupt handler in
|
||||
// response to this, where it will reconfigure and restart the channel. This
|
||||
// repeats.
|
||||
|
||||
#include <stdio.h>
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "pio_serialiser.pio.h"
|
||||
|
||||
// PIO sends one bit per 10 system clock cycles. DMA sends the same 32-bit
|
||||
// value 10 000 times before halting. This means we cycle through the 32 PWM
|
||||
// levels roughly once per second.
|
||||
#define PIO_SERIAL_CLKDIV 10.f
|
||||
#define PWM_REPEAT_COUNT 10000
|
||||
#define N_PWM_LEVELS 32
|
||||
|
||||
int dma_chan;
|
||||
|
||||
void dma_handler() {
|
||||
static int pwm_level = 0;
|
||||
static uint32_t wavetable[N_PWM_LEVELS];
|
||||
static bool first_run = true;
|
||||
// Entry number `i` has `i` one bits and `(32 - i)` zero bits.
|
||||
if (first_run) {
|
||||
first_run = false;
|
||||
for (int i = 0; i < N_PWM_LEVELS; ++i)
|
||||
wavetable[i] = ~(~0u << i);
|
||||
}
|
||||
|
||||
// Clear the interrupt request.
|
||||
dma_hw->ints0 = 1u << dma_chan;
|
||||
// Give the channel a new wave table entry to read from, and re-trigger it
|
||||
dma_channel_set_read_addr(dma_chan, &wavetable[pwm_level], true);
|
||||
|
||||
pwm_level = (pwm_level + 1) % N_PWM_LEVELS;
|
||||
}
|
||||
|
||||
int main() {
|
||||
#ifndef PICO_DEFAULT_LED_PIN
|
||||
#warning dma/channel_irq example requires a board with a regular LED
|
||||
#else
|
||||
// Set up a PIO state machine to serialise our bits
|
||||
uint offset = pio_add_program(pio0, &pio_serialiser_program);
|
||||
pio_serialiser_program_init(pio0, 0, offset, PICO_DEFAULT_LED_PIN, PIO_SERIAL_CLKDIV);
|
||||
|
||||
// Configure a channel to write the same word (32 bits) repeatedly to PIO0
|
||||
// SM0's TX FIFO, paced by the data request signal from that peripheral.
|
||||
dma_chan = dma_claim_unused_channel(true);
|
||||
dma_channel_config c = dma_channel_get_default_config(dma_chan);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&c, false);
|
||||
channel_config_set_dreq(&c, DREQ_PIO0_TX0);
|
||||
|
||||
dma_channel_configure(
|
||||
dma_chan,
|
||||
&c,
|
||||
&pio0_hw->txf[0], // Write address (only need to set this once)
|
||||
NULL, // Don't provide a read address yet
|
||||
PWM_REPEAT_COUNT, // Write the same value many times, then halt and interrupt
|
||||
false // Don't start yet
|
||||
);
|
||||
|
||||
// Tell the DMA to raise IRQ line 0 when the channel finishes a block
|
||||
dma_channel_set_irq0_enabled(dma_chan, true);
|
||||
|
||||
// Configure the processor to run dma_handler() when DMA IRQ 0 is asserted
|
||||
irq_set_exclusive_handler(DMA_IRQ_0, dma_handler);
|
||||
irq_set_enabled(DMA_IRQ_0, true);
|
||||
|
||||
// Manually call the handler once, to trigger the first transfer
|
||||
dma_handler();
|
||||
|
||||
// Everything else from this point is interrupt-driven. The processor has
|
||||
// time to sit and think about its early retirement -- maybe open a bakery?
|
||||
while (true)
|
||||
tight_loop_contents();
|
||||
#endif
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
;
|
||||
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
;
|
||||
; SPDX-License-Identifier: BSD-3-Clause
|
||||
;
|
||||
|
||||
.program pio_serialiser
|
||||
|
||||
; Just serialise a stream of bits. Take 32 bits from each FIFO record. LSB-first.
|
||||
|
||||
.wrap_target
|
||||
out pins, 1
|
||||
.wrap
|
||||
|
||||
% c-sdk {
|
||||
static inline void pio_serialiser_program_init(PIO pio, uint sm, uint offset, uint data_pin, float clk_div) {
|
||||
pio_gpio_init(pio, data_pin);
|
||||
pio_sm_set_consecutive_pindirs(pio, sm, data_pin, 1, true);
|
||||
pio_sm_config c = pio_serialiser_program_get_default_config(offset);
|
||||
sm_config_set_out_pins(&c, data_pin, 1);
|
||||
sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX);
|
||||
sm_config_set_clkdiv(&c, clk_div);
|
||||
sm_config_set_out_shift(&c, true, true, 32);
|
||||
pio_sm_init(pio, sm, offset, &c);
|
||||
pio_sm_set_enabled(pio, sm, true);
|
||||
}
|
||||
%}
|
||||
@ -1,11 +0,0 @@
|
||||
add_executable(dma_control_blocks
|
||||
control_blocks.c
|
||||
)
|
||||
|
||||
target_link_libraries(dma_control_blocks pico_stdlib hardware_dma)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(dma_control_blocks)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(dma_control_blocks)
|
||||
@ -1,115 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Use two DMA channels to make a programmed sequence of data transfers to the
|
||||
// UART (a data gather operation). One channel is responsible for transferring
|
||||
// the actual data, the other repeatedly reprograms that channel.
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/structs/uart.h"
|
||||
|
||||
// These buffers will be DMA'd to the UART, one after the other.
|
||||
|
||||
const char word0[] = "Transferring ";
|
||||
const char word1[] = "one ";
|
||||
const char word2[] = "word ";
|
||||
const char word3[] = "at ";
|
||||
const char word4[] = "a ";
|
||||
const char word5[] = "time.\n";
|
||||
|
||||
// Note the order of the fields here: it's important that the length is before
|
||||
// the read address, because the control channel is going to write to the last
|
||||
// two registers in alias 3 on the data channel:
|
||||
// +0x0 +0x4 +0x8 +0xC (Trigger)
|
||||
// Alias 0: READ_ADDR WRITE_ADDR TRANS_COUNT CTRL
|
||||
// Alias 1: CTRL READ_ADDR WRITE_ADDR TRANS_COUNT
|
||||
// Alias 2: CTRL TRANS_COUNT READ_ADDR WRITE_ADDR
|
||||
// Alias 3: CTRL WRITE_ADDR TRANS_COUNT READ_ADDR
|
||||
//
|
||||
// This will program the transfer count and read address of the data channel,
|
||||
// and trigger it. Once the data channel completes, it will restart the
|
||||
// control channel (via CHAIN_TO) to load the next two words into its control
|
||||
// registers.
|
||||
|
||||
const struct {uint32_t len; const char *data;} control_blocks[] = {
|
||||
{count_of(word0) - 1, word0}, // Skip null terminator
|
||||
{count_of(word1) - 1, word1},
|
||||
{count_of(word2) - 1, word2},
|
||||
{count_of(word3) - 1, word3},
|
||||
{count_of(word4) - 1, word4},
|
||||
{count_of(word5) - 1, word5},
|
||||
{0, NULL} // Null trigger to end chain.
|
||||
};
|
||||
|
||||
int main() {
|
||||
#ifndef uart_default
|
||||
#warning dma/control_blocks example requires a UART
|
||||
#else
|
||||
stdio_init_all();
|
||||
puts("DMA control block example:");
|
||||
|
||||
// ctrl_chan loads control blocks into data_chan, which executes them.
|
||||
int ctrl_chan = dma_claim_unused_channel(true);
|
||||
int data_chan = dma_claim_unused_channel(true);
|
||||
|
||||
// The control channel transfers two words into the data channel's control
|
||||
// registers, then halts. The write address wraps on a two-word
|
||||
// (eight-byte) boundary, so that the control channel writes the same two
|
||||
// registers when it is next triggered.
|
||||
|
||||
dma_channel_config c = dma_channel_get_default_config(ctrl_chan);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_32);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
channel_config_set_ring(&c, true, 3); // 1 << 3 byte boundary on write ptr
|
||||
|
||||
dma_channel_configure(
|
||||
ctrl_chan,
|
||||
&c,
|
||||
&dma_hw->ch[data_chan].al3_transfer_count, // Initial write address
|
||||
&control_blocks[0], // Initial read address
|
||||
2, // Halt after each control block
|
||||
false // Don't start yet
|
||||
);
|
||||
|
||||
// The data channel is set up to write to the UART FIFO (paced by the
|
||||
// UART's TX data request signal) and then chain to the control channel
|
||||
// once it completes. The control channel programs a new read address and
|
||||
// data length, and retriggers the data channel.
|
||||
|
||||
c = dma_channel_get_default_config(data_chan);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_dreq(&c, uart_get_dreq(uart_default, true));
|
||||
// Trigger ctrl_chan when data_chan completes
|
||||
channel_config_set_chain_to(&c, ctrl_chan);
|
||||
// Raise the IRQ flag when 0 is written to a trigger register (end of chain):
|
||||
channel_config_set_irq_quiet(&c, true);
|
||||
|
||||
dma_channel_configure(
|
||||
data_chan,
|
||||
&c,
|
||||
&uart_get_hw(uart_default)->dr,
|
||||
NULL, // Initial read address and transfer count are unimportant;
|
||||
0, // the control channel will reprogram them each time.
|
||||
false // Don't start yet.
|
||||
);
|
||||
|
||||
// Everything is ready to go. Tell the control channel to load the first
|
||||
// control block. Everything is automatic from here.
|
||||
dma_start_channel_mask(1u << ctrl_chan);
|
||||
|
||||
// The data channel will assert its IRQ flag when it gets a null trigger,
|
||||
// indicating the end of the control block list. We're just going to wait
|
||||
// for the IRQ flag instead of setting up an interrupt handler.
|
||||
while (!(dma_hw->intr & 1u << data_chan))
|
||||
tight_loop_contents();
|
||||
dma_hw->ints0 = 1u << data_chan;
|
||||
|
||||
puts("DMA finished.");
|
||||
#endif
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
add_executable(hello_dma
|
||||
hello_dma.c
|
||||
)
|
||||
|
||||
target_link_libraries(hello_dma pico_stdlib hardware_dma)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_dma)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_dma)
|
||||
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Use the DMA to copy data between two buffers in memory.
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/dma.h"
|
||||
|
||||
// Data will be copied from src to dst
|
||||
const char src[] = "Hello, world! (from DMA)";
|
||||
char dst[count_of(src)];
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
// Get a free channel, panic() if there are none
|
||||
int chan = dma_claim_unused_channel(true);
|
||||
|
||||
// 8 bit transfers. Both read and write address increment after each
|
||||
// transfer (each pointing to a location in src or dst respectively).
|
||||
// No DREQ is selected, so the DMA transfers as fast as it can.
|
||||
|
||||
dma_channel_config c = dma_channel_get_default_config(chan);
|
||||
channel_config_set_transfer_data_size(&c, DMA_SIZE_8);
|
||||
channel_config_set_read_increment(&c, true);
|
||||
channel_config_set_write_increment(&c, true);
|
||||
|
||||
dma_channel_configure(
|
||||
chan, // Channel to be configured
|
||||
&c, // The configuration we just created
|
||||
dst, // The initial write address
|
||||
src, // The initial read address
|
||||
count_of(src), // Number of transfers; in this case each is 1 byte.
|
||||
true // Start immediately.
|
||||
);
|
||||
|
||||
// We could choose to go and do something else whilst the DMA is doing its
|
||||
// thing. In this case the processor has nothing else to do, so we just
|
||||
// wait for the DMA to finish.
|
||||
dma_channel_wait_for_finish_blocking(chan);
|
||||
|
||||
// The DMA has now copied our text from the transmit buffer (src) to the
|
||||
// receive buffer (dst), so we can print it out from there.
|
||||
puts(dst);
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
set(PICO_EXAMPLE_URL_BASE "https://github.com/raspberrypi/pico-examples/tree/HEAD")
|
||||
macro(example_auto_set_url TARGET)
|
||||
file(RELATIVE_PATH URL_REL_PATH "${PICO_EXAMPLES_PATH}" "${CMAKE_CURRENT_LIST_DIR}")
|
||||
pico_set_program_url(${TARGET} "${PICO_EXAMPLE_URL_BASE}/${URL_REL_PATH}")
|
||||
endmacro()
|
||||
@ -1,7 +0,0 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(cache_perfctr)
|
||||
add_subdirectory(nuke)
|
||||
add_subdirectory(program)
|
||||
add_subdirectory(ssi_dma)
|
||||
add_subdirectory(xip_stream)
|
||||
endif ()
|
||||
@ -1,13 +0,0 @@
|
||||
add_executable(flash_cache_perfctr
|
||||
flash_cache_perfctr.c
|
||||
)
|
||||
|
||||
target_link_libraries(flash_cache_perfctr
|
||||
pico_stdlib
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(flash_cache_perfctr)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(flash_cache_perfctr)
|
||||
@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/structs/xip_ctrl.h"
|
||||
|
||||
// Example of using cache hit/access counters, and showing the effect of
|
||||
// invalidate on cache miss/hit.
|
||||
|
||||
|
||||
const uint32_t test_data[8] = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
|
||||
void __no_inline_not_in_flash_func(check)(bool cond, const char *msg) {
|
||||
if (!cond) {
|
||||
puts(msg);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void __no_inline_not_in_flash_func(check_hit_miss_invalidate)() {
|
||||
io_rw_32 *test_data_ptr = (io_rw_32 *) test_data;
|
||||
|
||||
/// \tag::check_hit_miss_invalidate[]
|
||||
// Flush cache to make sure we miss the first time we access test_data
|
||||
xip_ctrl_hw->flush = 1;
|
||||
while (!(xip_ctrl_hw->stat & XIP_STAT_FLUSH_READY_BITS))
|
||||
tight_loop_contents();
|
||||
|
||||
// Clear counters (write any value to clear)
|
||||
xip_ctrl_hw->ctr_acc = 1;
|
||||
xip_ctrl_hw->ctr_hit = 1;
|
||||
|
||||
(void) *test_data_ptr;
|
||||
check(xip_ctrl_hw->ctr_hit == 0 && xip_ctrl_hw->ctr_acc == 1,
|
||||
"First access to data should miss");
|
||||
|
||||
(void) *test_data_ptr;
|
||||
check(xip_ctrl_hw->ctr_hit == 1 && xip_ctrl_hw->ctr_acc == 2,
|
||||
"Second access to data should hit");
|
||||
|
||||
// Write to invalidate individual cache lines (64 bits)
|
||||
// Writes must be directed to the cacheable, allocatable alias (address 0x10.._....)
|
||||
*test_data_ptr = 0;
|
||||
(void) *test_data_ptr;
|
||||
check(xip_ctrl_hw->ctr_hit == 1 && xip_ctrl_hw->ctr_acc == 3,
|
||||
"Should miss after invalidation");
|
||||
(void) *test_data_ptr;
|
||||
check(xip_ctrl_hw->ctr_hit == 2 && xip_ctrl_hw->ctr_acc == 4,
|
||||
"Second access after invalidation should hit again");
|
||||
/// \end::check_hit_miss_invalidate[]
|
||||
}
|
||||
|
||||
// Some code which achieves a very high cache hit rate:
|
||||
int recursive_fibonacci(int n) {
|
||||
if (n <= 1)
|
||||
return 1;
|
||||
else
|
||||
return recursive_fibonacci(n - 1) + recursive_fibonacci(n - 2);
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
uint hit = xip_ctrl_hw->ctr_hit;
|
||||
uint access = xip_ctrl_hw->ctr_acc;
|
||||
|
||||
if (access == 0)
|
||||
printf("It looks like you're running this example from SRAM. This probably won't go well!\n");
|
||||
|
||||
// Note the hit rate will appear quite low at boot, as the .data,
|
||||
// .time_critical init in crt0 read a lot of read-once data from flash
|
||||
printf("At boot: %d hits, %d accesses\n", hit, access);
|
||||
printf("Hit rate so far: %.1f%%\n", hit * 100.f / access);
|
||||
|
||||
printf("Calculate 25th fibonacci number: %d\n", recursive_fibonacci(25));
|
||||
printf("New hit rate after printf and fibonacci: %.1f%%\n", xip_ctrl_hw->ctr_hit * 100.f / xip_ctrl_hw->ctr_acc);
|
||||
|
||||
check_hit_miss_invalidate();
|
||||
|
||||
printf("Hit/miss check passed\n");
|
||||
return 0;
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
add_executable(flash_nuke
|
||||
nuke.c
|
||||
)
|
||||
|
||||
target_link_libraries(flash_nuke
|
||||
pico_stdlib
|
||||
hardware_flash
|
||||
)
|
||||
|
||||
# It doesn't make sense to run this program from flash. Always build a
|
||||
# RAM-only binary.
|
||||
pico_set_binary_type(flash_nuke no_flash)
|
||||
|
||||
pico_add_extra_outputs(flash_nuke)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(flash_nuke)
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Obliterate the contents of flash. This is a silly thing to do if you are
|
||||
// trying to run this program from flash, so you should really load and run
|
||||
// directly from SRAM. You can enable RAM-only builds for all targets by doing:
|
||||
//
|
||||
// cmake -DPICO_NO_FLASH=1 ..
|
||||
//
|
||||
// in your build directory. We've also forced no-flash builds for this app in
|
||||
// particular by adding:
|
||||
//
|
||||
// pico_set_binary_type(flash_nuke no_flash)
|
||||
//
|
||||
// To the CMakeLists.txt app for this file. Just to be sure, we can check the
|
||||
// define:
|
||||
#if !PICO_NO_FLASH
|
||||
#error "This example must be built to run from SRAM!"
|
||||
#endif
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/flash.h"
|
||||
#include "pico/bootrom.h"
|
||||
|
||||
int main() {
|
||||
uint flash_size_bytes;
|
||||
#ifndef PICO_FLASH_SIZE_BYTES
|
||||
#warning PICO_FLASH_SIZE_BYTES not set, assuming 16M
|
||||
flash_size_bytes = 16 * 1024 * 1024;
|
||||
#else
|
||||
flash_size_bytes = PICO_FLASH_SIZE_BYTES;
|
||||
#endif
|
||||
flash_range_erase(0, flash_size_bytes);
|
||||
// Leave an eyecatcher pattern in the first page of flash so picotool can
|
||||
// more easily check the size:
|
||||
static const uint8_t eyecatcher[FLASH_PAGE_SIZE] = "NUKE";
|
||||
flash_range_program(0, eyecatcher, FLASH_PAGE_SIZE);
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
// Flash LED for success
|
||||
gpio_init(PICO_DEFAULT_LED_PIN);
|
||||
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 1);
|
||||
sleep_ms(100);
|
||||
gpio_put(PICO_DEFAULT_LED_PIN, 0);
|
||||
sleep_ms(100);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Pop back up as an MSD drive
|
||||
reset_usb_boot(0, 0);
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
add_executable(flash_program
|
||||
flash_program.c
|
||||
)
|
||||
|
||||
target_link_libraries(flash_program
|
||||
pico_stdlib
|
||||
hardware_flash
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(flash_program)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(flash_program)
|
||||
@ -1,58 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/flash.h"
|
||||
|
||||
// We're going to erase and reprogram a region 256k from the start of flash.
|
||||
// Once done, we can access this at XIP_BASE + 256k.
|
||||
#define FLASH_TARGET_OFFSET (256 * 1024)
|
||||
|
||||
const uint8_t *flash_target_contents = (const uint8_t *) (XIP_BASE + FLASH_TARGET_OFFSET);
|
||||
|
||||
void print_buf(const uint8_t *buf, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
printf("%02x", buf[i]);
|
||||
if (i % 16 == 15)
|
||||
printf("\n");
|
||||
else
|
||||
printf(" ");
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
uint8_t random_data[FLASH_PAGE_SIZE];
|
||||
for (int i = 0; i < FLASH_PAGE_SIZE; ++i)
|
||||
random_data[i] = rand() >> 16;
|
||||
|
||||
printf("Generated random data:\n");
|
||||
print_buf(random_data, FLASH_PAGE_SIZE);
|
||||
|
||||
// Note that a whole number of sectors must be erased at a time.
|
||||
printf("\nErasing target region...\n");
|
||||
flash_range_erase(FLASH_TARGET_OFFSET, FLASH_SECTOR_SIZE);
|
||||
printf("Done. Read back target region:\n");
|
||||
print_buf(flash_target_contents, FLASH_PAGE_SIZE);
|
||||
|
||||
printf("\nProgramming target region...\n");
|
||||
flash_range_program(FLASH_TARGET_OFFSET, random_data, FLASH_PAGE_SIZE);
|
||||
printf("Done. Read back target region:\n");
|
||||
print_buf(flash_target_contents, FLASH_PAGE_SIZE);
|
||||
|
||||
bool mismatch = false;
|
||||
for (int i = 0; i < FLASH_PAGE_SIZE; ++i) {
|
||||
if (random_data[i] != flash_target_contents[i])
|
||||
mismatch = true;
|
||||
}
|
||||
if (mismatch)
|
||||
printf("Programming failed!\n");
|
||||
else
|
||||
printf("Programming successful!\n");
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
add_executable(flash_ssi_dma
|
||||
flash_ssi_dma.c
|
||||
)
|
||||
|
||||
target_link_libraries(flash_ssi_dma
|
||||
pico_stdlib
|
||||
hardware_dma
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(flash_ssi_dma)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(flash_ssi_dma)
|
||||
@ -1,90 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/time.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/structs/ssi.h"
|
||||
|
||||
// This example DMAs 16kB of data from the start of flash to SRAM, and
|
||||
// measures the transfer speed.
|
||||
//
|
||||
// The SSI (flash interface) inside the XIP block has DREQ logic, so we can
|
||||
// DMA directly from its FIFOs. Unlike the XIP stream hardware (see
|
||||
// flash_xip_stream.c) this can *not* be done whilst code is running from
|
||||
// flash, without careful footwork like we do here. The tradeoff is that it's
|
||||
// ~2.5x as fast in QSPI mode, ~2x as fast in SPI mode.
|
||||
|
||||
void __no_inline_not_in_flash_func(flash_bulk_read)(uint32_t *rxbuf, uint32_t flash_offs, size_t len,
|
||||
uint dma_chan) {
|
||||
// SSI must be disabled to set transfer size. If software is executing
|
||||
// from flash right now then it's about to have a bad time
|
||||
ssi_hw->ssienr = 0;
|
||||
ssi_hw->ctrlr1 = len - 1; // NDF, number of data frames
|
||||
ssi_hw->dmacr = SSI_DMACR_TDMAE_BITS | SSI_DMACR_RDMAE_BITS;
|
||||
ssi_hw->ssienr = 1;
|
||||
// Other than NDF, the SSI configuration used for XIP is suitable for a bulk read too.
|
||||
|
||||
// Configure and start the DMA. Note we are avoiding the dma_*() functions
|
||||
// as we can't guarantee they'll be inlined
|
||||
dma_hw->ch[dma_chan].read_addr = (uint32_t) &ssi_hw->dr0;
|
||||
dma_hw->ch[dma_chan].write_addr = (uint32_t) rxbuf;
|
||||
dma_hw->ch[dma_chan].transfer_count = len;
|
||||
// Must enable DMA byteswap because non-XIP 32-bit flash transfers are
|
||||
// big-endian on SSI (we added a hardware tweak to make XIP sensible)
|
||||
dma_hw->ch[dma_chan].ctrl_trig =
|
||||
DMA_CH0_CTRL_TRIG_BSWAP_BITS |
|
||||
DREQ_XIP_SSIRX << DMA_CH0_CTRL_TRIG_TREQ_SEL_LSB |
|
||||
dma_chan << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB |
|
||||
DMA_CH0_CTRL_TRIG_INCR_WRITE_BITS |
|
||||
DMA_CH0_CTRL_TRIG_DATA_SIZE_VALUE_SIZE_WORD << DMA_CH0_CTRL_TRIG_DATA_SIZE_LSB |
|
||||
DMA_CH0_CTRL_TRIG_EN_BITS;
|
||||
|
||||
// Now DMA is waiting, kick off the SSI transfer (mode continuation bits in LSBs)
|
||||
ssi_hw->dr0 = (flash_offs << 8u) | 0xa0u;
|
||||
|
||||
// Wait for DMA finish
|
||||
while (dma_hw->ch[dma_chan].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS);
|
||||
|
||||
// Reconfigure SSI before we jump back into flash!
|
||||
ssi_hw->ssienr = 0;
|
||||
ssi_hw->ctrlr1 = 0; // Single 32-bit data frame per transfer
|
||||
ssi_hw->dmacr = 0;
|
||||
ssi_hw->ssienr = 1;
|
||||
}
|
||||
|
||||
#define DATA_SIZE_WORDS 4096
|
||||
|
||||
uint32_t rxdata[DATA_SIZE_WORDS];
|
||||
uint32_t *expect = (uint32_t *) XIP_NOCACHE_NOALLOC_BASE;
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
memset(rxdata, 0, DATA_SIZE_WORDS * sizeof(uint32_t));
|
||||
|
||||
printf("Starting DMA\n");
|
||||
uint32_t start_time = time_us_32();
|
||||
flash_bulk_read(rxdata, 0, DATA_SIZE_WORDS, 0);
|
||||
uint32_t finish_time = time_us_32();
|
||||
printf("DMA finished\n");
|
||||
|
||||
float elapsed_time_s = 1e-6f * (finish_time - start_time);
|
||||
printf("Transfer speed: %.3f MB/s\n", (sizeof(uint32_t) * DATA_SIZE_WORDS / 1e6f) / elapsed_time_s);
|
||||
|
||||
bool mismatch = false;
|
||||
for (int i = 0; i < DATA_SIZE_WORDS; ++i) {
|
||||
if (rxdata[i] != expect[i]) {
|
||||
printf("Mismatch at %d: expected %08x, got %08x\n", i, expect[i], rxdata[i]);
|
||||
mismatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mismatch)
|
||||
printf("Data check ok\n");
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
add_executable(flash_xip_stream
|
||||
flash_xip_stream.c
|
||||
)
|
||||
|
||||
target_link_libraries(flash_xip_stream
|
||||
pico_stdlib
|
||||
hardware_dma
|
||||
)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(flash_xip_stream)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(flash_xip_stream)
|
||||
@ -1,88 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/dma.h"
|
||||
#include "hardware/regs/addressmap.h"
|
||||
#include "hardware/structs/xip_ctrl.h"
|
||||
|
||||
#include "random_test_data.h"
|
||||
|
||||
// The XIP has some internal hardware that can stream a linear access sequence
|
||||
// to a DMAable FIFO, while the system is still doing random accesses on flash
|
||||
// code + data.
|
||||
|
||||
uint32_t buf[count_of(random_test_data)];
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
for (int i = 0; i < count_of(random_test_data); ++i)
|
||||
buf[i] = 0;
|
||||
|
||||
// This example won't work with PICO_NO_FLASH builds. Note that XIP stream
|
||||
// can be made to work in these cases, if you enable some XIP mode first
|
||||
// (e.g. via calling flash_enter_cmd_xip() in ROM). However, you will get
|
||||
// much better performance by DMAing directly from the SSI's FIFOs, as in
|
||||
// this way you can clock data continuously on the QSPI bus, rather than a
|
||||
// series of short transfers.
|
||||
if ((uint32_t) &random_test_data[0] >= SRAM_BASE) {
|
||||
printf("You need to run this example from flash!\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Transfer started by writing nonzero value to stream_ctr. stream_ctr
|
||||
// will count down as the transfer progresses. Can terminate early by
|
||||
// writing 0 to stream_ctr.
|
||||
// It's a good idea to drain the FIFO first!
|
||||
printf("Starting stream from %p\n", random_test_data);
|
||||
/// \tag::start_stream[]
|
||||
while (!(xip_ctrl_hw->stat & XIP_STAT_FIFO_EMPTY))
|
||||
(void) xip_ctrl_hw->stream_fifo;
|
||||
xip_ctrl_hw->stream_addr = (uint32_t) &random_test_data[0];
|
||||
xip_ctrl_hw->stream_ctr = count_of(random_test_data);
|
||||
/// \end::start_stream[]
|
||||
|
||||
// Start DMA transfer from XIP stream FIFO to our buffer in memory. Use
|
||||
// the auxiliary bus slave for the DMA<-FIFO accesses, to avoid stalling
|
||||
// the DMA against general XIP traffic. Doesn't really matter for this
|
||||
// example, but it can have a huge effect on DMA throughput.
|
||||
|
||||
printf("Starting DMA\n");
|
||||
/// \tag::start_dma[]
|
||||
const uint dma_chan = 0;
|
||||
dma_channel_config cfg = dma_channel_get_default_config(dma_chan);
|
||||
channel_config_set_read_increment(&cfg, false);
|
||||
channel_config_set_write_increment(&cfg, true);
|
||||
channel_config_set_dreq(&cfg, DREQ_XIP_STREAM);
|
||||
dma_channel_configure(
|
||||
dma_chan,
|
||||
&cfg,
|
||||
(void *) buf, // Write addr
|
||||
(const void *) XIP_AUX_BASE, // Read addr
|
||||
count_of(random_test_data), // Transfer count
|
||||
true // Start immediately!
|
||||
);
|
||||
/// \end::start_dma[]
|
||||
|
||||
dma_channel_wait_for_finish_blocking(dma_chan);
|
||||
|
||||
printf("DMA complete\n");
|
||||
|
||||
bool mismatch = false;
|
||||
for (int i = 0; i < count_of(random_test_data); ++i) {
|
||||
if (random_test_data[i] != buf[i]) {
|
||||
printf("Data mismatch: %08x (actual) != %08x (expected)\n", buf[i], random_test_data[i]);
|
||||
mismatch = true;
|
||||
break;
|
||||
}
|
||||
printf("%08x%c", buf[i], i % 8 == 7 ? '\n' : ' ');
|
||||
}
|
||||
if (!mismatch)
|
||||
printf("Data check OK\n");
|
||||
}
|
||||
@ -1,160 +0,0 @@
|
||||
const uint32_t random_test_data[] = {
|
||||
0x76654e22, 0x47207265, 0x616e6e6f,
|
||||
0x76694720, 0x6f592065, 0x70552075,
|
||||
0x570a0a22, 0x65722765, 0x206f6e20,
|
||||
0x61727473, 0x7265676e, 0x6f742073,
|
||||
0x766f6c20, 0x6f590a65, 0x6e6b2075,
|
||||
0x7420776f, 0x72206568, 0x73656c75,
|
||||
0x646e6120, 0x206f7320, 0x49206f64,
|
||||
0x6620410a, 0x206c6c75, 0x6d6d6f63,
|
||||
0x656d7469, 0x7327746e, 0x61687720,
|
||||
0x27492074, 0x6874206d, 0x696b6e69,
|
||||
0x6f20676e, 0x6f590a66, 0x6f772075,
|
||||
0x6e646c75, 0x67207427, 0x74207465,
|
||||
0x20736968, 0x6d6f7266, 0x796e6120,
|
||||
0x68746f20, 0x67207265, 0x0a0a7975,
|
||||
0x756a2049, 0x77207473, 0x616e6e61,
|
||||
0x6c657420, 0x6f79206c, 0x6f682075,
|
||||
0x27492077, 0x6566206d, 0x6e696c65,
|
||||
0x6f470a67, 0x20617474, 0x656b616d,
|
||||
0x756f7920, 0x646e7520, 0x74737265,
|
||||
0x0a646e61, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x76696720, 0x6f792065,
|
||||
0x70752075, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x74656c20, 0x756f7920,
|
||||
0x776f6420, 0x654e0a6e, 0x20726576,
|
||||
0x6e6e6f67, 0x75722061, 0x7261206e,
|
||||
0x646e756f, 0x646e6120, 0x73656420,
|
||||
0x20747265, 0x0a756f79, 0x6576654e,
|
||||
0x6f672072, 0x20616e6e, 0x656b616d,
|
||||
0x756f7920, 0x79726320, 0x76654e0a,
|
||||
0x67207265, 0x616e6e6f, 0x79617320,
|
||||
0x6f6f6720, 0x65796264, 0x76654e0a,
|
||||
0x67207265, 0x616e6e6f, 0x6c657420,
|
||||
0x2061206c, 0x2065696c, 0x20646e61,
|
||||
0x74727568, 0x756f7920, 0x65570a0a,
|
||||
0x20657627, 0x776f6e6b, 0x6165206e,
|
||||
0x6f206863, 0x72656874, 0x726f6620,
|
||||
0x206f7320, 0x676e6f6c, 0x756f590a,
|
||||
0x65682072, 0x27747261, 0x65622073,
|
||||
0x61206e65, 0x6e696863, 0x62202c67,
|
||||
0x590a7475, 0x7227756f, 0x6f742065,
|
||||
0x6873206f, 0x6f742079, 0x79617320,
|
||||
0x0a746920, 0x69736e49, 0x202c6564,
|
||||
0x62206577, 0x2068746f, 0x776f6e6b,
|
||||
0x61687720, 0x20732774, 0x6e656562,
|
||||
0x696f6720, 0x6f20676e, 0x65570a6e,
|
||||
0x6f6e6b20, 0x68742077, 0x61672065,
|
||||
0x6120656d, 0x7720646e, 0x65722765,
|
||||
0x6e6f6720, 0x7020616e, 0x2079616c,
|
||||
0x0a0a7469, 0x20646e41, 0x79206669,
|
||||
0x6120756f, 0x6d206b73, 0x6f682065,
|
||||
0x27492077, 0x6566206d, 0x6e696c65,
|
||||
0x6f440a67, 0x2074276e, 0x6c6c6574,
|
||||
0x20656d20, 0x27756f79, 0x74206572,
|
||||
0x62206f6f, 0x646e696c, 0x206f7420,
|
||||
0x0a656573, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x76696720, 0x6f792065,
|
||||
0x70752075, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x74656c20, 0x756f7920,
|
||||
0x776f6420, 0x654e0a6e, 0x20726576,
|
||||
0x6e6e6f67, 0x75722061, 0x7261206e,
|
||||
0x646e756f, 0x646e6120, 0x73656420,
|
||||
0x20747265, 0x0a756f79, 0x6576654e,
|
||||
0x6f672072, 0x20616e6e, 0x656b616d,
|
||||
0x756f7920, 0x79726320, 0x76654e0a,
|
||||
0x67207265, 0x616e6e6f, 0x79617320,
|
||||
0x6f6f6720, 0x65796264, 0x76654e0a,
|
||||
0x67207265, 0x616e6e6f, 0x6c657420,
|
||||
0x2061206c, 0x2065696c, 0x20646e61,
|
||||
0x74727568, 0x756f7920, 0x654e0a0a,
|
||||
0x20726576, 0x6e6e6f67, 0x69672061,
|
||||
0x79206576, 0x7520756f, 0x654e0a70,
|
||||
0x20726576, 0x6e6e6f67, 0x656c2061,
|
||||
0x6f792074, 0x6f642075, 0x4e0a6e77,
|
||||
0x72657665, 0x6e6f6720, 0x7220616e,
|
||||
0x61206e75, 0x6e756f72, 0x6e612064,
|
||||
0x65642064, 0x74726573, 0x756f7920,
|
||||
0x76654e0a, 0x67207265, 0x616e6e6f,
|
||||
0x6b616d20, 0x6f792065, 0x72632075,
|
||||
0x654e0a79, 0x20726576, 0x6e6e6f67,
|
||||
0x61732061, 0x6f672079, 0x7962646f,
|
||||
0x654e0a65, 0x20726576, 0x6e6e6f67,
|
||||
0x65742061, 0x61206c6c, 0x65696c20,
|
||||
0x646e6120, 0x72756820, 0x6f792074,
|
||||
0x280a0a75, 0x2c686f4f, 0x76696720,
|
||||
0x6f792065, 0x70752075, 0x4f280a29,
|
||||
0x202c686f, 0x65766967, 0x756f7920,
|
||||
0x29707520, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x76696720, 0x6e202c65,
|
||||
0x72657665, 0x6e6f6720, 0x6720616e,
|
||||
0x0a657669, 0x76694728, 0x6f792065,
|
||||
0x70752075, 0x654e0a29, 0x20726576,
|
||||
0x6e6e6f67, 0x69672061, 0x202c6576,
|
||||
0x6576656e, 0x6f672072, 0x20616e6e,
|
||||
0x65766967, 0x6947280a, 0x79206576,
|
||||
0x7520756f, 0x0a0a2970, 0x76276557,
|
||||
0x6e6b2065, 0x206e776f, 0x68636165,
|
||||
0x68746f20, 0x66207265, 0x7320726f,
|
||||
0x6f6c206f, 0x590a676e, 0x2072756f,
|
||||
0x72616568, 0x20732774, 0x6e656562,
|
||||
0x68636120, 0x2c676e69, 0x74756220,
|
||||
0x756f590a, 0x20657227, 0x206f6f74,
|
||||
0x20796873, 0x73206f74, 0x69207961,
|
||||
0x6e490a74, 0x65646973, 0x6577202c,
|
||||
0x746f6220, 0x6e6b2068, 0x7720776f,
|
||||
0x27746168, 0x65622073, 0x67206e65,
|
||||
0x676e696f, 0x0a6e6f20, 0x6b206557,
|
||||
0x20776f6e, 0x20656874, 0x656d6167,
|
||||
0x646e6120, 0x27657720, 0x67206572,
|
||||
0x616e6e6f, 0x616c7020, 0x74692079,
|
||||
0x20490a0a, 0x7473756a, 0x6e617720,
|
||||
0x7420616e, 0x206c6c65, 0x20756f79,
|
||||
0x20776f68, 0x206d2749, 0x6c656566,
|
||||
0x0a676e69, 0x74746f47, 0x616d2061,
|
||||
0x7920656b, 0x7520756f, 0x7265646e,
|
||||
0x6e617473, 0x4e0a0a64, 0x72657665,
|
||||
0x6e6f6720, 0x6720616e, 0x20657669,
|
||||
0x20756f79, 0x4e0a7075, 0x72657665,
|
||||
0x6e6f6720, 0x6c20616e, 0x79207465,
|
||||
0x6420756f, 0x0a6e776f, 0x6576654e,
|
||||
0x6f672072, 0x20616e6e, 0x206e7572,
|
||||
0x756f7261, 0x6120646e, 0x6420646e,
|
||||
0x72657365, 0x6f792074, 0x654e0a75,
|
||||
0x20726576, 0x6e6e6f67, 0x616d2061,
|
||||
0x7920656b, 0x6320756f, 0x4e0a7972,
|
||||
0x72657665, 0x6e6f6720, 0x7320616e,
|
||||
0x67207961, 0x62646f6f, 0x4e0a6579,
|
||||
0x72657665, 0x6e6f6720, 0x7420616e,
|
||||
0x206c6c65, 0x696c2061, 0x6e612065,
|
||||
0x75682064, 0x79207472, 0x0a0a756f,
|
||||
0x6576654e, 0x6f672072, 0x20616e6e,
|
||||
0x65766967, 0x756f7920, 0x0a707520,
|
||||
0x6576654e, 0x6f672072, 0x20616e6e,
|
||||
0x2074656c, 0x20756f79, 0x6e776f64,
|
||||
0x76654e0a, 0x67207265, 0x616e6e6f,
|
||||
0x6e757220, 0x6f726120, 0x20646e75,
|
||||
0x20646e61, 0x65736564, 0x79207472,
|
||||
0x4e0a756f, 0x72657665, 0x6e6f6720,
|
||||
0x6d20616e, 0x20656b61, 0x20756f79,
|
||||
0x0a797263, 0x6576654e, 0x6f672072,
|
||||
0x20616e6e, 0x20796173, 0x646f6f67,
|
||||
0x0a657962, 0x6576654e, 0x6f672072,
|
||||
0x20616e6e, 0x6c6c6574, 0x6c206120,
|
||||
0x61206569, 0x6820646e, 0x20747275,
|
||||
0x0a756f79, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x76696720, 0x6f792065,
|
||||
0x70752075, 0x76654e0a, 0x67207265,
|
||||
0x616e6e6f, 0x74656c20, 0x756f7920,
|
||||
0x776f6420, 0x654e0a6e, 0x20726576,
|
||||
0x6e6e6f67, 0x75722061, 0x7261206e,
|
||||
0x646e756f, 0x646e6120, 0x73656420,
|
||||
0x20747265, 0x0a756f79, 0x6576654e,
|
||||
0x6f672072, 0x20616e6e, 0x656b616d,
|
||||
0x756f7920, 0x79726320, 0x76654e0a,
|
||||
0x67207265, 0x616e6e6f, 0x79617320,
|
||||
0x6f6f6720, 0x65796264, 0x76654e0a,
|
||||
0x67207265, 0x616e6e6f, 0x6c657420,
|
||||
0x2061206c, 0x2065696c, 0x20646e61
|
||||
};
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(dht_sensor)
|
||||
add_subdirectory(hello_7segment)
|
||||
add_subdirectory(hello_gpio_irq)
|
||||
endif ()
|
||||
@ -1,11 +0,0 @@
|
||||
add_executable(dht
|
||||
dht.c
|
||||
)
|
||||
|
||||
target_link_libraries(dht pico_stdlib)
|
||||
|
||||
pico_add_extra_outputs(dht)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(dht)
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
= DHT-11, DHT-22, and AM2302 Sensors
|
||||
:xrefstyle: short
|
||||
|
||||
The DHT sensors are fairly well known hobbyist sensors for measuring relative humidity and temperature using a capacitive humidity sensor, and a thermistor. While they are slow, one reading every ~2 seconds, they are reliable and good for basic data logging. Communication is based on a custom protocol which uses a single wire for data.
|
||||
|
||||
[NOTE]
|
||||
======
|
||||
The DHT-11 and DHT-22 sensors are the most common. They use the same protocol but have different characteristics, the DHT-22 has better accuracy, and has a larger sensor range than the DHT-11. The sensor is available from a number of retailers.
|
||||
======
|
||||
|
||||
== Wiring information
|
||||
|
||||
See <<dht-wiring-diagram>> for wiring instructions.
|
||||
|
||||
[[dht-wiring-diagram]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring the DHT-22 temperature sensor to Raspberry Pi Pico, and connecting Pico's UART0 to the Raspberry Pi 4.
|
||||
image::pi-and-pico-uart-and-dht-sensor.png[]
|
||||
|
||||
NOTE: One of the pins (pin 3) on the DHT sensor will not be connected, it is not used.
|
||||
|
||||
You will want to place a 10 kΩ resistor between VCC and the data pin, to act as a medium-strength pull up on the data line.
|
||||
|
||||
Connecting UART0 of Pico to Raspberry Pi as in <<dht-wiring-diagram>> and you should see something similar to <<dht-serial-output-diagram>> in `minicom` when connected to `/dev/serial0` on the Raspberry Pi.
|
||||
|
||||
[[dht-serial-output-diagram]]
|
||||
[pdfwidth=75%]
|
||||
.Serial output over Pico's UART0 in a terminal window.
|
||||
image::serial-output.png[]
|
||||
|
||||
Connect to `/dev/serial0` by typing,
|
||||
|
||||
----
|
||||
$ minicom -b 115200 -o -D /dev/serial0
|
||||
----
|
||||
|
||||
at the command line.
|
||||
|
||||
== List of Files
|
||||
|
||||
A list of files with descriptions of their function;
|
||||
|
||||
CMakeLists.txt:: Make file to incorporate the example in to the examples build tree.
|
||||
dht.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[dht-22-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| 10 kΩ resistor | 1 | generic part
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
| DHT-22 sensor | 1 | generic part
|
||||
|===
|
||||
@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
**/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
#ifdef PICO_DEFAULT_LED_PIN
|
||||
#define LED_PIN PICO_DEFAULT_LED_PIN
|
||||
#endif
|
||||
|
||||
const uint DHT_PIN = 15;
|
||||
const uint MAX_TIMINGS = 85;
|
||||
|
||||
typedef struct {
|
||||
float humidity;
|
||||
float temp_celsius;
|
||||
} dht_reading;
|
||||
|
||||
void read_from_dht(dht_reading *result);
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
gpio_init(DHT_PIN);
|
||||
#ifdef LED_PIN
|
||||
gpio_init(LED_PIN);
|
||||
gpio_set_dir(LED_PIN, GPIO_OUT);
|
||||
#endif
|
||||
while (1) {
|
||||
dht_reading reading;
|
||||
read_from_dht(&reading);
|
||||
float fahrenheit = (reading.temp_celsius * 9 / 5) + 32;
|
||||
printf("Humidity = %.1f%%, Temperature = %.1fC (%.1fF)\n",
|
||||
reading.humidity, reading.temp_celsius, fahrenheit);
|
||||
|
||||
sleep_ms(2000);
|
||||
}
|
||||
}
|
||||
|
||||
void read_from_dht(dht_reading *result) {
|
||||
int data[5] = {0, 0, 0, 0, 0};
|
||||
uint last = 1;
|
||||
uint j = 0;
|
||||
|
||||
gpio_set_dir(DHT_PIN, GPIO_OUT);
|
||||
gpio_put(DHT_PIN, 0);
|
||||
sleep_ms(20);
|
||||
gpio_set_dir(DHT_PIN, GPIO_IN);
|
||||
|
||||
#ifdef LED_PIN
|
||||
gpio_put(LED_PIN, 1);
|
||||
#endif
|
||||
for (uint i = 0; i < MAX_TIMINGS; i++) {
|
||||
uint count = 0;
|
||||
while (gpio_get(DHT_PIN) == last) {
|
||||
count++;
|
||||
sleep_us(1);
|
||||
if (count == 255) break;
|
||||
}
|
||||
last = gpio_get(DHT_PIN);
|
||||
if (count == 255) break;
|
||||
|
||||
if ((i >= 4) && (i % 2 == 0)) {
|
||||
data[j / 8] <<= 1;
|
||||
if (count > 16) data[j / 8] |= 1;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
#ifdef LED_PIN
|
||||
gpio_put(LED_PIN, 0);
|
||||
#endif
|
||||
|
||||
if ((j >= 40) && (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF))) {
|
||||
result->humidity = (float) ((data[0] << 8) + data[1]) / 10;
|
||||
if (result->humidity > 100) {
|
||||
result->humidity = data[0];
|
||||
}
|
||||
result->temp_celsius = (float) (((data[2] & 0x7F) << 8) + data[3]) / 10;
|
||||
if (result->temp_celsius > 125) {
|
||||
result->temp_celsius = data[2];
|
||||
}
|
||||
if (data[2] & 0x80) {
|
||||
result->temp_celsius = -result->temp_celsius;
|
||||
}
|
||||
} else {
|
||||
printf("Bad data\n");
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 303 KiB |
|
Before Width: | Height: | Size: 409 KiB |
@ -1,12 +0,0 @@
|
||||
add_executable(hello_7segment
|
||||
hello_7segment.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_7segment pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_7segment)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_7segment)
|
||||
@ -1,48 +0,0 @@
|
||||
= Attaching a 7 segment LED via GPIO
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico to a generic 7 segment LED device. It uses the LED to count from 0 to 9 and then repeat. If the button is pressed, then the numbers will count down instead of up.
|
||||
|
||||
== Wiring information
|
||||
|
||||
Our 7 Segment display has pins as follows.
|
||||
|
||||
----
|
||||
--A--
|
||||
F B
|
||||
--G--
|
||||
E C
|
||||
--D--
|
||||
----
|
||||
|
||||
By default we are allocating GPIO 2 to segment A, 3 to B etc.
|
||||
So, connect GPIO 2 to pin A on the 7 segment LED display and so on. You will need the appropriate resistors (68 ohm should be fine) for each segment.
|
||||
The LED device used here is common anode, so the anode pin is connected to the 3.3v supply, and the GPIOs need to pull low (to ground) to complete the circuit.
|
||||
The pull direction of the GPIOs is specified in the code itself.
|
||||
|
||||
Connect the switch to connect on pressing. One side should be connected to ground, the other to GPIO 9.
|
||||
|
||||
|
||||
[[hello_7segment_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for 7 segment LED.
|
||||
image::hello_7segment_bb.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree.
|
||||
hello_7segment.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[hello_7segment-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| 7 segment LED module | 1 | generic part
|
||||
| 68 ohm resistor | 7 | generic part
|
||||
| DIL push to make switch | 1 | generic switch
|
||||
| M/M Jumper wires | 10 | generic part
|
||||
|===
|
||||
@ -1,95 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
/*
|
||||
Our 7 Segment display has pins as follows:
|
||||
|
||||
--A--
|
||||
F B
|
||||
--G--
|
||||
E C
|
||||
--D--
|
||||
|
||||
By default we are allocating GPIO 2 to segment A, 3 to B etc.
|
||||
So, connect GPIO 2 to pin A on the 7 segment LED display etc. Don't forget
|
||||
the appropriate resistors, best to use one for each segment!
|
||||
|
||||
Connect button so that pressing the switch connects the GPIO 9 (default) to
|
||||
ground (pull down)
|
||||
*/
|
||||
|
||||
#define FIRST_GPIO 2
|
||||
#define BUTTON_GPIO (FIRST_GPIO+7)
|
||||
|
||||
// This array converts a number 0-9 to a bit pattern to send to the GPIOs
|
||||
int bits[10] = {
|
||||
0x3f, // 0
|
||||
0x06, // 1
|
||||
0x5b, // 2
|
||||
0x4f, // 3
|
||||
0x66, // 4
|
||||
0x6d, // 5
|
||||
0x7d, // 6
|
||||
0x07, // 7
|
||||
0x7f, // 8
|
||||
0x67 // 9
|
||||
};
|
||||
|
||||
/// \tag::hello_gpio[]
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
printf("Hello, 7segment - press button to count down!\n");
|
||||
|
||||
// We could use gpio_set_dir_out_masked() here
|
||||
for (int gpio = FIRST_GPIO; gpio < FIRST_GPIO + 7; gpio++) {
|
||||
gpio_init(gpio);
|
||||
gpio_set_dir(gpio, GPIO_OUT);
|
||||
// Our bitmap above has a bit set where we need an LED on, BUT, we are pulling low to light
|
||||
// so invert our output
|
||||
gpio_set_outover(gpio, GPIO_OVERRIDE_INVERT);
|
||||
}
|
||||
|
||||
gpio_init(BUTTON_GPIO);
|
||||
gpio_set_dir(BUTTON_GPIO, GPIO_IN);
|
||||
// We are using the button to pull down to 0v when pressed, so ensure that when
|
||||
// unpressed, it uses internal pull ups. Otherwise when unpressed, the input will
|
||||
// be floating.
|
||||
gpio_pull_up(BUTTON_GPIO);
|
||||
|
||||
int val = 0;
|
||||
while (true) {
|
||||
// Count upwards or downwards depending on button input
|
||||
// We are pulling down on switch active, so invert the get to make
|
||||
// a press count downwards
|
||||
if (!gpio_get(BUTTON_GPIO)) {
|
||||
if (val == 9) {
|
||||
val = 0;
|
||||
} else {
|
||||
val++;
|
||||
}
|
||||
} else if (val == 0) {
|
||||
val = 9;
|
||||
} else {
|
||||
val--;
|
||||
}
|
||||
|
||||
// We are starting with GPIO 2, our bitmap starts at bit 0 so shift to start at 2.
|
||||
int32_t mask = bits[val] << FIRST_GPIO;
|
||||
|
||||
// Set all our GPIOs in one go!
|
||||
// If something else is using GPIO, we might want to use gpio_put_masked()
|
||||
gpio_set_mask(mask);
|
||||
sleep_ms(250);
|
||||
gpio_clr_mask(mask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/// \end::hello_gpio[]
|
||||
|
Before Width: | Height: | Size: 220 KiB |
@ -1,12 +0,0 @@
|
||||
add_executable(hello_gpio_irq
|
||||
hello_gpio_irq.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_gpio_irq pico_stdlib)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(hello_gpio_irq)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_gpio_irq)
|
||||
@ -1,61 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/gpio.h"
|
||||
|
||||
static char event_str[128];
|
||||
|
||||
void gpio_event_string(char *buf, uint32_t events);
|
||||
|
||||
void gpio_callback(uint gpio, uint32_t events) {
|
||||
// Put the GPIO event(s) that just happened into event_str
|
||||
// so we can print it
|
||||
gpio_event_string(event_str, events);
|
||||
printf("GPIO %d %s\n", gpio, event_str);
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
printf("Hello GPIO IRQ\n");
|
||||
gpio_set_irq_enabled_with_callback(2, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
|
||||
|
||||
// Wait forever
|
||||
while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const char *gpio_irq_str[] = {
|
||||
"LEVEL_LOW", // 0x1
|
||||
"LEVEL_HIGH", // 0x2
|
||||
"EDGE_FALL", // 0x4
|
||||
"EDGE_RISE" // 0x8
|
||||
};
|
||||
|
||||
void gpio_event_string(char *buf, uint32_t events) {
|
||||
for (uint i = 0; i < 4; i++) {
|
||||
uint mask = (1 << i);
|
||||
if (events & mask) {
|
||||
// Copy this event string into the user string
|
||||
const char *event_str = gpio_irq_str[i];
|
||||
while (*event_str != '\0') {
|
||||
*buf++ = *event_str++;
|
||||
}
|
||||
events &= ~mask;
|
||||
|
||||
// If more events add ", "
|
||||
if (events) {
|
||||
*buf++ = ',';
|
||||
*buf++ = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
*buf++ = '\0';
|
||||
}
|
||||
@ -1,2 +0,0 @@
|
||||
add_subdirectory(serial)
|
||||
add_subdirectory(usb)
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(hello_serial
|
||||
hello_serial.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_serial pico_stdlib)
|
||||
|
||||
# create map/bin/hex/uf2 file etc.
|
||||
pico_add_extra_outputs(hello_serial)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_serial)
|
||||
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
while (true) {
|
||||
printf("Hello, world!\n");
|
||||
sleep_ms(1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
if (TARGET tinyusb_device)
|
||||
add_executable(hello_usb
|
||||
hello_usb.c
|
||||
)
|
||||
|
||||
# pull in common dependencies
|
||||
target_link_libraries(hello_usb pico_stdlib)
|
||||
|
||||
# enable usb output, disable uart output
|
||||
pico_enable_stdio_usb(hello_usb 1)
|
||||
pico_enable_stdio_uart(hello_usb 0)
|
||||
|
||||
# create map/bin/hex/uf2 file etc.
|
||||
pico_add_extra_outputs(hello_usb)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(hello_usb)
|
||||
elseif(PICO_ON_DEVICE)
|
||||
message(WARNING "not building hello_usb because TinyUSB submodule is not initialized in the SDK")
|
||||
endif()
|
||||
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
while (true) {
|
||||
printf("Hello, world!\n");
|
||||
sleep_ms(1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
if (NOT PICO_NO_HARDWARE)
|
||||
add_subdirectory(bmp280_i2c)
|
||||
add_subdirectory(bus_scan)
|
||||
add_subdirectory(lcd_1602_i2c)
|
||||
add_subdirectory(lis3dh_i2c)
|
||||
add_subdirectory(mcp9808_i2c)
|
||||
add_subdirectory(mma8451_i2c)
|
||||
add_subdirectory(mpl3115a2_i2c)
|
||||
add_subdirectory(mpu6050_i2c)
|
||||
add_subdirectory(oled_i2c)
|
||||
add_subdirectory(pa1010d_i2c)
|
||||
add_subdirectory(pcf8523_i2c)
|
||||
endif ()
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(bmp280_i2c
|
||||
bmp280_i2c.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
target_link_libraries(bmp280_i2c pico_stdlib hardware_i2c)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(bmp280_i2c)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(bmp280_i2c)
|
||||
@ -1,41 +0,0 @@
|
||||
= Attaching a BMP280 temp/pressure sensor via I2C
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico with the popular BMP280 temperature and air pressure sensor manufactured by Bosch. A similar variant, the BME280, exists that can also measure humidity. There is another example that uses the BME280 device but talks to it via SPI as opposed to I2C.
|
||||
|
||||
The code reads data from the sensor's registers every 500 milliseconds and prints it via the onboard UART. This example operates the BMP280 in _normal_ mode, meaning that the device continuously cycles between a measurement period and a standby period at a regular interval we can set. This has the advantage that subsequent reads do not require configuration register writes and is the recommended mode of operation to filter out short-term disturbances.
|
||||
|
||||
[TIP]
|
||||
======
|
||||
The BMP280 is highly configurable with 3 modes of operation, various oversampling levels, and 5 filter settings. Find the datasheet online (https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf) to explore all of its capabilities beyond the simple example given here.
|
||||
======
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 4 jumpers, to connect VCC (3.3v), GND, SDA and SCL. The example here uses the default I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the 3.3V pin from the Pico.
|
||||
|
||||
WARNING: The BMP280 has a maximum supply voltage rating of 3.6V. Most breakout boards have voltage regulators that will allow a range of input voltages of 2-6V, but make sure to check beforehand.
|
||||
|
||||
[[bmp280_i2c_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for BMP280 sensor via I2C.
|
||||
image::bmp280_i2c_bb.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example into the examples build tree.
|
||||
bmp280_i2c.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[bmp280_i2c-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| BMP280-based breakout board | 1 | https://shop.pimoroni.com/products/bmp280-breakout-temperature-pressure-altitude-sensor[from Pimoroni]
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
|===
|
||||
|
||||
|
||||
@ -1,254 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
**/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "hardware/i2c.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "pico/stdlib.h"
|
||||
|
||||
/* Example code to talk to a BMP280 temperature and pressure sensor
|
||||
|
||||
NOTE: Ensure the device is capable of being driven at 3.3v NOT 5v. The Pico
|
||||
GPIO (and therefore I2C) cannot be used at 5v.
|
||||
|
||||
You will need to use a level shifter on the I2C lines if you want to run the
|
||||
board at 5v.
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO PICO_DEFAULT_I2C_SDA_PIN (on Pico this is GP4 (pin 6)) -> SDA on BMP280
|
||||
board
|
||||
GPIO PICO_DEFAULT_I2C_SCK_PIN (on Pico this is GP5 (pin 7)) -> SCL on
|
||||
BMP280 board
|
||||
3.3v (pin 36) -> VCC on BMP280 board
|
||||
GND (pin 38) -> GND on BMP280 board
|
||||
*/
|
||||
|
||||
// device has default bus address of 0x76
|
||||
#define ADDR _u(0x76)
|
||||
|
||||
// hardware registers
|
||||
#define REG_CONFIG _u(0xF5)
|
||||
#define REG_CTRL_MEAS _u(0xF4)
|
||||
#define REG_RESET _u(0xE0)
|
||||
|
||||
#define REG_TEMP_XLSB _u(0xFC)
|
||||
#define REG_TEMP_LSB _u(0xFB)
|
||||
#define REG_TEMP_MSB _u(0xFA)
|
||||
|
||||
#define REG_PRESSURE_XLSB _u(0xF9)
|
||||
#define REG_PRESSURE_LSB _u(0xF8)
|
||||
#define REG_PRESSURE_MSB _u(0xF7)
|
||||
|
||||
// calibration registers
|
||||
#define REG_DIG_T1_LSB _u(0x88)
|
||||
#define REG_DIG_T1_MSB _u(0x89)
|
||||
#define REG_DIG_T2_LSB _u(0x8A)
|
||||
#define REG_DIG_T2_MSB _u(0x8B)
|
||||
#define REG_DIG_T3_LSB _u(0x8C)
|
||||
#define REG_DIG_T3_MSB _u(0x8D)
|
||||
#define REG_DIG_P1_LSB _u(0x8E)
|
||||
#define REG_DIG_P1_MSB _u(0x8F)
|
||||
#define REG_DIG_P2_LSB _u(0x90)
|
||||
#define REG_DIG_P2_MSB _u(0x91)
|
||||
#define REG_DIG_P3_LSB _u(0x92)
|
||||
#define REG_DIG_P3_MSB _u(0x93)
|
||||
#define REG_DIG_P4_LSB _u(0x94)
|
||||
#define REG_DIG_P4_MSB _u(0x95)
|
||||
#define REG_DIG_P5_LSB _u(0x96)
|
||||
#define REG_DIG_P5_MSB _u(0x97)
|
||||
#define REG_DIG_P6_LSB _u(0x98)
|
||||
#define REG_DIG_P6_MSB _u(0x99)
|
||||
#define REG_DIG_P7_LSB _u(0x9A)
|
||||
#define REG_DIG_P7_MSB _u(0x9B)
|
||||
#define REG_DIG_P8_LSB _u(0x9C)
|
||||
#define REG_DIG_P8_MSB _u(0x9D)
|
||||
#define REG_DIG_P9_LSB _u(0x9E)
|
||||
#define REG_DIG_P9_MSB _u(0x9F)
|
||||
|
||||
// number of calibration registers to be read
|
||||
#define NUM_CALIB_PARAMS 24
|
||||
|
||||
struct bmp280_calib_param {
|
||||
// temperature params
|
||||
uint16_t dig_t1;
|
||||
int16_t dig_t2;
|
||||
int16_t dig_t3;
|
||||
|
||||
// pressure params
|
||||
uint16_t dig_p1;
|
||||
int16_t dig_p2;
|
||||
int16_t dig_p3;
|
||||
int16_t dig_p4;
|
||||
int16_t dig_p5;
|
||||
int16_t dig_p6;
|
||||
int16_t dig_p7;
|
||||
int16_t dig_p8;
|
||||
int16_t dig_p9;
|
||||
};
|
||||
|
||||
#ifdef i2c_default
|
||||
void bmp280_init() {
|
||||
// use the "handheld device dynamic" optimal setting (see datasheet)
|
||||
uint8_t buf[2];
|
||||
|
||||
// 500ms sampling time, x16 filter
|
||||
const uint8_t reg_config_val = ((0x04 << 5) | (0x05 << 2)) & 0xFC;
|
||||
|
||||
// send register number followed by its corresponding value
|
||||
buf[0] = REG_CONFIG;
|
||||
buf[1] = reg_config_val;
|
||||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false);
|
||||
|
||||
// osrs_t x1, osrs_p x4, normal mode operation
|
||||
const uint8_t reg_ctrl_meas_val = (0x01 << 5) | (0x03 << 2) | (0x03);
|
||||
buf[0] = REG_CTRL_MEAS;
|
||||
buf[1] = reg_ctrl_meas_val;
|
||||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false);
|
||||
}
|
||||
|
||||
void bmp280_read_raw(int32_t* temp, int32_t* pressure) {
|
||||
// BMP280 data registers are auto-incrementing and we have 3 temperature and
|
||||
// pressure registers each, so we start at 0xF7 and read 6 bytes to 0xFC
|
||||
// note: normal mode does not require further ctrl_meas and config register writes
|
||||
|
||||
uint8_t buf[6];
|
||||
uint8_t reg = REG_PRESSURE_MSB;
|
||||
i2c_write_blocking(i2c_default, ADDR, ®, 1, true); // true to keep master control of bus
|
||||
i2c_read_blocking(i2c_default, ADDR, buf, 6, false); // false - finished with bus
|
||||
|
||||
// store the 20 bit read in a 32 bit signed integer for conversion
|
||||
*pressure = (buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4);
|
||||
*temp = (buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4);
|
||||
}
|
||||
|
||||
void bmp280_reset() {
|
||||
// reset the device with the power-on-reset procedure
|
||||
uint8_t buf[2] = { REG_RESET, 0xB6 };
|
||||
i2c_write_blocking(i2c_default, ADDR, buf, 2, false);
|
||||
}
|
||||
|
||||
// intermediate function that calculates the fine resolution temperature
|
||||
// used for both pressure and temperature conversions
|
||||
int32_t bmp280_convert(int32_t temp, struct bmp280_calib_param* params) {
|
||||
// use the 32-bit fixed point compensation implementation given in the
|
||||
// datasheet
|
||||
|
||||
int32_t var1, var2;
|
||||
var1 = ((((temp >> 3) - ((int32_t)params->dig_t1 << 1))) * ((int32_t)params->dig_t2)) >> 11;
|
||||
var2 = (((((temp >> 4) - ((int32_t)params->dig_t1)) * ((temp >> 4) - ((int32_t)params->dig_t1))) >> 12) * ((int32_t)params->dig_t3)) >> 14;
|
||||
return var1 + var2;
|
||||
}
|
||||
|
||||
int32_t bmp280_convert_temp(int32_t temp, struct bmp280_calib_param* params) {
|
||||
// uses the BMP280 calibration parameters to compensate the temperature value read from its registers
|
||||
int32_t t_fine = bmp280_convert(temp, params);
|
||||
return (t_fine * 5 + 128) >> 8;
|
||||
}
|
||||
|
||||
int32_t bmp280_convert_pressure(int32_t pressure, int32_t temp, struct bmp280_calib_param* params) {
|
||||
// uses the BMP280 calibration parameters to compensate the pressure value read from its registers
|
||||
|
||||
int32_t t_fine = bmp280_convert(temp, params);
|
||||
|
||||
int32_t var1, var2;
|
||||
uint32_t converted = 0.0;
|
||||
var1 = (((int32_t)t_fine) >> 1) - (int32_t)64000;
|
||||
var2 = (((var1 >> 2) * (var1 >> 2)) >> 11) * ((int32_t)params->dig_p6);
|
||||
var2 += ((var1 * ((int32_t)params->dig_p5)) << 1);
|
||||
var2 = (var2 >> 2) + (((int32_t)params->dig_p4) << 16);
|
||||
var1 = (((params->dig_p3 * (((var1 >> 2) * (var1 >> 2)) >> 13)) >> 3) + ((((int32_t)params->dig_p2) * var1) >> 1)) >> 18;
|
||||
var1 = ((((32768 + var1)) * ((int32_t)params->dig_p1)) >> 15);
|
||||
if (var1 == 0) {
|
||||
return 0; // avoid exception caused by division by zero
|
||||
}
|
||||
converted = (((uint32_t)(((int32_t)1048576) - pressure) - (var2 >> 12))) * 3125;
|
||||
if (converted < 0x80000000) {
|
||||
converted = (converted << 1) / ((uint32_t)var1);
|
||||
} else {
|
||||
converted = (converted / (uint32_t)var1) * 2;
|
||||
}
|
||||
var1 = (((int32_t)params->dig_p9) * ((int32_t)(((converted >> 3) * (converted >> 3)) >> 13))) >> 12;
|
||||
var2 = (((int32_t)(converted >> 2)) * ((int32_t)params->dig_p8)) >> 13;
|
||||
converted = (uint32_t)((int32_t)converted + ((var1 + var2 + params->dig_p7) >> 4));
|
||||
return converted;
|
||||
}
|
||||
|
||||
void bmp280_get_calib_params(struct bmp280_calib_param* params) {
|
||||
// raw temp and pressure values need to be calibrated according to
|
||||
// parameters generated during the manufacturing of the sensor
|
||||
// there are 3 temperature params, and 9 pressure params, each with a LSB
|
||||
// and MSB register, so we read from 24 registers
|
||||
|
||||
uint8_t buf[NUM_CALIB_PARAMS] = { 0 };
|
||||
uint8_t reg = REG_DIG_T1_LSB;
|
||||
i2c_write_blocking(i2c_default, ADDR, ®, 1, true); // true to keep master control of bus
|
||||
// read in one go as register addresses auto-increment
|
||||
i2c_read_blocking(i2c_default, ADDR, buf, NUM_CALIB_PARAMS, false); // false, we're done reading
|
||||
|
||||
// store these in a struct for later use
|
||||
params->dig_t1 = (uint16_t)(buf[1] << 8) | buf[0];
|
||||
params->dig_t2 = (int16_t)(buf[3] << 8) | buf[2];
|
||||
params->dig_t3 = (int16_t)(buf[5] << 8) | buf[4];
|
||||
|
||||
params->dig_p1 = (uint16_t)(buf[7] << 8) | buf[6];
|
||||
params->dig_p2 = (int16_t)(buf[9] << 8) | buf[8];
|
||||
params->dig_p3 = (int16_t)(buf[11] << 8) | buf[10];
|
||||
params->dig_p4 = (int16_t)(buf[13] << 8) | buf[12];
|
||||
params->dig_p5 = (int16_t)(buf[15] << 8) | buf[14];
|
||||
params->dig_p6 = (int16_t)(buf[17] << 8) | buf[16];
|
||||
params->dig_p7 = (int16_t)(buf[19] << 8) | buf[18];
|
||||
params->dig_p8 = (int16_t)(buf[21] << 8) | buf[20];
|
||||
params->dig_p9 = (int16_t)(buf[23] << 8) | buf[22];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
#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
|
||||
i2c_init(i2c_default, 100 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
|
||||
|
||||
// configure BMP280
|
||||
bmp280_init();
|
||||
|
||||
// retrieve fixed compensation params
|
||||
struct bmp280_calib_param params;
|
||||
bmp280_get_calib_params(¶ms);
|
||||
|
||||
int32_t raw_temperature;
|
||||
int32_t raw_pressure;
|
||||
|
||||
sleep_ms(250); // sleep so that data polling and register update don't collide
|
||||
while (1) {
|
||||
bmp280_read_raw(&raw_temperature, &raw_pressure);
|
||||
int32_t temperature = bmp280_convert_temp(raw_temperature, ¶ms);
|
||||
int32_t pressure = bmp280_convert_pressure(raw_pressure, raw_temperature, ¶ms);
|
||||
printf("Pressure = %.3f kPa\n", pressure / 1000.f);
|
||||
printf("Temp. = %.2f C\n", temperature / 100.f);
|
||||
// poll every 500ms
|
||||
sleep_ms(500);
|
||||
}
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 151 KiB |
@ -1,12 +0,0 @@
|
||||
add_executable(i2c_bus_scan
|
||||
bus_scan.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
target_link_libraries(i2c_bus_scan pico_stdlib hardware_i2c)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(i2c_bus_scan)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(i2c_bus_scan)
|
||||
@ -1,77 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
// Sweep through all 7-bit I2C addresses, to see if any slaves are present on
|
||||
// the I2C bus. Print out a table that looks like this:
|
||||
//
|
||||
// I2C Bus Scan
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
// 0
|
||||
// 1 @
|
||||
// 2
|
||||
// 3 @
|
||||
// 4
|
||||
// 5
|
||||
// 6
|
||||
// 7
|
||||
//
|
||||
// E.g. if slave addresses 0x12 and 0x34 were acknowledged.
|
||||
|
||||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
// I2C reserves some addresses for special purposes. We exclude these from the scan.
|
||||
// These are any addresses of the form 000 0xxx or 111 1xxx
|
||||
bool reserved_addr(uint8_t addr) {
|
||||
return (addr & 0x78) == 0 || (addr & 0x78) == 0x78;
|
||||
}
|
||||
|
||||
int main() {
|
||||
// Enable UART so we can print status output
|
||||
stdio_init_all();
|
||||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
|
||||
#warning i2c/bus_scan example requires a board with I2C pins
|
||||
puts("Default I2C pins were not defined");
|
||||
#else
|
||||
// This example will use I2C0 on the default SDA and SCL pins (GP4, GP5 on a Pico)
|
||||
i2c_init(i2c_default, 100 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
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));
|
||||
|
||||
printf("\nI2C Bus Scan\n");
|
||||
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
|
||||
|
||||
for (int addr = 0; addr < (1 << 7); ++addr) {
|
||||
if (addr % 16 == 0) {
|
||||
printf("%02x ", addr);
|
||||
}
|
||||
|
||||
// Perform a 1-byte dummy read from the probe address. If a slave
|
||||
// acknowledges this address, the function returns the number of bytes
|
||||
// transferred. If the address byte is ignored, the function returns
|
||||
// -1.
|
||||
|
||||
// Skip over any reserved addresses.
|
||||
int ret;
|
||||
uint8_t rxdata;
|
||||
if (reserved_addr(addr))
|
||||
ret = PICO_ERROR_GENERIC;
|
||||
else
|
||||
ret = i2c_read_blocking(i2c_default, addr, &rxdata, 1, false);
|
||||
|
||||
printf(ret < 0 ? "." : "@");
|
||||
printf(addr % 16 == 15 ? "\n" : " ");
|
||||
}
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
add_executable(lcd_1602_i2c
|
||||
lcd_1602_i2c.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
target_link_libraries(lcd_1602_i2c pico_stdlib hardware_i2c)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(lcd_1602_i2c)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(lcd_1602_i2c)
|
||||
@ -1,39 +0,0 @@
|
||||
= Attaching a 16x2 LCD via I2C
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico to one of the very common 16x2 LCD character displays. The display will need a 3.3V I2C adapter board as this example uses I2C for communications.
|
||||
|
||||
[NOTE]
|
||||
======
|
||||
These LCD displays can also be driven directly using GPIO without the use of an adapter board. That is beyond the scope of this example.
|
||||
======
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 4 jumpers, to connect VCC (3.3v), GND, SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the 3.3V pin.
|
||||
|
||||
WARNING: Many displays of this type are 5v. If you wish to use a 5v display you will need to use level shifters on the SDA and SCL lines to convert from the 3.3V used by the RP2040. Whilst a 5v display will just about work at 3.3v, the display will be dim.
|
||||
|
||||
|
||||
[[lcd_1602_i2c_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for LCD1602A LCD with I2C bridge.
|
||||
image::lcd_1602_i2c_bb.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree.
|
||||
lcd_1602_i2c.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[lcd_1602_i2c-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| 1602A based LCD panel 3.3v | 1 | generic part
|
||||
| 1602A to I2C bridge device 3.3v | 1 | generic part
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
|===
|
||||
@ -1,169 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "hardware/i2c.h"
|
||||
#include "pico/binary_info.h"
|
||||
|
||||
/* Example code to drive a 16x2 LCD panel via a I2C bridge chip (e.g. PCF8574)
|
||||
|
||||
NOTE: The panel must be capable of being driven at 3.3v NOT 5v. The Pico
|
||||
GPIO (and therefor I2C) cannot be used at 5v.
|
||||
|
||||
You will need to use a level shifter on the I2C lines if you want to run the
|
||||
board at 5v.
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO 4 (pin 6)-> SDA on LCD bridge board
|
||||
GPIO 5 (pin 7)-> SCL on LCD bridge board
|
||||
3.3v (pin 36) -> VCC on LCD bridge board
|
||||
GND (pin 38) -> GND on LCD bridge board
|
||||
*/
|
||||
// commands
|
||||
const int LCD_CLEARDISPLAY = 0x01;
|
||||
const int LCD_RETURNHOME = 0x02;
|
||||
const int LCD_ENTRYMODESET = 0x04;
|
||||
const int LCD_DISPLAYCONTROL = 0x08;
|
||||
const int LCD_CURSORSHIFT = 0x10;
|
||||
const int LCD_FUNCTIONSET = 0x20;
|
||||
const int LCD_SETCGRAMADDR = 0x40;
|
||||
const int LCD_SETDDRAMADDR = 0x80;
|
||||
|
||||
// flags for display entry mode
|
||||
const int LCD_ENTRYSHIFTINCREMENT = 0x01;
|
||||
const int LCD_ENTRYLEFT = 0x02;
|
||||
|
||||
// flags for display and cursor control
|
||||
const int LCD_BLINKON = 0x01;
|
||||
const int LCD_CURSORON = 0x02;
|
||||
const int LCD_DISPLAYON = 0x04;
|
||||
|
||||
// flags for display and cursor shift
|
||||
const int LCD_MOVERIGHT = 0x04;
|
||||
const int LCD_DISPLAYMOVE = 0x08;
|
||||
|
||||
// flags for function set
|
||||
const int LCD_5x10DOTS = 0x04;
|
||||
const int LCD_2LINE = 0x08;
|
||||
const int LCD_8BITMODE = 0x10;
|
||||
|
||||
// flag for backlight control
|
||||
const int LCD_BACKLIGHT = 0x08;
|
||||
|
||||
const int LCD_ENABLE_BIT = 0x04;
|
||||
|
||||
// By default these LCD display drivers are on bus address 0x27
|
||||
static int addr = 0x27;
|
||||
|
||||
// Modes for lcd_send_byte
|
||||
#define LCD_CHARACTER 1
|
||||
#define LCD_COMMAND 0
|
||||
|
||||
#define MAX_LINES 2
|
||||
#define MAX_CHARS 16
|
||||
|
||||
/* Quick helper function for single byte transfers */
|
||||
void i2c_write_byte(uint8_t val) {
|
||||
#ifdef i2c_default
|
||||
i2c_write_blocking(i2c_default, addr, &val, 1, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void lcd_toggle_enable(uint8_t val) {
|
||||
// Toggle enable pin on LCD display
|
||||
// We cannot do this too quickly or things don't work
|
||||
#define DELAY_US 600
|
||||
sleep_us(DELAY_US);
|
||||
i2c_write_byte(val | LCD_ENABLE_BIT);
|
||||
sleep_us(DELAY_US);
|
||||
i2c_write_byte(val & ~LCD_ENABLE_BIT);
|
||||
sleep_us(DELAY_US);
|
||||
}
|
||||
|
||||
// The display is sent a byte as two separate nibble transfers
|
||||
void lcd_send_byte(uint8_t val, int mode) {
|
||||
uint8_t high = mode | (val & 0xF0) | LCD_BACKLIGHT;
|
||||
uint8_t low = mode | ((val << 4) & 0xF0) | LCD_BACKLIGHT;
|
||||
|
||||
i2c_write_byte(high);
|
||||
lcd_toggle_enable(high);
|
||||
i2c_write_byte(low);
|
||||
lcd_toggle_enable(low);
|
||||
}
|
||||
|
||||
void lcd_clear(void) {
|
||||
lcd_send_byte(LCD_CLEARDISPLAY, LCD_COMMAND);
|
||||
}
|
||||
|
||||
// go to location on LCD
|
||||
void lcd_set_cursor(int line, int position) {
|
||||
int val = (line == 0) ? 0x80 + position : 0xC0 + position;
|
||||
lcd_send_byte(val, LCD_COMMAND);
|
||||
}
|
||||
|
||||
static void inline lcd_char(char val) {
|
||||
lcd_send_byte(val, LCD_CHARACTER);
|
||||
}
|
||||
|
||||
void lcd_string(const char *s) {
|
||||
while (*s) {
|
||||
lcd_char(*s++);
|
||||
}
|
||||
}
|
||||
|
||||
void lcd_init() {
|
||||
lcd_send_byte(0x03, LCD_COMMAND);
|
||||
lcd_send_byte(0x03, LCD_COMMAND);
|
||||
lcd_send_byte(0x03, LCD_COMMAND);
|
||||
lcd_send_byte(0x02, LCD_COMMAND);
|
||||
|
||||
lcd_send_byte(LCD_ENTRYMODESET | LCD_ENTRYLEFT, LCD_COMMAND);
|
||||
lcd_send_byte(LCD_FUNCTIONSET | LCD_2LINE, LCD_COMMAND);
|
||||
lcd_send_byte(LCD_DISPLAYCONTROL | LCD_DISPLAYON, LCD_COMMAND);
|
||||
lcd_clear();
|
||||
}
|
||||
|
||||
int main() {
|
||||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
|
||||
#warning i2c/lcd_1602_i2c example requires a board with I2C pins
|
||||
#else
|
||||
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
|
||||
i2c_init(i2c_default, 100 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
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));
|
||||
|
||||
lcd_init();
|
||||
|
||||
static char *message[] =
|
||||
{
|
||||
"RP2040 by", "Raspberry Pi",
|
||||
"A brand new", "microcontroller",
|
||||
"Twin core M0", "Full C SDK",
|
||||
"More power in", "your product",
|
||||
"More beans", "than Heinz!"
|
||||
};
|
||||
|
||||
while (1) {
|
||||
for (int m = 0; m < sizeof(message) / sizeof(message[0]); m += MAX_LINES) {
|
||||
for (int line = 0; line < MAX_LINES; line++) {
|
||||
lcd_set_cursor(line, (MAX_CHARS / 2) - strlen(message[m + line]) / 2);
|
||||
lcd_string(message[m + line]);
|
||||
}
|
||||
sleep_ms(2000);
|
||||
lcd_clear();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
Before Width: | Height: | Size: 261 KiB |
@ -1,12 +0,0 @@
|
||||
add_executable(lis3dh_i2c
|
||||
lis3dh_i2c.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
target_link_libraries(lis3dh_i2c pico_stdlib hardware_i2c)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(lis3dh_i2c)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(lis3dh_i2c)
|
||||
@ -1,38 +0,0 @@
|
||||
= Attaching a LIS3DH Nano Accelerometer via i2c.
|
||||
|
||||
This example shows you how to interface the Raspberry Pi Pico to the LIS3DH accelerometer and temperature sensor.
|
||||
|
||||
The code reads and displays the acceleration values of the board in the 3 axes and the ambient temperature value. The datasheet for the sensor can be found at https://www.st.com/resource/en/datasheet/cd00274221.pdf. The device is being operated on 'normal mode' and at a frequency of 1.344 kHz (this can be changed by editing the ODR bits of CTRL_REG4). The range of the data is controlled by the FS bit in CTRL_REG4 and is equal to ±2g in this example. The sensitivity depends on the operating mode and data range; exact values can be found on page 10 of the datasheet. In this case, the sensitivity value is 4mg (where g is the value of gravitational acceleration on the surface of Earth). In order to use the auxiliary ADC to read temperature, the we must set the BDU bit to 1 in CTRL_REG4 and the ADC_EN bit to 1 in TEMP_CFG_REG. Temperature is communicated through ADC 3.
|
||||
|
||||
[NOTE]
|
||||
=====
|
||||
The sensor doesn't have features to eliminate offsets in the data and these will need to be taken into account in the code.
|
||||
=====
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 4 jumpers, to connect VIN, GND, SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the 3V pin.
|
||||
|
||||
|
||||
[[lis3dh_i2c_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for LIS3DH.
|
||||
image::lis3dh_i2c.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree.
|
||||
lis3dh_i2c.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[lis3dh-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| LIS3DH board| 1 | https://www.adafruit.com/product/2809
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
|===
|
||||
@ -1,129 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
/* Example code to talk to a LIS3DH Mini GPS module.
|
||||
|
||||
This example reads data from all 3 axes of the accelerometer and uses an auxillary ADC to output temperature values.
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO PICO_DEFAULT_I2C_SDA_PIN (On Pico this is 4 (physical pin 6)) -> SDA on LIS3DH board
|
||||
GPIO PICO_DEFAULT_I2C_SCK_PIN (On Pico this is 5 (physical pin 7)) -> SCL on LIS3DH board
|
||||
3.3v (physical pin 36) -> VIN on LIS3DH board
|
||||
GND (physical pin 38) -> GND on LIS3DH board
|
||||
*/
|
||||
|
||||
// By default this device is on bus address 0x18
|
||||
|
||||
const int ADDRESS = 0x18;
|
||||
const uint8_t CTRL_REG_1 = 0x20;
|
||||
const uint8_t CTRL_REG_4 = 0x23;
|
||||
const uint8_t TEMP_CFG_REG = 0xC0;
|
||||
|
||||
#ifdef i2c_default
|
||||
|
||||
void lis3dh_init() {
|
||||
uint8_t buf[2];
|
||||
|
||||
// Turn normal mode and 1.344kHz data rate on
|
||||
buf[0] = CTRL_REG_1;
|
||||
buf[1] = 0x97;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
|
||||
// Turn block data update on (for temperature sensing)
|
||||
buf[0] = CTRL_REG_4;
|
||||
buf[1] = 0x80;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
|
||||
// Turn auxillary ADC on
|
||||
buf[0] = TEMP_CFG_REG;
|
||||
buf[1] = 0xC0;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
}
|
||||
|
||||
void lis3dh_calc_value(uint16_t raw_value, float *final_value, bool isAccel) {
|
||||
// Convert with respect to the value being temperature or acceleration reading
|
||||
float scaling;
|
||||
float senstivity = 0.004f; // g per unit
|
||||
|
||||
if (isAccel == true) {
|
||||
scaling = 64 / senstivity;
|
||||
} else {
|
||||
scaling = 64;
|
||||
}
|
||||
|
||||
// raw_value is signed
|
||||
*final_value = (float) ((int16_t) raw_value) / scaling;
|
||||
}
|
||||
|
||||
void lis3dh_read_data(uint8_t reg, float *final_value, bool IsAccel) {
|
||||
// Read two bytes of data and store in a 16 bit data structure
|
||||
uint8_t lsb;
|
||||
uint8_t msb;
|
||||
uint16_t raw_accel;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, ®, 1, true);
|
||||
i2c_read_blocking(i2c_default, ADDRESS, &lsb, 1, false);
|
||||
|
||||
reg |= 0x01;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, ®, 1, true);
|
||||
i2c_read_blocking(i2c_default, ADDRESS, &msb, 1, false);
|
||||
|
||||
raw_accel = (msb << 8) | lsb;
|
||||
|
||||
lis3dh_calc_value(raw_accel, final_value, IsAccel);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
|
||||
#warning i2c/lis3dh_i2c example requires a board with I2C pins
|
||||
puts("Default I2C pins were not defined");
|
||||
#else
|
||||
printf("Hello, LIS3DH! Reading raw data from registers...\n");
|
||||
|
||||
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
|
||||
i2c_init(i2c_default, 400 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
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));
|
||||
|
||||
float x_accel, y_accel, z_accel, temp;
|
||||
|
||||
lis3dh_init();
|
||||
|
||||
while (1) {
|
||||
lis3dh_read_data(0x28, &x_accel, true);
|
||||
lis3dh_read_data(0x2A, &y_accel, true);
|
||||
lis3dh_read_data(0x2C, &z_accel, true);
|
||||
lis3dh_read_data(0x0C, &temp, false);
|
||||
|
||||
// Display data
|
||||
printf("TEMPERATURE: %.3f%cC\n", temp, 176);
|
||||
// Acceleration is read as a multiple of g (gravitational acceleration on the Earth's surface)
|
||||
printf("ACCELERATION VALUES: \n");
|
||||
printf("X acceleration: %.3fg\n", x_accel);
|
||||
printf("Y acceleration: %.3fg\n", y_accel);
|
||||
printf("Z acceleration: %.3fg\n", z_accel);
|
||||
|
||||
sleep_ms(500);
|
||||
|
||||
// Clear terminal
|
||||
printf("\e[1;1H\e[2J");
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 160 KiB |
@ -1,12 +0,0 @@
|
||||
add_executable(mcp9808_i2c
|
||||
mcp9808_i2c.c
|
||||
)
|
||||
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
target_link_libraries(mcp9808_i2c pico_stdlib hardware_i2c)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(mcp9808_i2c)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(mcp9808_i2c)
|
||||
@ -1,34 +0,0 @@
|
||||
= Attaching a MCP9808 digital temperature sensor via I2C
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico to the MCP9808 digital temperature sensor board.
|
||||
|
||||
This example reads the ambient temperature value each second from the sensor and sets upper, lower and critical limits for the temperature and checks if alerts need to be raised. The CONFIG register can also be used to check for an alert if the critical temperature is surpassed.
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 4 jumpers, to connect VDD, GND, SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the VSYS pin.
|
||||
|
||||
[[mcp9808_i2c_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for MCP9808.
|
||||
image::mcp9808_i2c.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree.
|
||||
mcp9808_i2c.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[mcp9808-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| MCP9808 board| 1 | https://www.adafruit.com/product/1782
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
|===
|
||||
|
||||
|
||||
@ -1,147 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
/* Example code to talk to a MCP9808 ±0.5°C Digital temperature Sensor
|
||||
|
||||
This reads and writes to registers on the board.
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO PICO_DEFAULT_I2C_SDA_PIN (On Pico this is GP4 (physical pin 6)) -> SDA on MCP9808 board
|
||||
GPIO PICO_DEFAULT_I2C_SCK_PIN (On Pico this is GP5 (physcial pin 7)) -> SCL on MCP9808 board
|
||||
Vsys (physical pin 39) -> VDD on MCP9808 board
|
||||
GND (physical pin 38) -> GND on MCP9808 board
|
||||
|
||||
*/
|
||||
//The bus address is determined by the state of pins A0, A1 and A2 on the MCP9808 board
|
||||
static uint8_t ADDRESS = 0x18;
|
||||
|
||||
//hardware registers
|
||||
|
||||
const uint8_t REG_POINTER = 0x00;
|
||||
const uint8_t REG_CONFIG = 0x01;
|
||||
const uint8_t REG_TEMP_UPPER = 0x02;
|
||||
const uint8_t REG_TEMP_LOWER = 0x03;
|
||||
const uint8_t REG_TEMP_CRIT = 0x04;
|
||||
const uint8_t REG_TEMP_AMB = 0x05;
|
||||
const uint8_t REG_RESOLUTION = 0x08;
|
||||
|
||||
|
||||
void mcp9808_check_limits(uint8_t upper_byte) {
|
||||
|
||||
// Check flags and raise alerts accordingly
|
||||
if ((upper_byte & 0x40) == 0x40) { //TA > TUPPER
|
||||
printf("Temperature is above the upper temperature limit.\n");
|
||||
}
|
||||
if ((upper_byte & 0x20) == 0x20) { //TA < TLOWER
|
||||
printf("Temperature is below the lower temperature limit.\n");
|
||||
}
|
||||
if ((upper_byte & 0x80) == 0x80) { //TA > TCRIT
|
||||
printf("Temperature is above the critical temperature limit.\n");
|
||||
}
|
||||
}
|
||||
|
||||
float mcp9808_convert_temp(uint8_t upper_byte, uint8_t lower_byte) {
|
||||
|
||||
float temperature;
|
||||
|
||||
|
||||
//Check if TA <= 0°C and convert to denary accordingly
|
||||
if ((upper_byte & 0x10) == 0x10) {
|
||||
upper_byte = upper_byte & 0x0F;
|
||||
temperature = 256 - (((float) upper_byte * 16) + ((float) lower_byte / 16));
|
||||
} else {
|
||||
temperature = (((float) upper_byte * 16) + ((float) lower_byte / 16));
|
||||
|
||||
}
|
||||
return temperature;
|
||||
}
|
||||
|
||||
#ifdef i2c_default
|
||||
void mcp9808_set_limits() {
|
||||
|
||||
//Set an upper limit of 30°C for the temperature
|
||||
uint8_t upper_temp_msb = 0x01;
|
||||
uint8_t upper_temp_lsb = 0xE0;
|
||||
|
||||
//Set a lower limit of 20°C for the temperature
|
||||
uint8_t lower_temp_msb = 0x01;
|
||||
uint8_t lower_temp_lsb = 0x40;
|
||||
|
||||
//Set a critical limit of 40°C for the temperature
|
||||
uint8_t crit_temp_msb = 0x02;
|
||||
uint8_t crit_temp_lsb = 0x80;
|
||||
|
||||
uint8_t buf[3];
|
||||
buf[0] = REG_TEMP_UPPER;
|
||||
buf[1] = upper_temp_msb;
|
||||
buf[2] = upper_temp_lsb;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 3, false);
|
||||
|
||||
buf[0] = REG_TEMP_LOWER;
|
||||
buf[1] = lower_temp_msb;
|
||||
buf[2] = lower_temp_lsb;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 3, false);
|
||||
|
||||
buf[0] = REG_TEMP_CRIT;
|
||||
buf[1] = crit_temp_msb;
|
||||
buf[2] = crit_temp_lsb;;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 3, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
|
||||
stdio_init_all();
|
||||
|
||||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
|
||||
#warning i2c/mcp9808_i2c example requires a board with I2C pins
|
||||
puts("Default I2C pins were not defined");
|
||||
#else
|
||||
printf("Hello, MCP9808! Reading raw data from registers...\n");
|
||||
|
||||
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
|
||||
i2c_init(i2c_default, 400 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
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));
|
||||
|
||||
mcp9808_set_limits();
|
||||
|
||||
uint8_t buf[2];
|
||||
uint16_t upper_byte;
|
||||
uint16_t lower_byte;
|
||||
|
||||
float temperature;
|
||||
|
||||
while (1) {
|
||||
// Start reading ambient temperature register for 2 bytes
|
||||
i2c_write_blocking(i2c_default, ADDRESS, ®_TEMP_AMB, 1, true);
|
||||
i2c_read_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
|
||||
upper_byte = buf[0];
|
||||
lower_byte = buf[1];
|
||||
|
||||
//isolates limit flags in upper byte
|
||||
mcp9808_check_limits(upper_byte & 0xE0);
|
||||
|
||||
//clears flag bits in upper byte
|
||||
temperature = mcp9808_convert_temp(upper_byte & 0x1F, lower_byte);
|
||||
printf("Ambient temperature: %.4f°C\n", temperature);
|
||||
|
||||
sleep_ms(1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
Before Width: | Height: | Size: 151 KiB |
@ -1,11 +0,0 @@
|
||||
add_executable(mma8451_i2c
|
||||
mma8451_i2c.c
|
||||
)
|
||||
# pull in common dependencies and additional i2c hardware support
|
||||
target_link_libraries(mma8451_i2c pico_stdlib hardware_i2c)
|
||||
|
||||
# create map/bin/hex file etc.
|
||||
pico_add_extra_outputs(mma8451_i2c)
|
||||
|
||||
# add url via pico_set_program_url
|
||||
example_auto_set_url(mma8451_i2c)
|
||||
@ -1,36 +0,0 @@
|
||||
= Attaching a MMA8451 3-axis digital accelerometer via I2C
|
||||
|
||||
This example code shows how to interface the Raspberry Pi Pico to the MMA8451 digital accelerometer sensor board.
|
||||
|
||||
This example reads and displays the acceleration values of the board in the 3 axis. It also allows the user to set the trade-off between the range and precision based on the values they require. Values often have an offset which can be accounted for by writing to the offset correction registers. The datasheet for the sensor can be found at https://cdn-shop.adafruit.com/datasheets/MMA8451Q-1.pdf for additional information.
|
||||
|
||||
== Wiring information
|
||||
|
||||
Wiring up the device requires 4 jumpers, to connect VIN, GND, SDA and SCL. The example here uses I2C port 0, which is assigned to GPIO 4 (SDA) and 5 (SCL) in software. Power is supplied from the VSYS pin.
|
||||
|
||||
|
||||
|
||||
[[mma8451_i2c_wiring]]
|
||||
[pdfwidth=75%]
|
||||
.Wiring Diagram for MMA8451.
|
||||
image::mma8451_i2c.png[]
|
||||
|
||||
== List of Files
|
||||
|
||||
CMakeLists.txt:: CMake file to incorporate the example in to the examples build tree.
|
||||
mma8451_i2c.c:: The example code.
|
||||
|
||||
== Bill of Materials
|
||||
|
||||
.A list of materials required for the example
|
||||
[[mma8451-bom-table]]
|
||||
[cols=3]
|
||||
|===
|
||||
| *Item* | *Quantity* | Details
|
||||
| Breadboard | 1 | generic part
|
||||
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
|
||||
| MMA8451 board| 1 | https://www.adafruit.com/product/2019
|
||||
| M/M Jumper wires | 4 | generic part
|
||||
|===
|
||||
|
||||
|
||||
@ -1,128 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/i2c.h"
|
||||
|
||||
/* Example code to talk to a MMA8451 triple-axis accelerometer.
|
||||
|
||||
This reads and writes to registers on the board.
|
||||
|
||||
Connections on Raspberry Pi Pico board, other boards may vary.
|
||||
|
||||
GPIO PICO_DEFAULT_I2C_SDA_PIN (On Pico this is GP4 (physical pin 6)) -> SDA on MMA8451 board
|
||||
GPIO PICO_DEFAULT_I2C_SCK_PIN (On Pico this is GP5 (physcial pin 7)) -> SCL on MMA8451 board
|
||||
VSYS (physical pin 39) -> VDD on MMA8451 board
|
||||
GND (physical pin 38) -> GND on MMA8451 board
|
||||
|
||||
*/
|
||||
|
||||
const uint8_t ADDRESS = 0x1D;
|
||||
|
||||
//hardware registers
|
||||
|
||||
const uint8_t REG_X_MSB = 0x01;
|
||||
const uint8_t REG_X_LSB = 0x02;
|
||||
const uint8_t REG_Y_MSB = 0x03;
|
||||
const uint8_t REG_Y_LSB = 0x04;
|
||||
const uint8_t REG_Z_MSB = 0x05;
|
||||
const uint8_t REG_Z_LSB = 0x06;
|
||||
const uint8_t REG_DATA_CFG = 0x0E;
|
||||
const uint8_t REG_CTRL_REG1 = 0x2A;
|
||||
|
||||
// Set the range and precision for the data
|
||||
const uint8_t range_config = 0x01; // 0x00 for ±2g, 0x01 for ±4g, 0x02 for ±8g
|
||||
const float count = 2048; // 4096 for ±2g, 2048 for ±4g, 1024 for ±8g
|
||||
|
||||
uint8_t buf[2];
|
||||
|
||||
float mma8451_convert_accel(uint16_t raw_accel) {
|
||||
float acceleration;
|
||||
// Acceleration is read as a multiple of g (gravitational acceleration on the Earth's surface)
|
||||
// Check if acceleration < 0 and convert to decimal accordingly
|
||||
if ((raw_accel & 0x2000) == 0x2000) {
|
||||
raw_accel &= 0x1FFF;
|
||||
acceleration = (-8192 + (float) raw_accel) / count;
|
||||
} else {
|
||||
acceleration = (float) raw_accel / count;
|
||||
}
|
||||
acceleration *= 9.81f;
|
||||
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();
|
||||
|
||||
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
|
||||
#warning i2c/mma8451_i2c example requires a board with I2C pins
|
||||
puts("Default I2C pins were not defined");
|
||||
#else
|
||||
printf("Hello, MMA8451! Reading raw data from registers...\n");
|
||||
|
||||
// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
|
||||
i2c_init(i2c_default, 400 * 1000);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
|
||||
gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
|
||||
gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
|
||||
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));
|
||||
|
||||
float x_acceleration;
|
||||
float y_acceleration;
|
||||
float z_acceleration;
|
||||
|
||||
// Enable standby mode
|
||||
mma8451_set_state(0x00);
|
||||
|
||||
// Edit configuration while in standby mode
|
||||
buf[0] = REG_DATA_CFG;
|
||||
buf[1] = range_config;
|
||||
i2c_write_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
|
||||
// Enable active mode
|
||||
mma8451_set_state(0x01);
|
||||
|
||||
while (1) {
|
||||
|
||||
// Start reading acceleration registers for 2 bytes
|
||||
i2c_write_blocking(i2c_default, ADDRESS, ®_X_MSB, 1, true);
|
||||
i2c_read_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
x_acceleration = mma8451_convert_accel(buf[0] << 6 | buf[1] >> 2);
|
||||
|
||||
i2c_write_blocking(i2c_default, ADDRESS, ®_Y_MSB, 1, true);
|
||||
i2c_read_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
y_acceleration = mma8451_convert_accel(buf[0] << 6 | buf[1] >> 2);
|
||||
|
||||
i2c_write_blocking(i2c_default, ADDRESS, ®_Z_MSB, 1, true);
|
||||
i2c_read_blocking(i2c_default, ADDRESS, buf, 2, false);
|
||||
z_acceleration = mma8451_convert_accel(buf[0] << 6 | buf[1] >> 2);
|
||||
|
||||
// Display acceleration values
|
||||
printf("ACCELERATION VALUES: \n");
|
||||
printf("X acceleration: %.6fms^-2\n", x_acceleration);
|
||||
printf("Y acceleration: %.6fms^-2\n", y_acceleration);
|
||||
printf("Z acceleration: %.6fms^-2\n", z_acceleration);
|
||||
|
||||
sleep_ms(500);
|
||||
|
||||
// Clear terminal
|
||||
printf("\e[1;1H\e[2J");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||