Skip to content

Commit 9a82398

Browse files
mjcrossmartin
and
martin
authored
Add pio onewire example (#334)
* Adds PIO onewire example Co-authored-by: martin <[email protected]>
1 parent 56b4522 commit 9a82398

12 files changed

+434
-1
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,14 @@ App|Description
216216
---|---
217217
[hello_pio](pio/hello_pio) | Absolutely minimal example showing how to control an LED by pushing values into a PIO FIFO.
218218
[apa102](pio/apa102) | Rainbow pattern on on a string of APA102 addressable RGB LEDs.
219+
[clocked_input](pio/clocked_input) | Shift in serial data, sampling with an external clock.
219220
[differential_manchester](pio/differential_manchester) | Send and receive differential Manchester-encoded serial (BMC).
220221
[hub75](pio/hub75) | Display an image on a 128x64 HUB75 RGB LED matrix.
221222
[i2c](pio/i2c) | Scan an I2C bus.
222223
[ir_nec](pio/ir_nec) | Sending and receiving IR (infra-red) codes using the PIO.
223224
[logic_analyser](pio/logic_analyser) | Use PIO and DMA to capture a logic trace of some GPIOs, whilst a PWM unit is driving them.
224225
[manchester_encoding](pio/manchester_encoding) | Send and receive Manchester-encoded serial.
226+
[onewire](pio/onewire)| A library for interfacing to 1-Wire devices, with an example for the DS18B20 temperature sensor.
225227
[pio_blink](pio/pio_blink) | Set up some PIO state machines to blink LEDs at different frequencies, according to delay counts pushed into their FIFOs.
226228
[pwm](pio/pwm) | Pulse width modulation on PIO. Use it to gradually fade the brightness of an LED.
227229
[spi](pio/spi) | Use PIO to erase, program and read an external SPI flash chip. A second example runs a loopback test with all four CPHA/CPOL combinations.
@@ -232,7 +234,6 @@ App|Description
232234
[uart_tx](pio/uart_tx) | Implement the transmit component of a UART serial port, and print hello world.
233235
[ws2812](pio/ws2812) | Examples of driving WS2812 addressable RGB LEDs.
234236
[addition](pio/addition) | Add two integers together using PIO. Only around 8 billion times slower than Cortex-M0+.
235-
[clocked_input](pio/clocked_input) | Shift in serial data, sampling with an external clock.
236237

237238
### PWM
238239

pio/onewire/CMakeLists.txt

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
add_executable(pio_onewire)
2+
3+
target_sources(pio_onewire PRIVATE onewire.c)
4+
5+
add_subdirectory(onewire_library)
6+
7+
target_link_libraries(pio_onewire PRIVATE
8+
pico_stdlib
9+
hardware_pio
10+
onewire_library)
11+
12+
pico_add_extra_outputs(pio_onewire)
13+
14+
# add url via pico_set_program_url
15+
example_auto_set_url(pio_onewire)

pio/onewire/README.adoc

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
= Interfacing 1-Wire devices to the Pico
2+
3+
This example demonstrates how to use link:https://www.analog.com/en/technical-articles/guide-to-1wire-communication.html[1-Wire] devices with the Raspberry Pi Pico (RP2040).
4+
1-Wire is an interface that enables a master to control several slave devices over a simple shared serial bus.
5+
6+
The example provides a 1-Wire library that is used to take readings from a set of connected link:https://www.analog.com/media/en/technical-documentation/data-sheets/ds18b20.pdf[DS18B20] 1-Wire temperature sensors. The results are sent to the default serial terminal connected via USB or UART as configured in the SDK.
7+
8+
The library uses a driver based on the RP2040 PIO state machine to generate accurate bus timings and control the 1-Wire bus via a GPIO pin.
9+
10+
_1-Wire(R) is a registered trademark of Maxim Integrated Products, Inc._
11+
12+
== Wiring information
13+
14+
Connect one or more DS18B20 sensors to the Pico as shown in the diagram and table below.
15+
16+
Connect GPIO 15 to 3V3(OUT) with a pull-up resistor of about 4k ohms.
17+
18+
[[pio_onewire_wiring-diagram]]
19+
[pdfwidth=75%]
20+
.Wiring diagram
21+
image::pio_onewire.png[]
22+
23+
[[pio_onewire_connections-table]]
24+
.Connections table
25+
[options="header,footer"]
26+
|==================================================
27+
|Pico |pin |DS18B20 |pin / sensor wire
28+
|GND |38 or equivalent |GND |1 / Black
29+
|GPIO 15 |20 |DQ |2 / Yellow
30+
|3V3(OUT)|36 |VDD |3 / Red
31+
|==================================================
32+
33+
== Bill of materials
34+
35+
.A list of materials for the example circuit
36+
[[pio_onewire_bom-table]]
37+
[cols=3]
38+
|===
39+
| *Item* | *Quantity* | *Details*
40+
| Breadboard | 1 | generic part
41+
| Raspberry Pi Pico | 1 | https://www.raspberrypi.com/products/raspberry-pi-pico/
42+
| DS18B20 | 3 | chip or wired sensor
43+
| 3900 ohm resistor | 1 | generic part
44+
| M/M jumper wire | 13 | generic part
45+
|===
46+
47+
== List of files
48+
[[pio_onewire_file-list]]
49+
CMakeLists.txt:: CMake file to incorporate the example in the build tree.
50+
onewire.c:: Source code for the example program.
51+
ow_rom.h:: Header file with generic ROM command codes for 1-Wire devices.
52+
ds18b20.h:: Header file with function command codes for the DS18B20 device.
53+
onewire_library/:: Subdirectory containing the 1-Wire library and driver.
54+
onewire_library/CMakeLists.txt:: CMake file to build the 1-Wire library and driver.
55+
onewire_library/onewire_library.c:: Source code for the 1-Wire user functions.
56+
onewire_library/onewire_library.h:: Header file for the 1-Wire user functions and types.
57+
onewire_library/onewire_library.pio:: PIO assembly code for the 1-Wire driver.

pio/onewire/ds18b20.h

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Function commands for d218b20 1-Wire temperature sensor
2+
// https://www.analog.com/en/products/ds18b20.html
3+
//
4+
#define DS18B20_CONVERT_T 0x44
5+
#define DS18B20_WRITE_SCRATCHPAD 0x4e
6+
#define DS18B20_READ_SCRATCHPAD 0xbe
7+
#define DS18B20_COPY_SCRATCHPAD 0x48
8+
#define DS18B20_RECALL_EE 0xb8
9+
#define DS18B20_READ_POWER_SUPPLY 0xb4

pio/onewire/onewire.c

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Copyright (c) 2023 mjcross
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
**/
6+
7+
#include <stdio.h>
8+
#include "pico/stdlib.h"
9+
#include "pico/binary_info.h"
10+
11+
#include "onewire_library.h" // onewire library functions
12+
#include "ow_rom.h" // onewire ROM command codes
13+
#include "ds18b20.h" // ds18b20 function codes
14+
15+
// Demonstrates the PIO onewire driver by taking readings from a set of
16+
// ds18b20 1-wire temperature sensors.
17+
18+
int main() {
19+
stdio_init_all();
20+
21+
PIO pio = pio0;
22+
uint gpio = 15;
23+
24+
OW ow;
25+
uint offset;
26+
// add the program to the PIO shared address space
27+
if (pio_can_add_program (pio, &onewire_program)) {
28+
offset = pio_add_program (pio, &onewire_program);
29+
30+
// claim a state machine and initialise a driver instance
31+
if (ow_init (&ow, pio, offset, gpio)) {
32+
33+
// find and display 64-bit device addresses
34+
int maxdevs = 10;
35+
uint64_t romcode[maxdevs];
36+
int num_devs = ow_romsearch (&ow, romcode, maxdevs, OW_SEARCH_ROM);
37+
38+
printf("Found %d devices\n", num_devs);
39+
for (int i = 0; i < num_devs; i += 1) {
40+
printf("\t%d: 0x%llx\n", i, romcode[i]);
41+
}
42+
putchar ('\n');
43+
44+
while (num_devs > 0) {
45+
// start temperature conversion in parallel on all devices
46+
// (see ds18b20 datasheet)
47+
ow_reset (&ow);
48+
ow_send (&ow, OW_SKIP_ROM);
49+
ow_send (&ow, DS18B20_CONVERT_T);
50+
51+
// wait for the conversions to finish
52+
while (ow_read(&ow) == 0);
53+
54+
// read the result from each device
55+
for (int i = 0; i < num_devs; i += 1) {
56+
ow_reset (&ow);
57+
ow_send (&ow, OW_MATCH_ROM);
58+
for (int b = 0; b < 64; b += 8) {
59+
ow_send (&ow, romcode[i] >> b);
60+
}
61+
ow_send (&ow, DS18B20_READ_SCRATCHPAD);
62+
int16_t temp = 0;
63+
temp = ow_read (&ow) | (ow_read (&ow) << 8);
64+
printf ("\t%d: %f", i, temp / 16.0);
65+
}
66+
putchar ('\n');
67+
}
68+
69+
} else {
70+
puts ("could not initialise the driver");
71+
}
72+
} else {
73+
puts ("could not add the program");
74+
}
75+
76+
while(true);
77+
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
add_library(onewire_library INTERFACE)
2+
target_sources(onewire_library onewire_library.c)
3+
4+
# invoke pio_asm to assemble the state machine programs
5+
#
6+
pico_generate_pio_header(onewire_library ${CMAKE_CURRENT_LIST_DIR}/onewire_library.pio)
7+
8+
target_link_libraries(onewire_library INTERFACE
9+
pico_stdlib
10+
hardware_pio
11+
)
12+
13+
# add the `binary` directory so that the generated headers are included in the project
14+
#
15+
target_include_directories(onewire_library INTERFACE
16+
${CMAKE_CURRENT_SOURCE_DIR}
17+
${CMAKE_CURRENT_BINARY_DIR}
18+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Copyright (c) 2023 mjcross
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
**/
6+
7+
#include "pico/stdlib.h"
8+
#include "hardware/gpio.h"
9+
#include "hardware/pio.h"
10+
11+
#include "onewire_library.h"
12+
13+
14+
// Create a driver instance and populate the provided OW structure.
15+
// Returns: True on success.
16+
// ow: A pointer to a blank OW structure to hold the driver parameters.
17+
// pio: The PIO hardware instance to use.
18+
// offset: The location of the onewire program in the PIO shared address space.
19+
// gpio: The pin to use for the bus (NB: see the README).
20+
bool ow_init (OW *ow, PIO pio, uint offset, uint gpio) {
21+
int sm = pio_claim_unused_sm (pio, false);
22+
if (sm == -1) {
23+
return false;
24+
}
25+
gpio_init (gpio); // enable the gpio and clear any output value
26+
pio_gpio_init (pio, gpio); // set the function to PIO output
27+
ow->gpio = gpio;
28+
ow->pio = pio;
29+
ow->offset = offset;
30+
ow->sm = (uint)sm;
31+
ow->jmp_reset = onewire_reset_instr (ow->offset); // assemble the bus reset instruction
32+
onewire_sm_init (ow->pio, ow->sm, ow->offset, ow->gpio, 8); // set 8 bits per word
33+
return true;
34+
}
35+
36+
37+
// Send a binary word on the bus (LSB first).
38+
// ow: A pointer to an OW driver struct.
39+
// data: The word to be sent.
40+
void ow_send (OW *ow, uint data) {
41+
pio_sm_put_blocking (ow->pio, ow->sm, (uint32_t)data);
42+
pio_sm_get_blocking (ow->pio, ow->sm); // discard the response
43+
}
44+
45+
46+
// Read a binary word from the bus.
47+
// Returns: the word read (LSB first).
48+
// ow: pointer to an OW driver struct
49+
uint8_t ow_read (OW *ow) {
50+
pio_sm_put_blocking (ow->pio, ow->sm, 0xff); // generate read slots
51+
return (uint8_t)(pio_sm_get_blocking (ow->pio, ow->sm) >> 24); // shift response into bits 0..7
52+
}
53+
54+
55+
// Reset the bus and detect any connected slaves.
56+
// Returns: true if any slaves responded.
57+
// ow: pointer to an OW driver struct
58+
bool ow_reset (OW *ow) {
59+
pio_sm_exec_wait_blocking (ow->pio, ow->sm, ow->jmp_reset);
60+
if ((pio_sm_get_blocking (ow->pio, ow->sm) & 1) == 0) { // apply pin mask (see pio program)
61+
return true; // a slave pulled the bus low
62+
}
63+
return false;
64+
}
65+
66+
67+
// Find ROM codes (64-bit hardware addresses) of all connected devices.
68+
// See https://www.analog.com/en/app-notes/1wire-search-algorithm.html
69+
// Returns: the number of devices found (up to maxdevs) or -1 if an error occurrred.
70+
// ow: pointer to an OW driver struct
71+
// romcodes: location at which store the addresses (NULL means don't store)
72+
// maxdevs: maximum number of devices to find (0 means no limit)
73+
// command: 1-Wire search command (e.g. OW_SEARCHROM or OW_ALARM_SEARCH)
74+
int ow_romsearch (OW *ow, uint64_t *romcodes, int maxdevs, uint command) {
75+
int index;
76+
uint64_t romcode = 0ull;
77+
int branch_point;
78+
int next_branch_point = -1;
79+
int num_found = 0;
80+
bool finished = false;
81+
82+
onewire_sm_init (ow->pio, ow->sm, ow->offset, ow->gpio, 1); // set driver to 1-bit mode
83+
84+
while (finished == false && (maxdevs == 0 || num_found < maxdevs )) {
85+
finished = true;
86+
branch_point = next_branch_point;
87+
if (ow_reset (ow) == false) {
88+
num_found = 0; // no slaves present
89+
finished = true;
90+
break;
91+
}
92+
for (int i = 0; i < 8; i += 1) { // send search command as single bits
93+
ow_send (ow, command >> i);
94+
}
95+
for (index = 0; index < 64; index += 1) { // determine romcode bits 0..63 (see ref)
96+
uint a = ow_read (ow);
97+
uint b = ow_read (ow);
98+
if (a == 0 && b == 0) { // (a, b) = (0, 0)
99+
if (index == branch_point) {
100+
ow_send (ow, 1);
101+
romcode |= (1ull << index);
102+
} else {
103+
if (index > branch_point || (romcode & (1ull << index)) == 0) {
104+
ow_send(ow, 0);
105+
finished = false;
106+
romcode &= ~(1ull << index);
107+
next_branch_point = index;
108+
} else { // index < branch_point or romcode[index] = 1
109+
ow_send (ow, 1);
110+
}
111+
}
112+
} else if (a != 0 && b != 0) { // (a, b) = (1, 1) error (e.g. device disconnected)
113+
num_found = -2; // function will return -1
114+
finished = true;
115+
break; // terminate for loop
116+
} else {
117+
if (a == 0) { // (a, b) = (0, 1) or (1, 0)
118+
ow_send (ow, 0);
119+
romcode &= ~(1ull << index);
120+
} else {
121+
ow_send (ow, 1);
122+
romcode |= (1ull << index);
123+
}
124+
}
125+
} // end of for loop
126+
127+
if (romcodes != NULL) {
128+
romcodes[num_found] = romcode; // store the romcode
129+
}
130+
num_found += 1;
131+
} // end of while loop
132+
133+
onewire_sm_init (ow->pio, ow->sm, ow->offset, ow->gpio, 8); // restore 8-bit mode
134+
return num_found;
135+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "hardware/pio.h"
2+
#include "hardware/clocks.h" // for clock_get_hz() in generated header
3+
#include "onewire_library.pio.h" // generated by pioasm
4+
5+
typedef struct {
6+
PIO pio;
7+
uint sm;
8+
uint jmp_reset;
9+
int offset;
10+
int gpio;
11+
} OW;
12+
13+
bool ow_init (OW *ow, PIO pio, uint offset, uint gpio);
14+
void ow_send (OW *ow, uint data);
15+
uint8_t ow_read (OW *ow);
16+
bool ow_reset (OW *ow);
17+
int ow_romsearch (OW *ow, uint64_t *romcodes, int maxdevs, uint command);

0 commit comments

Comments
 (0)