roo_display
API Documentation for roo_display
Loading...
Searching...
No Matches
spi_dma.h
Go to the documentation of this file.
1#pragma once
2
3#include <memory>
4
5#include "freertos/FreeRTOS.h"
6#include "freertos/task.h"
7#include "hal/spi_types.h"
9
10#if __has_include("esp_private/spi_common_internal.h")
11#include "esp_private/spi_common_internal.h"
12#define ROO_DISPLAY_HAS_SPI_INTERNAL_DMA 1
13#else
14#define ROO_DISPLAY_HAS_SPI_INTERNAL_DMA 0
15#endif
16
17namespace roo_display {
18namespace esp32 {
19
20template <typename T>
21class RingBuf {
22 public:
24 : buf_(new T[capacity]), capacity_(capacity), head_(0), used_(0) {}
25
26 size_t capacity() const { return capacity_; }
27 size_t used() const { return used_; }
28 size_t free() const { return capacity_ - used_; }
29
30 bool full() const { return used_ == capacity_; }
31 bool empty() const { return used_ == 0; }
32
33 void clear() {
34 head_ = 0;
35 used_ = 0;
36 }
37
38 // Writes a single item to the ringbuffer. Returns true if successful, or
39 // false if the buffer is full.
40 bool write(T item) {
41 if (used_ == capacity_) return false;
42 buf_[write_pos()] = std::move(item);
43 used_++;
44 return true;
45 }
46
47 // Reads a single item from the ringbuffer. Returns true if successful, or
48 // false if the buffer is empty.
49 bool read(T& item) {
50 if (used_ == 0) return false;
51 item = std::move(buf_[head_]);
52 head_++;
53 if (head_ == capacity_) head_ = 0;
54 used_--;
55 return true;
56 }
57
58 private:
59 size_t write_pos() const {
60 size_t pos = head_ + used_;
61 if (pos >= capacity_) pos -= capacity_;
62 return pos;
63 }
64
65 std::unique_ptr<T[]> buf_;
66 size_t capacity_;
67 size_t head_;
68 size_t used_;
69};
70
72 public:
73 struct Operation {
74 const roo::byte* out_data;
75 size_t out_len;
76 };
77
79
80 // Initializes the DMA controller. This method must be called before any other
81 // method. The caller must ensure that the dma_buffer_pool remains valid for
82 // the entire duration of use of the DmaController.
83 void begin();
84
85 // Deinitializes the DMA controller. After this method returns, the caller
86 // must not call any other method. The caller must ensure that there are no
87 // pending DMA operations before calling this method.
88 void end();
89
90 // Submits a DMA operation. The caller must ensure that the data buffer
91 // remains valid until the operation completes. The caller must ensure that
92 // the out_len is aligned to 32 bits.
93 //
94 // If there is no pending DMA operation, the submitted operation gets started
95 // immediately. Otherwise, the submitted operation gets enqueued and will
96 // start when all previously submitted operations complete. The caller can
97 // submit multiple operations in a row, and they will get executed in order.
98 // It is the caller's responsibility to ensure that the total number of
99 // pending operations does not exceed the capacity of the dma_buffer_pool_.
100 //
101 // If the DMA operation fails to start, this method returns false.
102 bool submit(Operation op);
103
104 // Blocks until all previously submitted operations complete. If there are no
105 // pending operations, returns immediately. Only one task can wait for
106 // completion at a time; if multiple tasks call this method concurrently, the
107 // behavior is undefined.
108 void awaitCompleted();
109
110 private:
111 // The DMA ISR callback.
112 friend void IRAM_ATTR DmaTransferCompleteISR(void*);
113
114 // Called from the SPI ISR when a DMA operation completes. This method marks
115 // the current operation as complete, returns the buffer to the pool, and
116 // starts the next pending operation, if any. If there are no more operations,
117 // the potential waiter gets notified. This method must not be called directly
118 // from application code.
119 void transferCompleteISR();
120
121#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
122 // Starts a DMA operation. Must be called while holding mux_ (task or ISR
123 // critical section variant).
125#endif
126
127 // SPI host this controller is bound to.
128 spi_host_device_t host_id_;
129
130#if ROO_DISPLAY_HAS_SPI_INTERNAL_DMA
131 // Active DMA context used for transfers; may be borrowed from SPI bus or
132 // point to owned_dma_ctx_.
133 const spi_dma_ctx_t* dma_ctx_;
134
135 // DMA context allocated by this controller (if bus-level context is absent,
136 // e.g. in case of Arduino SPI). Freed in end(); null when dma_ctx_ is
137 // borrowed.
139
140 // Handle returned by esp_intr_alloc for the SPI host interrupt.
141 // Guarded by mux_.
143
144 // True while a DMA transfer is currently active on the peripheral.
145 // Guarded by mux_.
147
148 // Task currently blocked in awaitCompleted(), if any.
149 // Guarded by mux_.
151
152 // Operation currently being transferred by DMA.
153 // Guarded by mux_.
155
156 // Protects ISR/task shared state above.
157 portMUX_TYPE mux_;
158#endif
159
160 // Buffer pool backing operation payload storage.
161 DmaBufferPool& dma_buffer_pool_;
162
163 // FIFO of submitted operations waiting to start.
164 // Guarded by mux_.
165 RingBuf<Operation> pending_ops_;
166};
167
168} // namespace esp32
169} // namespace roo_display
friend void IRAM_ATTR DmaTransferCompleteISR(void *)
Definition spi_dma.cpp:26
RingBuf(size_t capacity)
Definition spi_dma.h:23
size_t capacity() const
Definition spi_dma.h:26
size_t used() const
Definition spi_dma.h:27
size_t free() const
Definition spi_dma.h:28
Defines 140 opaque HTML named colors.