Firmware SDK
twr_dma.c
1 #include <twr_dma.h>
2 #include <twr_irq.h>
3 #include <twr_scheduler.h>
4 #include <twr_fifo.h>
5 #include <stm32l0xx.h>
6 
7 #define _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(__CHANNEL) \
8  if ((DMA1->ISR & DMA_ISR_GIF##__CHANNEL) != 0) \
9  { \
10  if ((DMA1->ISR & DMA_ISR_TEIF##__CHANNEL) != 0) \
11  { \
12  _twr_dma_irq_handler(TWR_DMA_CHANNEL_##__CHANNEL, TWR_DMA_EVENT_ERROR); \
13  } \
14  else if ((DMA1->ISR & DMA_ISR_HTIF##__CHANNEL) != 0) \
15  { \
16  _twr_dma_irq_handler(TWR_DMA_CHANNEL_##__CHANNEL, TWR_DMA_EVENT_HALF_DONE); \
17  DMA1->IFCR |= DMA_IFCR_CHTIF##__CHANNEL; \
18  } \
19  else if ((DMA1->ISR & DMA_ISR_TCIF##__CHANNEL) != 0) \
20  { \
21  _twr_dma_irq_handler(TWR_DMA_CHANNEL_##__CHANNEL, TWR_DMA_EVENT_DONE); \
22  DMA1->IFCR |= DMA_IFCR_CTCIF##__CHANNEL; \
23  } \
24  }
25 
26 typedef struct
27 {
28  uint8_t channel : 4;
29  uint8_t event : 4;
30 
32 
33 static twr_dma_pending_event_t _twr_dma_pending_event_buffer[2 * 7 * sizeof(twr_dma_pending_event_t)];
34 
35 static struct
36 {
37  bool is_initialized;
38 
39  struct
40  {
41  DMA_Channel_TypeDef *instance;
42  void (*event_handler)(twr_dma_channel_t, twr_dma_event_t, void *);
43  void *event_param;
44 
45  } channel[7];
46 
47  twr_fifo_t fifo_pending;
49 
50 } _twr_dma;
51 
52 static void _twr_dma_task(void *param);
53 
54 static void _twr_dma_irq_handler(twr_dma_channel_t channel, twr_dma_event_t event);
55 
56 void twr_dma_init(void)
57 {
58  if (_twr_dma.is_initialized)
59  {
60  return;
61  }
62 
63  // Initialize channel instances
64  _twr_dma.channel[TWR_DMA_CHANNEL_1].instance = DMA1_Channel1;
65  _twr_dma.channel[TWR_DMA_CHANNEL_2].instance = DMA1_Channel2;
66  _twr_dma.channel[TWR_DMA_CHANNEL_3].instance = DMA1_Channel3;
67  _twr_dma.channel[TWR_DMA_CHANNEL_4].instance = DMA1_Channel4;
68  _twr_dma.channel[TWR_DMA_CHANNEL_5].instance = DMA1_Channel5;
69  _twr_dma.channel[TWR_DMA_CHANNEL_6].instance = DMA1_Channel6;
70  _twr_dma.channel[TWR_DMA_CHANNEL_7].instance = DMA1_Channel7;
71 
72  twr_fifo_init(&_twr_dma.fifo_pending, _twr_dma_pending_event_buffer, sizeof(_twr_dma_pending_event_buffer));
73 
74  _twr_dma.task_id = twr_scheduler_register(_twr_dma_task, NULL, TWR_TICK_INFINITY);
75 
76  // Enable DMA1
77  RCC->AHBENR |= RCC_AHBENR_DMA1EN;
78 
79  // Errata workaround
80  RCC->AHBENR;
81 
82  // Enable DMA 1 channel 1 interrupt
83  NVIC_SetPriority(DMA1_Channel1_IRQn, 0);
84  NVIC_EnableIRQ(DMA1_Channel1_IRQn);
85 
86  // Enable DMA 1 channel 2 and 3 interrupts
87  NVIC_SetPriority(DMA1_Channel2_3_IRQn, 1);
88  NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
89 
90  // Enable DMA 1 channel 4, 5, 6 and 7 interrupts
91  NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2);
92  NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
93 }
94 
96 {
97  DMA_Channel_TypeDef *dma_channel = _twr_dma.channel[channel].instance;
98 
99  uint32_t dma_cselr_pos = channel * 4;
100 
101  twr_irq_disable();
102 
103  // Set DMA direction
105  {
106  dma_channel->CCR |= DMA_CCR_DIR;
107  }
108  else
109  {
110  dma_channel->CCR &= ~DMA_CCR_DIR;
111  }
112 
113  // Set memory data size
114  dma_channel->CCR &= ~DMA_CCR_MSIZE_Msk;
115 
116  if (config->data_size_memory == TWR_DMA_SIZE_2)
117  {
118  dma_channel->CCR |= DMA_CCR_MSIZE_0;
119  }
120  else if (config->data_size_memory == TWR_DMA_SIZE_4)
121  {
122  dma_channel->CCR |= DMA_CCR_MSIZE_1;
123  }
124 
125  // Set peripheral data size
126  dma_channel->CCR &= ~DMA_CCR_PSIZE_Msk;
127 
128  if (config->data_size_peripheral == TWR_DMA_SIZE_2)
129  {
130  dma_channel->CCR |= DMA_CCR_PSIZE_0;
131  }
132  else if (config->data_size_peripheral == TWR_DMA_SIZE_4)
133  {
134  dma_channel->CCR |= DMA_CCR_PSIZE_1;
135  }
136 
137  // Set DMA mode
138  if (config->mode == TWR_DMA_MODE_STANDARD)
139  {
140  dma_channel->CCR &= ~DMA_CCR_CIRC;
141  }
142  else if (config->mode == TWR_DMA_MODE_CIRCULAR)
143  {
144  dma_channel->CCR |= DMA_CCR_CIRC;
145  }
146 
147  // Set memory incrementation
148  dma_channel->CCR |= DMA_CCR_MINC;
149 
150  // Set DMA channel priority
151  dma_channel->CCR &= ~DMA_CCR_PL_Msk;
152  dma_channel->CCR |= config->priority << DMA_CCR_PL_Pos;
153 
154  // Configure request selection for DMA1 Channel
155  DMA1_CSELR->CSELR &= ~(0xf << dma_cselr_pos);
156  DMA1_CSELR->CSELR |= config->request << dma_cselr_pos;
157 
158  // Configure DMA channel data length
159  dma_channel->CNDTR = config->length;
160 
161  // Configure DMA channel source address
162  dma_channel->CPAR = (uint32_t) config->address_peripheral;
163 
164  // Configure DMA channel destination address
165  dma_channel->CMAR = (uint32_t) config->address_memory;
166 
167  // Enable the transfer complete, half-complete and error interrupts
168  dma_channel->CCR |= DMA_CCR_TCIE | DMA_CCR_HTIE | DMA_CCR_TEIE;
169 
170  twr_irq_enable();
171 }
172 
173 void twr_dma_set_event_handler(twr_dma_channel_t channel, void (*event_handler)(twr_dma_channel_t, twr_dma_event_t, void *), void *event_param)
174 {
175  _twr_dma.channel[channel].event_handler = event_handler;
176  _twr_dma.channel[channel].event_param = event_param;
177 }
178 
180 {
181  _twr_dma.channel[channel].instance->CCR |= DMA_CCR_EN;
182 }
183 
185 {
186  _twr_dma.channel[channel].instance->CCR &= ~DMA_CCR_EN;
187 }
188 
189 size_t twr_dma_channel_get_length(twr_dma_channel_t channel)
190 {
191  return (size_t) _twr_dma.channel[channel].instance->CNDTR;
192 }
193 
194 void _twr_dma_task(void *param)
195 {
196  (void) param;
197 
198  twr_dma_pending_event_t pending_event;
199 
200  while (twr_fifo_read(&_twr_dma.fifo_pending, &pending_event, sizeof(twr_dma_pending_event_t)) != 0)
201  {
202  if (_twr_dma.channel[pending_event.channel].event_handler != NULL)
203  {
204  _twr_dma.channel[pending_event.channel].event_handler(pending_event.channel, pending_event.event, _twr_dma.channel[pending_event.channel].event_param);
205  }
206  }
207 }
208 
209 void _twr_dma_irq_handler(twr_dma_channel_t channel, twr_dma_event_t event)
210 {
211  if (event == TWR_DMA_EVENT_DONE && !(_twr_dma.channel[channel].instance->CCR & DMA_CCR_CIRC))
212  {
213  twr_dma_channel_stop(channel);
214  }
215 
216  twr_dma_pending_event_t pending_event = { channel, event };
217 
218  twr_fifo_irq_write(&_twr_dma.fifo_pending, &pending_event, sizeof(twr_dma_pending_event_t));
219 
220  twr_scheduler_plan_now(_twr_dma.task_id);
221 }
222 
223 void DMA1_Channel1_IRQHandler(void)
224 {
225  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(1);
226 }
227 
228 void DMA1_Channel2_3_IRQHandler(void)
229 {
230  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(2);
231 
232  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(3);
233 }
234 
235 void DMA1_Channel4_5_6_7_IRQHandler(void)
236 {
237  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(4);
238 
239  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(5);
240 
241  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(6);
242 
243  _TWR_DMA_CHECK_IRQ_OF_CHANNEL_(7);
244 }
void twr_dma_channel_config(twr_dma_channel_t channel, twr_dma_channel_config_t *config)
Configure DMA channel.
Definition: twr_dma.c:95
void twr_dma_channel_run(twr_dma_channel_t channel)
Start DMA channel.
Definition: twr_dma.c:179
void twr_dma_set_event_handler(twr_dma_channel_t channel, void(*event_handler)(twr_dma_channel_t, twr_dma_event_t, void *), void *event_param)
Set callback function.
Definition: twr_dma.c:173
twr_dma_event_t
DMA channel event.
Definition: twr_dma.h:133
void twr_dma_init(void)
Initialize DMA.
Definition: twr_dma.c:56
twr_dma_channel_t
DMA channels.
Definition: twr_dma.h:13
void twr_dma_channel_stop(twr_dma_channel_t channel)
Stop DMA channel.
Definition: twr_dma.c:184
@ TWR_DMA_EVENT_DONE
DMA channel event done.
Definition: twr_dma.h:141
@ TWR_DMA_SIZE_2
DMA channel data size 2B.
Definition: twr_dma.h:111
@ TWR_DMA_SIZE_4
DMA channel data size 4B.
Definition: twr_dma.h:114
@ TWR_DMA_DIRECTION_TO_PERIPHERAL
DMA channel direction from RAM to peripheral.
Definition: twr_dma.h:96
@ TWR_DMA_CHANNEL_7
DMA channel 7.
Definition: twr_dma.h:33
@ TWR_DMA_CHANNEL_6
DMA channel 6.
Definition: twr_dma.h:30
@ TWR_DMA_CHANNEL_1
DMA channel 1.
Definition: twr_dma.h:15
@ TWR_DMA_CHANNEL_5
DMA channel 5, used for SPI.
Definition: twr_dma.h:27
@ TWR_DMA_CHANNEL_2
DMA channel 2.
Definition: twr_dma.h:18
@ TWR_DMA_CHANNEL_3
DMA channel 3.
Definition: twr_dma.h:21
@ TWR_DMA_CHANNEL_4
DMA channel 4.
Definition: twr_dma.h:24
@ TWR_DMA_MODE_CIRCULAR
DMA channel mode circular.
Definition: twr_dma.h:126
@ TWR_DMA_MODE_STANDARD
DMA channel mode standard.
Definition: twr_dma.h:123
size_t twr_fifo_irq_write(twr_fifo_t *fifo, const void *buffer, size_t length)
Write data to FIFO from interrupt.
Definition: twr_fifo.c:101
size_t twr_fifo_read(twr_fifo_t *fifo, void *buffer, size_t length)
Read data from FIFO.
Definition: twr_fifo.c:63
void twr_fifo_init(twr_fifo_t *fifo, void *buffer, size_t size)
Initialize FIFO buffer.
Definition: twr_fifo.c:4
void twr_irq_disable(void)
Disable interrupt requests globally (call can be nested)
Definition: twr_irq.c:7
void twr_irq_enable(void)
Enable interrupt requests globally (call can be nested)
Definition: twr_irq.c:21
size_t twr_scheduler_task_id_t
Task ID assigned by scheduler.
Definition: twr_scheduler.h:22
void twr_scheduler_plan_now(twr_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
twr_scheduler_task_id_t twr_scheduler_register(void(*task)(void *), void *param, twr_tick_t tick)
Register task in scheduler.
Definition: twr_scheduler.c:53
#define TWR_TICK_INFINITY
Maximum timestamp value.
Definition: twr_tick.h:12
DMA channel configuration.
Definition: twr_dma.h:166
twr_dma_mode_t mode
DMA channel mode.
Definition: twr_dma.h:183
twr_dma_direction_t direction
DMA channel direction.
Definition: twr_dma.h:171
twr_dma_request_t request
DMA channel request.
Definition: twr_dma.h:168
void * address_peripheral
Peripheral address.
Definition: twr_dma.h:189
twr_dma_size_t data_size_memory
DMA channel memory data size.
Definition: twr_dma.h:174
size_t length
DMA channel data length.
Definition: twr_dma.h:180
void * address_memory
RAM memory address.
Definition: twr_dma.h:186
twr_dma_size_t data_size_peripheral
DMA channel peripheral data size.
Definition: twr_dma.h:177
twr_dma_priority_t priority
DMA channel priority.
Definition: twr_dma.h:192
Structure of FIFO instance.
Definition: twr_fifo.h:13