Skip to content

Commit 92d8e59

Browse files
Tejasgargbessman
andauthored
ADC DMA Driver (#93)
Co-authored-by: Alexander Bessman <[email protected]>
1 parent de20f66 commit 92d8e59

File tree

5 files changed

+326
-69
lines changed

5 files changed

+326
-69
lines changed

src/application/main.c

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,21 @@
2323

2424
// Callback when at least 5 bytes are available
2525
#define CB_THRESHOLD (sizeof("Hello") - 1)
26+
2627
enum { RX_BUFFER_SIZE = 256 };
28+
enum { ADC_BUFFER_SIZE = 256 }; // Size of ADC buffer for DMA
2729

2830
/*****************************************************************************
2931
* Static variables
3032
******************************************************************************/
3133

3234
static uint8_t g_usb_rx_buffer_data[RX_BUFFER_SIZE] = { 0 };
33-
static bool g_usb_service_requested = false;
35+
// Buffer for USB RX data
36+
static uint16_t g_adc_buffer_data[ADC_BUFFER_SIZE] = { 0 };
37+
// Buffer for ADC data
3438

35-
uint32_t volatile g_latest_adc_value = 0;
36-
bool volatile g_adc_data_ready = false;
39+
static bool g_usb_service_requested = false;
40+
bool volatile g_adc_ready = false;
3741

3842
/*****************************************************************************
3943
* Static prototypes
@@ -52,10 +56,12 @@ void usb_cb(USB_Handle *husb, uint32_t bytes_available)
5256
LOG_FUNCTION_EXIT();
5357
}
5458

55-
static void g_adc_callback(uint32_t value)
59+
static void adc_cb(ADC_Handle *hadc)
5660
{
57-
g_latest_adc_value = value; // Store the latest ADC value
58-
g_adc_data_ready = true; // Set a flag to indicate new data is ready
61+
LOG_FUNCTION_ENTRY();
62+
(void)hadc;
63+
g_adc_ready = true; // Set flag to indicate ADC data is ready
64+
LOG_FUNCTION_EXIT();
5965
}
6066

6167
int main(void) // NOLINT
@@ -88,11 +94,14 @@ int main(void) // NOLINT
8894
circular_buffer_init(&usb_rx_buf, g_usb_rx_buffer_data, RX_BUFFER_SIZE);
8995
USB_Handle *husb = USB_init(0, &usb_rx_buf);
9096

97+
ADC_init(g_adc_buffer_data, ADC_BUFFER_SIZE);
98+
9199
USB_set_rx_callback(husb, usb_cb, CB_THRESHOLD);
92-
// Initialize ADC
93-
ADC_init();
94-
ADC_set_complete_callback(g_adc_callback);
95-
ADC_start(); // Start ADC conversions
100+
ADC_set_callback(adc_cb);
101+
102+
// Start ADC conversions
103+
ADC_start();
104+
96105
/* Basic USB/LED example:
97106
* - Process incoming bytes when USB callback is triggered
98107
* - If a byte is received, toggle the LED
@@ -113,12 +122,22 @@ int main(void) // NOLINT
113122
LOG_INFO("System running, USB active");
114123
}
115124

116-
if (g_adc_data_ready) {
117-
g_adc_data_ready = false; // Reset flag
118-
LED_toggle(); // Toggle LED to indicate ADC data ready
119-
LOG_INFO(
120-
"ADC Value: %u", (unsigned int)g_latest_adc_value
121-
); // Log the ADC value
125+
if (g_adc_ready) {
126+
g_adc_ready = false;
127+
LED_toggle();
128+
uint16_t buf[ADC_BUFFER_SIZE] = { 0 };
129+
uint32_t samples_read = ADC_read(buf, ADC_BUFFER_SIZE);
130+
131+
if (samples_read > 0) {
132+
133+
uint16_t num_samples = samples_read;
134+
for (uint32_t i = 0; i < num_samples; i++) {
135+
LOG_INFO("Sample %u: %u", i + 1, buf[i]);
136+
}
137+
} else {
138+
LOG_ERROR("ADC read failed or no data available");
139+
}
140+
ADC_restart(); // Restart ADC for next conversion
122141
}
123142

124143
if (g_usb_service_requested) {

src/platform/adc_ll.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,30 @@ typedef enum {
1717
ADC_TRIGGER_TIMER7 = 7
1818
} ADC_LL_TriggerSource;
1919

20-
typedef void (*ADC_LL_CompleteCallback)(uint32_t value);
20+
/**
21+
* @brief Callback function type for ADC complete events.
22+
*
23+
* This callback is called when the ADC conversion is complete.
24+
* It receives the DMA position as an argument.
25+
* @param dma_pos Current DMA position.
26+
*/
27+
typedef void (*ADC_LL_CompleteCallback)(void);
2128

2229
/**
2330
* @brief Initializes the ADC1 peripheral.
2431
*
2532
* This function configures the ADC1 peripheral with the specified settings.
33+
* It must be called before any ADC operations can be performed.
34+
*
35+
* @param adc_buf Pointer to the ADC data buffer.
36+
* @param sz Size of the ADC data buffer.
37+
* @param adc_trigger_timer Trigger source for the ADC (e.g., timer).
2638
*/
27-
void ADC_LL_init(ADC_LL_TriggerSource adc_trigger_timer);
39+
void ADC_LL_init(
40+
uint16_t *adc_buf,
41+
uint32_t sz,
42+
ADC_LL_TriggerSource adc_trigger_timer
43+
);
2844

2945
/**
3046
* @brief Deinitializes the ADC peripheral.
@@ -51,11 +67,10 @@ void ADC_LL_start(void);
5167
void ADC_LL_stop(void);
5268

5369
/**
54-
* @brief Sets the callback function to be called when an ADC conversion is
55-
* complete.
70+
* @brief Sets the callback for ADC completion events.
5671
*
57-
* This function allows the user to set a callback that will be invoked
58-
* when an ADC conversion is complete.
72+
* This function sets a user-defined callback that will be called when
73+
* the ADC conversion is complete.
5974
*
6075
* @param callback Pointer to the callback function to be set.
6176
*/

src/platform/h563xx/adc_ll.c

Lines changed: 127 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,27 @@
2222

2323
enum { ADC_IRQ_PRIORITY = 1 }; // ADC interrupt priority
2424

25+
typedef struct {
26+
ADC_HandleTypeDef *adc_handle; // Pointer to the ADC handle
27+
ADC_ChannelConfTypeDef adc_config; // ADC channel configuration
28+
DMA_HandleTypeDef *dma_handle; // Pointer to the DMA handle
29+
uint16_t *adc_buffer_data; // Pointer to the ADC data buffer
30+
uint32_t adc_buffer_size; // Size of the ADC data buffer
31+
ADC_LL_CompleteCallback
32+
adc_complete_callback; // Callback for ADC completion
33+
bool initialized; // Flag to indicate if the ADC is initialized
34+
} ADCInstance;
35+
2536
static ADC_HandleTypeDef g_hadc = { nullptr };
2637

2738
static ADC_ChannelConfTypeDef g_config = { 0 };
2839

29-
static ADC_LL_CompleteCallback volatile g_adc_complete_callback;
40+
static DMA_HandleTypeDef g_hdma_adc = { nullptr };
41+
42+
static ADCInstance g_adc_instance = {
43+
.adc_handle = &g_hadc,
44+
.dma_handle = &g_hdma_adc,
45+
};
3046

3147
/**
3248
* @brief Initializes the ADC MSP (MCU Support Package).
@@ -45,16 +61,42 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
4561
__HAL_RCC_ADC_CLK_ENABLE();
4662
// Enable GPIOA clock
4763
__HAL_RCC_GPIOA_CLK_ENABLE();
64+
// Enable DMA1 clock
65+
__HAL_RCC_GPDMA1_CLK_ENABLE();
4866

4967
// Configure GPIO pin for ADC1_IN0 (PA0)
5068
gpio_init.Pin = GPIO_PIN_0; // PA0
5169
gpio_init.Mode = GPIO_MODE_ANALOG;
5270
gpio_init.Pull = GPIO_NOPULL;
5371
HAL_GPIO_Init(GPIOA, &gpio_init);
5472

73+
/*DMA for the ADC*/
74+
g_hdma_adc.Instance = GPDMA1_Channel6; // DMA channel for ADC1
75+
g_hdma_adc.Init.Request = GPDMA1_REQUEST_ADC1;
76+
g_hdma_adc.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
77+
g_hdma_adc.Init.Direction = DMA_PERIPH_TO_MEMORY;
78+
g_hdma_adc.Init.SrcInc = DMA_SINC_FIXED;
79+
g_hdma_adc.Init.DestInc = DMA_DINC_INCREMENTED;
80+
g_hdma_adc.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
81+
g_hdma_adc.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
82+
g_hdma_adc.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
83+
g_hdma_adc.Init.SrcBurstLength = 1;
84+
g_hdma_adc.Init.DestBurstLength = 1;
85+
g_hdma_adc.Init.TransferAllocatedPort =
86+
(DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0);
87+
g_hdma_adc.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
88+
g_hdma_adc.Init.Mode = DMA_NORMAL;
89+
if (HAL_DMA_Init(&g_hdma_adc) != HAL_OK) {
90+
THROW(ERROR_HARDWARE_FAULT);
91+
}
92+
__HAL_LINKDMA(&g_hadc, DMA_Handle, g_hdma_adc);
93+
5594
// Enable ADC1 interrupt
5695
HAL_NVIC_SetPriority(ADC1_IRQn, ADC_IRQ_PRIORITY, 0);
5796
HAL_NVIC_EnableIRQ(ADC1_IRQn);
97+
98+
HAL_NVIC_SetPriority(GPDMA1_Channel6_IRQn, ADC_IRQ_PRIORITY, 1);
99+
HAL_NVIC_EnableIRQ(GPDMA1_Channel6_IRQn);
58100
}
59101

60102
/**
@@ -65,52 +107,75 @@ void HAL_ADC_MspInit(ADC_HandleTypeDef *hadc)
65107
* parameters.
66108
*
67109
*/
68-
void ADC_LL_init(ADC_LL_TriggerSource adc_trigger_timer)
110+
void ADC_LL_init(
111+
uint16_t *adc_buf,
112+
uint32_t sz,
113+
ADC_LL_TriggerSource adc_trigger_timer
114+
)
69115
{
116+
if (adc_buf == nullptr || sz == 0) {
117+
THROW(ERROR_INVALID_ARGUMENT);
118+
return;
119+
}
120+
121+
if (g_adc_instance.initialized) {
122+
THROW(ERROR_RESOURCE_BUSY);
123+
return;
124+
}
125+
126+
// Initialize the ADC handle
127+
ADCInstance *instance = &g_adc_instance;
70128

71129
// Initialize the ADC peripheral
72-
g_hadc.Instance = ADC1;
73-
g_hadc.Init.ClockPrescaler =
130+
instance->adc_handle->Instance = ADC1;
131+
instance->adc_handle->Init.ClockPrescaler =
74132
ADC_CLOCK_SYNC_PCLK_DIV1; // ADC clock and prescaler
75-
g_hadc.Init.Resolution = ADC_RESOLUTION_12B;
76-
g_hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
77-
g_hadc.Init.ScanConvMode = DISABLE;
78-
g_hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
79-
g_hadc.Init.LowPowerAutoWait = DISABLE;
80-
g_hadc.Init.ContinuousConvMode = DISABLE;
81-
g_hadc.Init.NbrOfConversion = 1;
82-
g_hadc.Init.DiscontinuousConvMode = DISABLE;
133+
instance->adc_handle->Init.Resolution = ADC_RESOLUTION_12B;
134+
instance->adc_handle->Init.DataAlign = ADC_DATAALIGN_RIGHT;
135+
instance->adc_handle->Init.ScanConvMode = DISABLE;
136+
instance->adc_handle->Init.EOCSelection = ADC_EOC_SINGLE_CONV;
137+
instance->adc_handle->Init.LowPowerAutoWait = DISABLE;
138+
instance->adc_handle->Init.ContinuousConvMode = DISABLE;
139+
instance->adc_handle->Init.NbrOfConversion = 1;
140+
instance->adc_handle->Init.DiscontinuousConvMode = DISABLE;
83141
if (adc_trigger_timer == ADC_TRIGGER_TIMER6) {
84-
g_hadc.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T6_TRGO;
142+
instance->adc_handle->Init.ExternalTrigConv = ADC_EXTERNALTRIG_T6_TRGO;
85143
} else {
86144
THROW(ERROR_INVALID_ARGUMENT);
87145
return;
88146
}
89-
g_hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
90-
g_hadc.Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
91-
g_hadc.Init.DMAContinuousRequests = DISABLE;
92-
g_hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
93-
g_hadc.Init.OversamplingMode = DISABLE;
147+
instance->adc_handle->Init.ExternalTrigConvEdge =
148+
ADC_EXTERNALTRIGCONVEDGE_RISING;
149+
instance->adc_handle->Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL;
150+
instance->adc_handle->Init.DMAContinuousRequests = DISABLE;
151+
instance->adc_handle->Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
152+
instance->adc_handle->Init.OversamplingMode = DISABLE;
94153

95154
if (HAL_ADC_Init(&g_hadc) != HAL_OK) {
96155
THROW(ERROR_HARDWARE_FAULT);
97156
}
98157

99-
// Configure ADC channel
158+
// Configure the ADC channel
159+
instance->adc_config = g_config;
100160
g_config.Channel = ADC_CHANNEL_0; // ADC1_IN0
101161
g_config.Rank = ADC_REGULAR_RANK_1;
102162
g_config.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
103163
g_config.SingleDiff = ADC_SINGLE_ENDED; // Single-ended input
104164
g_config.OffsetNumber = ADC_OFFSET_NONE;
105165
g_config.Offset = 0;
106-
if (HAL_ADC_ConfigChannel(&g_hadc, &g_config) != HAL_OK) {
166+
if (HAL_ADC_ConfigChannel(instance->adc_handle, &g_config) != HAL_OK) {
107167
THROW(ERROR_HARDWARE_FAULT);
108168
}
109169

110170
// Calibration with error handling
111171
if (HAL_ADCEx_Calibration_Start(&g_hadc, ADC_SINGLE_ENDED) != HAL_OK) {
112172
THROW(ERROR_HARDWARE_FAULT);
113173
} // Calibration
174+
175+
// Set the ADC buffer and size
176+
instance->adc_buffer_data = adc_buf;
177+
instance->adc_buffer_size = sz;
178+
instance->initialized = true;
114179
}
115180

116181
/**
@@ -119,10 +184,24 @@ void ADC_LL_init(ADC_LL_TriggerSource adc_trigger_timer)
119184
*/
120185
void ADC_LL_deinit(void)
121186
{
187+
if (!g_adc_instance.initialized) {
188+
THROW(ERROR_RESOURCE_UNAVAILABLE);
189+
return;
190+
}
191+
192+
ADCInstance *instance = &g_adc_instance;
193+
// Stop the ADC conversion;
194+
HAL_NVIC_DisableIRQ(ADC1_IRQn); // Disable ADC1 interrupt
195+
122196
// Deinitialize the ADC peripheral
123-
if (HAL_ADC_DeInit(&g_hadc) != HAL_OK) {
197+
if (HAL_ADC_DeInit(instance->adc_handle) != HAL_OK) {
124198
THROW(ERROR_HARDWARE_FAULT);
125199
}
200+
201+
instance->adc_buffer_data = nullptr;
202+
instance->adc_buffer_size = 0;
203+
instance->adc_complete_callback = nullptr; // Clear the callback
204+
instance->initialized = false;
126205
}
127206

128207
/**
@@ -134,10 +213,15 @@ void ADC_LL_deinit(void)
134213
*/
135214
void ADC_LL_start(void)
136215
{
137-
// Start the ADC conversion
138-
if (HAL_ADC_Start_IT(&g_hadc) != HAL_OK) {
216+
__HAL_ADC_CLEAR_FLAG(&g_hadc, ADC_FLAG_OVR); // Clear any previous flags
217+
218+
if (HAL_ADC_Start_DMA(
219+
&g_hadc,
220+
(uint32_t *)g_adc_instance.adc_buffer_data,
221+
g_adc_instance.adc_buffer_size
222+
) != HAL_OK) {
139223
THROW(ERROR_HARDWARE_FAULT);
140-
}
224+
} // Start ADC in DMA mode
141225
}
142226

143227
/**
@@ -150,24 +234,38 @@ void ADC_LL_start(void)
150234
void ADC_LL_stop(void)
151235
{
152236
// Stop the ADC conversion
153-
if (HAL_ADC_Stop_IT(&g_hadc) != HAL_OK) {
237+
if (HAL_ADC_Stop_DMA(&g_hadc) != HAL_OK) {
154238
THROW(ERROR_HARDWARE_FAULT);
155239
}
156240
}
157241

242+
/**
243+
* @brief Sets the ADC complete callback.
244+
*
245+
* This function sets the user-defined callback that will be called when the
246+
* ADC conversion is complete.
247+
*
248+
* @param callback Pointer to the callback function.
249+
*/
158250
void ADC_LL_set_complete_callback(ADC_LL_CompleteCallback callback)
159251
{
160-
g_adc_complete_callback = callback; // Set the user-defined callback
252+
g_adc_instance.adc_complete_callback =
253+
callback; // Set the user-defined callback
161254
}
162255

163256
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
164257
{
165258
(void)hadc; // Suppress unused parameter warning
166-
uint32_t value = HAL_ADC_GetValue(&g_hadc);
167-
if (g_adc_complete_callback != nullptr) {
168-
g_adc_complete_callback(value
259+
260+
if (g_adc_instance.adc_complete_callback != nullptr) {
261+
g_adc_instance.adc_complete_callback(
169262
); // Call the user-defined callback with the ADC value
170263
}
171264
}
172265

173266
void ADC1_IRQHandler(void) { HAL_ADC_IRQHandler(&g_hadc); }
267+
268+
void GPDMA1_Channel6_IRQHandler(void)
269+
{
270+
HAL_DMA_IRQHandler(&g_hdma_adc); // Handle DMA interrupts
271+
}

0 commit comments

Comments
 (0)