Skip to content

Commit 2efe590

Browse files
committed
Wake SPI DMA waits from interrupt
1 parent b081fd1 commit 2efe590

4 files changed

Lines changed: 73 additions & 10 deletions

File tree

ch32v305/src/hw/spi.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,32 @@
11
#include "spi.h"
22

3+
#include "FreeRTOS.h"
4+
#include "task.h"
5+
36
#include "debug.h"
7+
#include "main.h"
48
#include "pinout.h"
59

610
#include "ch32v30x_dma.h"
11+
#include "ch32v30x_misc.h"
712
#include "ch32v30x_rcc.h"
813
#include "ch32v30x_spi.h"
914

10-
#define SPI3_TX_DMA_CHANNEL DMA2_Channel2
11-
#define SPI3_TX_DMA_FLAG_GL DMA2_FLAG_GL2
12-
#define SPI3_TX_DMA_FLAG_TC DMA2_FLAG_TC2
13-
#define SPI3_TX_DMA_FLAG_TE DMA2_FLAG_TE2
14-
#define SPI3_TX_DMA_MAX_LEN 65535U
15+
#define SPI3_TX_DMA_CHANNEL DMA2_Channel2
16+
#define SPI3_TX_DMA_IRQn DMA2_Channel2_IRQn
17+
#define SPI3_TX_DMA_FLAG_GL DMA2_FLAG_GL2
18+
#define SPI3_TX_DMA_FLAG_TC DMA2_FLAG_TC2
19+
#define SPI3_TX_DMA_FLAG_TE DMA2_FLAG_TE2
20+
#define SPI3_TX_DMA_IT_GL DMA2_IT_GL2
21+
#define SPI3_TX_DMA_IT_TC DMA2_IT_TC2
22+
#define SPI3_TX_DMA_IT_TE DMA2_IT_TE2
23+
#define SPI3_TX_DMA_MAX_LEN 65535U
1524

1625
static uint16_t s_spi3_data_size = SPI_DataSize_8b;
1726
static uint16_t s_spi3_tx_repeat_word = 0U;
1827

28+
void DMA2_Channel2_IRQHandler(void) __attribute__((interrupt));
29+
1930
static void spi_hw_set_data_size(uint16_t data_size)
2031
{
2132
if(s_spi3_data_size == data_size)
@@ -35,16 +46,27 @@ static void spi_hw_set_data_size(uint16_t data_size)
3546

3647
void spi_hw_wait_dma(void)
3748
{
38-
while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_BSY) != RESET)
49+
50+
bool const dma_in_progress =
51+
((SPI3_TX_DMA_CHANNEL->CFGR & DMA_CFGR1_EN) != 0U) &&
52+
(DMA_GetCurrDataCounter(SPI3_TX_DMA_CHANNEL) > 0U);
53+
54+
if(dma_in_progress)
3955
{
56+
(void)ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
4057
}
4158

42-
while(DMA_GetCurrDataCounter(SPI3_TX_DMA_CHANNEL) > 0)
59+
/* DMA completion means the last word reached DATAR, not necessarily the
60+
* wire. The remaining BSY interval is at most one SPI frame. */
61+
while(SPI_I2S_GetFlagStatus(SPI3, SPI_I2S_FLAG_BSY) != RESET)
4362
{
4463
}
45-
DMA_Cmd(SPI3_TX_DMA_CHANNEL, DISABLE);
64+
4665
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, DISABLE);
66+
DMA_ITConfig(SPI3_TX_DMA_CHANNEL, DMA_IT_TC | DMA_IT_TE, DISABLE);
67+
DMA_Cmd(SPI3_TX_DMA_CHANNEL, DISABLE);
4768
DMA_ClearFlag(SPI3_TX_DMA_FLAG_GL | SPI3_TX_DMA_FLAG_TC | SPI3_TX_DMA_FLAG_TE);
69+
NVIC_ClearPendingIRQ(SPI3_TX_DMA_IRQn);
4870
}
4971

5072
void spi_hw_init(void)
@@ -82,6 +104,15 @@ void spi_hw_init(void)
82104
DMA_Cmd(SPI3_TX_DMA_CHANNEL, DISABLE);
83105
DMA_DeInit(SPI3_TX_DMA_CHANNEL);
84106
DMA_ClearFlag(SPI3_TX_DMA_FLAG_GL | SPI3_TX_DMA_FLAG_TC | SPI3_TX_DMA_FLAG_TE);
107+
NVIC_ClearPendingIRQ(SPI3_TX_DMA_IRQn);
108+
109+
NVIC_InitTypeDef dma_irq = {0};
110+
dma_irq.NVIC_IRQChannel = SPI3_TX_DMA_IRQn;
111+
dma_irq.NVIC_IRQChannelPreemptionPriority = 2U;
112+
dma_irq.NVIC_IRQChannelSubPriority = 0U;
113+
dma_irq.NVIC_IRQChannelCmd = ENABLE;
114+
NVIC_Init(&dma_irq);
115+
85116
s_spi3_data_size = SPI_DataSize_8b;
86117
}
87118

@@ -125,6 +156,7 @@ static void spi_hw_transfer_dma_chunk(const uint8_t *tx_buf, uint16_t len)
125156
dma_init.DMA_M2M = DMA_M2M_Disable;
126157
DMA_Init(SPI3_TX_DMA_CHANNEL, &dma_init);
127158

159+
DMA_ITConfig(SPI3_TX_DMA_CHANNEL, DMA_IT_TC | DMA_IT_TE, ENABLE);
128160
DMA_Cmd(SPI3_TX_DMA_CHANNEL, ENABLE);
129161
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
130162
}
@@ -157,6 +189,7 @@ static void spi_hw_transfer_dma_repeat_u16_chunk(uint16_t tx_word, uint16_t coun
157189
dma_init.DMA_M2M = DMA_M2M_Disable;
158190
DMA_Init(SPI3_TX_DMA_CHANNEL, &dma_init);
159191

192+
DMA_ITConfig(SPI3_TX_DMA_CHANNEL, DMA_IT_TC | DMA_IT_TE, ENABLE);
160193
DMA_Cmd(SPI3_TX_DMA_CHANNEL, ENABLE);
161194
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
162195
}
@@ -188,10 +221,31 @@ static void spi_hw_transfer_dma_u16_chunk(const uint16_t *tx_buf, uint16_t count
188221
dma_init.DMA_M2M = DMA_M2M_Disable;
189222
DMA_Init(SPI3_TX_DMA_CHANNEL, &dma_init);
190223

224+
DMA_ITConfig(SPI3_TX_DMA_CHANNEL, DMA_IT_TC | DMA_IT_TE, ENABLE);
191225
DMA_Cmd(SPI3_TX_DMA_CHANNEL, ENABLE);
192226
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
193227
}
194228

229+
void DMA2_Channel2_IRQHandler(void)
230+
{
231+
BaseType_t higher_priority_task_woken = pdFALSE;
232+
bool const transfer_ended =
233+
(DMA_GetITStatus(SPI3_TX_DMA_IT_TC) != RESET) ||
234+
(DMA_GetITStatus(SPI3_TX_DMA_IT_TE) != RESET);
235+
236+
DMA_ClearITPendingBit(SPI3_TX_DMA_IT_GL |
237+
SPI3_TX_DMA_IT_TC |
238+
SPI3_TX_DMA_IT_TE);
239+
240+
if(transfer_ended)
241+
{
242+
vTaskNotifyGiveFromISR(g_application_task_handle,
243+
&higher_priority_task_woken);
244+
}
245+
246+
portYIELD_FROM_ISR(higher_priority_task_woken);
247+
}
248+
195249
void spi_hw_transfer_dma(const uint8_t *tx_buf, size_t len)
196250
{
197251
if((tx_buf == NULL) || (len == 0U))

ch32v305/src/hw/spi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ uint8_t spi_hw_transfer_u8(uint8_t tx_byte);
1818
void spi_hw_transfer_dma(const uint8_t *tx_buf, size_t len);
1919
void spi_hw_transfer_dma_u16(const uint16_t *tx_buf, size_t count);
2020
void spi_hw_transfer_dma_repeat_u16(uint16_t tx_word, size_t count);
21+
/* Blocks only the calling FreeRTOS task while an SPI3 TX DMA transfer is
22+
* active. It returns immediately when the DMA channel has no work pending,
23+
* then waits for the final SPI frame to leave the shifter. */
2124
void spi_hw_wait_dma(void);
2225

2326
#ifdef __cplusplus

ch32v305/src/main.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static StackType_t s_usb_task_stack[USB_TASK_STACK_WORDS]
6565
static StaticTask_t s_application_task_tcb;
6666
static StackType_t s_application_task_stack[APP_TASK_STACK_WORDS]
6767
__attribute__((aligned(portBYTE_ALIGNMENT)));
68+
TaskHandle_t g_application_task_handle;
6869

6970
void detect_hardware_rev(void)
7071
{
@@ -426,15 +427,15 @@ int main(void)
426427
&s_usb_task_tcb);
427428
configASSERT(usb_task_handle != nullptr);
428429

429-
TaskHandle_t application_task =
430+
g_application_task_handle =
430431
xTaskCreateStatic(Application_Task,
431432
"application",
432433
APP_TASK_STACK_WORDS,
433434
nullptr,
434435
1U,
435436
s_application_task_stack,
436437
&s_application_task_tcb);
437-
configASSERT(application_task != nullptr);
438+
configASSERT(g_application_task_handle != nullptr);
438439

439440
vTaskStartScheduler();
440441
configASSERT(false);

ch32v305/src/main.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#ifndef MAIN_H
22
#define MAIN_H
33

4+
#include "FreeRTOS.h"
5+
#include "task.h"
6+
47
#ifdef __cplusplus
58
extern "C" {
69
#endif
@@ -12,6 +15,8 @@ typedef enum
1215
HARDWARE_REV_V2 = 2,
1316
} hardware_rev_t;
1417

18+
extern TaskHandle_t g_application_task_handle;
19+
1520
void detect_hardware_rev(void);
1621
[[nodiscard]] hardware_rev_t get_hardware_rev(void);
1722

0 commit comments

Comments
 (0)