forked from raspberrypi/pico-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
uart_rx_intr.c
181 lines (154 loc) · 6.31 KB
/
uart_rx_intr.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/**
* Copyright (c) 2023 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdio.h>
#include <stdlib.h>
#include "pico/stdlib.h"
#include "pico/multicore.h"
#include "pico/util/queue.h"
#include "pico/async_context_threadsafe_background.h"
#include "hardware/pio.h"
#include "hardware/uart.h"
#include "uart_rx.pio.h"
// This program
// - Uses UART1 (the spare UART, by default) to transmit some text
// - Uses a PIO state machine to receive that text
// - Use an interrupt to determine when the PIO FIFO has some data
// - Saves characters in a queue
// - Uses an async context to perform work when notified by the irq
// - Prints out the received text to the default console (UART0)
// This might require some reconfiguration on boards where UART1 is the
// default UART.
#define SERIAL_BAUD PICO_DEFAULT_UART_BAUD_RATE
#define HARD_UART_INST uart1
// You'll need a wire from GPIO4 -> GPIO3
#define HARD_UART_TX_PIN 4
#define PIO_RX_PIN 3
#define FIFO_SIZE 64
#define MAX_COUNTER 10
static PIO pio;
static uint sm;
static int8_t pio_irq;
static queue_t fifo;
static uint offset;
static uint32_t counter;
static bool work_done;
// Ask core 1 to print a string, to make things easier on core 0
static void core1_main() {
while(counter < MAX_COUNTER) {
sleep_ms(1000 + (rand() % 1000));
static char text[64];
sprintf(text, "Hello, world from PIO with interrupts! %u\n", counter++);
uart_puts(HARD_UART_INST, text);
}
}
static void async_worker_func(async_context_t *async_context, async_when_pending_worker_t *worker);
// An async context is notified by the irq to "do some work"
static async_context_threadsafe_background_t async_context;
static async_when_pending_worker_t worker = { .do_work = async_worker_func };
// IRQ called when the pio fifo is not empty, i.e. there are some characters on the uart
// This needs to run as quickly as possible or else you will lose characters (in particular don't printf!)
static void pio_irq_func(void) {
while(!pio_sm_is_rx_fifo_empty(pio, sm)) {
char c = uart_rx_program_getc(pio, sm);
if (!queue_try_add(&fifo, &c)) {
panic("fifo full");
}
}
// Tell the async worker that there are some characters waiting for us
async_context_set_work_pending(&async_context.core, &worker);
}
// Process characters
static void async_worker_func(__unused async_context_t *async_context, __unused async_when_pending_worker_t *worker) {
work_done = true;
while(!queue_is_empty(&fifo)) {
char c;
if (!queue_try_remove(&fifo, &c)) {
panic("fifo empty");
}
putchar(c); // Display character in the console
}
}
// Find a free pio and state machine and load the program into it.
// Returns false if this fails
static bool init_pio(const pio_program_t *program, PIO *pio_hw, uint *sm, uint *offset) {
// Find a free pio
*pio_hw = pio1;
if (!pio_can_add_program(*pio_hw, program)) {
*pio_hw = pio0;
if (!pio_can_add_program(*pio_hw, program)) {
*offset = -1;
return false;
}
}
*offset = pio_add_program(*pio_hw, program);
// Find a state machine
*sm = (int8_t)pio_claim_unused_sm(*pio_hw, false);
if (*sm < 0) {
return false;
}
return true;
}
int main() {
// Console output (also a UART, yes it's confusing)
setup_default_uart();
printf("Starting PIO UART RX interrupt example\n");
// Set up the hard UART we're going to use to print characters
uart_init(HARD_UART_INST, SERIAL_BAUD);
gpio_set_function(HARD_UART_TX_PIN, GPIO_FUNC_UART);
// create a queue so the irq can save the data somewhere
queue_init(&fifo, 1, FIFO_SIZE);
// Setup an async context and worker to perform work when needed
if (!async_context_threadsafe_background_init_with_defaults(&async_context)) {
panic("failed to setup context");
}
async_context_add_when_pending_worker(&async_context.core, &worker);
// Set up the state machine we're going to use to receive them.
// In real code you need to find a free pio and state machine in case pio resources are used elsewhere
if (!init_pio(&uart_rx_program, &pio, &sm, &offset)) {
panic("failed to setup pio");
}
uart_rx_program_init(pio, sm, offset, PIO_RX_PIN, SERIAL_BAUD);
// Find a free irq
static_assert(PIO0_IRQ_1 == PIO0_IRQ_0 + 1 && PIO1_IRQ_1 == PIO1_IRQ_0 + 1, "");
pio_irq = (pio == pio0) ? PIO0_IRQ_0 : PIO1_IRQ_0;
if (irq_get_exclusive_handler(pio_irq)) {
pio_irq++;
if (irq_get_exclusive_handler(pio_irq)) {
panic("All IRQs are in use");
}
}
// Enable interrupt
irq_add_shared_handler(pio_irq, pio_irq_func, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); // Add a shared IRQ handler
irq_set_enabled(pio_irq, true); // Enable the IRQ
const uint irq_index = pio_irq - ((pio == pio0) ? PIO0_IRQ_0 : PIO1_IRQ_0); // Get index of the IRQ
pio_set_irqn_source_enabled(pio, irq_index, pis_sm0_rx_fifo_not_empty + sm, true); // Set pio to tell us when the FIFO is NOT empty
// Tell core 1 to print text to uart1
multicore_launch_core1(core1_main);
// Echo characters received from PIO to the console
while (counter < MAX_COUNTER || work_done) {
// Note that we could just sleep here as we're using "threadsafe_background" that uses a low priority interrupt
// But if we changed to use a "polling" context that wouldn't work. The following works for both types of context.
// When using "threadsafe_background" the poll does nothing. This loop is just preventing main from exiting!
work_done = false;
async_context_poll(&async_context.core);
async_context_wait_for_work_ms(&async_context.core, 2000);
}
// Disable interrupt
pio_set_irqn_source_enabled(pio, irq_index, pis_sm0_rx_fifo_not_empty + sm, false);
irq_set_enabled(pio_irq, false);
irq_remove_handler(pio_irq, pio_irq_func);
// Cleanup pio
pio_sm_set_enabled(pio, sm, false);
pio_remove_program(pio, &uart_rx_program, offset);
pio_sm_unclaim(pio, sm);
async_context_remove_when_pending_worker(&async_context.core, &worker);
async_context_deinit(&async_context.core);
queue_free(&fifo);
uart_deinit(HARD_UART_INST);
printf("Test complete\n");
sleep_ms(100);
return 0;
}