Skip to content

Commit

Permalink
Merge pull request #48 from leonardocavagnis/adc_config_fix
Browse files Browse the repository at this point in the history
Fixing ADC initialization function to work properly on Portenta H7
  • Loading branch information
leonardocavagnis authored Dec 6, 2023
2 parents 9e610e2 + 065318b commit 0fcd53e
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 62 deletions.
40 changes: 4 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,7 @@
<img src="https://content.arduino.cc/website/Arduino_logo_teal.svg" height="100" align="right" />

`Arduino_AdvancedAnalog`
===========================
[![Compile Examples status](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions/workflows/compile-examples.yml)
[![Spell Check status](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions/workflows/spell-check.yml)
[![Sync Labels status](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions/workflows/sync-labels.yml/badge.svg)](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions/workflows/sync-labels.yml)
[![Arduino Lint](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/workflows/Arduino%20Lint/badge.svg)](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/actions?workflow=Arduino+Lint)
# 〰️ Arduino AdvancedAnalog Library

Advanced Analog Library for Arduino Giga.
The Arduino AdvancedAnalog library is designed to offer high performance DAC/ADC applications on boards based on the STM32H7 microcontroller.

## :mag_right: Resources

* [How to install a library](https://www.arduino.cc/en/guide/libraries)
* [Help Center](https://support.arduino.cc/)
* [Forum](https://forum.arduino.cc)

## :bug: Bugs & Issues

If you want to report an issue with this library, you can submit it to the [issue tracker](https://github.com/arduino-libraries/Arduino_AdvancedAnalog/issues) of this repository. Remember to include as much detail as you can about your hardware set-up, code and steps for reproducing the issue. Make sure you're using an original Arduino board.

## :technologist: Development

There are many ways to contribute:

* Improve documentation and examples
* Fix a bug
* Test open Pull Requests
* Implement a new feature
* Discuss potential ways to improve this library

You can submit your patches directly to this repository as Pull Requests. Please provide a detailed description of the problem you're trying to solve and make sure you test on real hardware.

## :yellow_heart: Donations

This open-source code is maintained by Arduino with the help of the community. We invest a considerable amount of time in testing code, optimizing it and introducing new features. Please consider [donating](https://www.arduino.cc/en/donate/) or [sponsoring](https://github.com/sponsors/arduino) to support our work, as well as [buying original Arduino boards](https://store.arduino.cc/) which is the best way to make sure our effort can continue in the long term.

## Known issues
* ADC is running at the slowest possible clock (PCLK), should probably set up a PLL and change the clock source.
📖 For more information about this library please read the documentation [here](./docs/).
166 changes: 164 additions & 2 deletions docs/readme.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,174 @@
# Arduino_AdvancedAnalog library

The **Arduino_AdvancedAnalog** library is designed for high performance DAC/ADC applications on boards based on the STM32H747XI microcontroller:
[![License](https://img.shields.io/badge/License-LGPLv2.1-blue.svg)](../LICENSE)

The **Arduino_AdvancedAnalog** library is designed to offer high performance DAC/ADC applications on boards based on the STM32H747XI microcontroller:
- [Arduino GIGA R1 WiFi](https://store.arduino.cc/products/giga-r1-wifi)
- [Arduino Portenta H7](https://store.arduino.cc/products/portenta-h7)

To use this library:
## Features

- ADC/DAC parameters fine tuning: resolution, channel number, queue number and size
- ADC acquisition with DMA in double buffering mode
- ADC Multichannel acquisition
- DAC Multichannel writing
- Storing ADC samples history in multiple queues
## Guides

To learn more about using the DAC & ADC on the GIGA R1 WiFi, check out the [GIGA Advanced DAC/ADC Guide](https://docs.arduino.cc/tutorials/giga-r1-wifi/giga-audio).

This includes examples such as audio playback from USB storage, generate sine waves and more.
## Usage

### ADC

To use this library for ADC applications, you must have a supported Arduino board and include the AdvancedAnalog library in your Arduino sketch. Here is a minimal example:

```cpp
#include <Arduino_AdvancedAnalog.h>

AdvancedADC adc1(A0);

void setup() {
Serial.begin(9600);

// Initialize ADC with: resolution, sample rate, number of samples per channel, queue depth
if (!adc1.begin(AN_RESOLUTION_16, 16000, 32, 64)) {
Serial.println("Failed to start ADC!");
while (1);
}
}

void loop() {
// Check if an ADC measurement is ready
if (adc1.available()) {
// Get read buffer
SampleBuffer buf = adc1.read();

// Print sample from read buffer
Serial.println(buf[0]);

// Release read buffer
buf.release();
}
}
```
#### ADC Multichannel (GIGA R1 WiFi)
This library supports concurrent usage of up to **three** ADCs (_ADC1_, _ADC2_ and _ADC3_).
Each ADC instance can handle up to **five** channels.
**Note:** It's important to be aware that certain pins cannot be used across multiple ADCs or cannot share the same ADC.
*Please ensure that you refer to tables below when configuring your project to avoid conflicts in pin assignments.*
Below is a table illustrating the pin mapping for each ADC in **Arduino Giga R1 WiFi**:
| Pin | ADC1 | ADC2 | ADC3 |
|-------|-------|-------|-------|
| A0 | X | X | |
| A1 | X | X | |
| A2 | X | X | |
| A3 | X | X | |
| A4 | X | X | |
| A5 | X | X | X |
| A6 | X | X | X |
| A7 | X | | |
| A8 | | | X |
| A9 | | | X |
| A10 | X | X | |
| A11 | X | X | |
Here is a example for the Arduino GIGA R1 WiFi:
```cpp
#include <Arduino_AdvancedAnalog.h>
AdvancedADC adc_a(A0, A1);
/* Mapped to ADC1 */
AdvancedADC adc_b(A2);
/* Mapped to ADC2, because ADC1 is occupied by A0 and A1 */
void setup() {
...
```

#### ADC Multichannel (Portenta H7)

Below is a table illustrating the pin mapping for each ADC in **Portenta H7**:

| Pin | ADC1 | ADC2 | ADC3 |
|-------|-------|-------|-------|
| A0 | X | X | |
| A1 | X | X | |
| A2 | | | X |
| A3 | | | X |
| A4 | X | X | X |
| A5 | X | X | |
| A6 | X | X | |
| A7 | X | X | |

Here is an example for the Portenta H7:

```cpp
#include <Arduino_AdvancedAnalog.h>

AdvancedADC adc_c(A2, A3, A4);
/* Mapped to ADC3 */

AdvancedADC adc_d(A5);
/* Mapped to ADC1 */

void setup() {
...
```
### DAC
To use this library for DAC application, you must have a supported Arduino board and include the AdvancedAnalog library in your Arduino sketch. Here is a minimal example for the Arduino GIGA R1 WiFi:
```cpp
#include <Arduino_AdvancedAnalog.h>
AdvancedDAC dac1(A12);
void setup() {
Serial.begin(9600);
// Initialize DAC with: resolution, sample rate, number of samples per channel, queue depth
if (!dac1.begin(AN_RESOLUTION_12, 8000, 32, 64)) {
Serial.println("Failed to start DAC!");
while (1);
}
}
void loop() {
if (dac1.available()) {
// Get a free buffer for writing
SampleBuffer buf = dac1.dequeue();
// Write data to buffer (Even position: 0, Odd position: 0xFFF)
for (int i=0; i<buf.size(); i++) {
buf.data()[i] = (i % 2 == 0) ? 0: 0xFFF;
}
// Write the buffer to DAC
dac1.write(buf);
}
}
```

## Examples
- **[Advanced](../examples/Advanced):** This folder contains examples showing how to configure ADC/DAC to read/write data.
- **[Beginner](../examples/Beginner):** This folder contains examples showing how to generate waveforms with DAC.

## API

The API documentation can be found [here](./api.md).

## License

This library is released under the [LGPLv2.1 license](../LICENSE).

86 changes: 65 additions & 21 deletions src/AdvancedADC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,27 @@
#include "AdvancedADC.h"

#define ADC_NP ((ADCName) NC)
#define ADC_PIN_ALT_MASK (uint32_t) (ALT0 | ALT1 )

struct adc_descr_t {
ADC_HandleTypeDef adc;
DMA_HandleTypeDef dma;
IRQn_Type dma_irqn;
TIM_HandleTypeDef tim;
uint32_t tim_trig;
uint32_t pin_alt;
DMABufferPool<Sample> *pool;
DMABuffer<Sample> *dmabuf[2];
};

static uint32_t adc_pin_alt[3] = {0, ALT0, ALT1};

static adc_descr_t adc_descr_all[3] = {
{{ADC1}, {DMA1_Stream1, {DMA_REQUEST_ADC1}}, DMA1_Stream1_IRQn, {TIM1}, ADC_EXTERNALTRIG_T1_TRGO,
0, nullptr, {nullptr, nullptr}},
nullptr, {nullptr, nullptr}},
{{ADC2}, {DMA1_Stream2, {DMA_REQUEST_ADC2}}, DMA1_Stream2_IRQn, {TIM2}, ADC_EXTERNALTRIG_T2_TRGO,
ALT0, nullptr, {nullptr, nullptr}},
nullptr, {nullptr, nullptr}},
{{ADC3}, {DMA1_Stream3, {DMA_REQUEST_ADC3}}, DMA1_Stream3_IRQn, {TIM3}, ADC_EXTERNALTRIG_T3_TRGO,
ALT1, nullptr, {nullptr, nullptr}},
nullptr, {nullptr, nullptr}},
};

static uint32_t ADC_RES_LUT[] = {
Expand Down Expand Up @@ -121,13 +123,31 @@ int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_sampl
return 0;
}

// Clear ALTx pin.
for (size_t i=0; i<n_channels; i++) {
adc_pins[i] = (PinName) (adc_pins[i] & ~(ADC_PIN_ALT_MASK));
}

// Find an ADC that can be used with these set of pins/channels.
for (size_t i=0; instance == ADC_NP && i<AN_ARRAY_SIZE(adc_descr_all); i++) {
descr = &adc_descr_all[i];
if (descr->pool == nullptr) {
// Check if the first channel is connected to this ADC.
PinName pin = (PinName) (adc_pins[0] | descr->pin_alt);
instance = (ADCName) pinmap_peripheral(pin, PinMap_ADC);
for (size_t i=0; instance == ADC_NP && i<AN_ARRAY_SIZE(adc_pin_alt); i++) {
// Calculate alternate function pin.
PinName pin = (PinName) (adc_pins[0] | adc_pin_alt[i]); // First pin decides the ADC.

// Check if pin is mapped.
if (pinmap_find_peripheral(pin, PinMap_ADC) == NC) {
break;
}

// Find the first free ADC according to the available ADCs on pin.
for (size_t j=0; instance == ADC_NP && j<AN_ARRAY_SIZE(adc_descr_all); j++) {
descr = &adc_descr_all[j];
if (descr->pool == nullptr) {
ADCName tmp_instance = (ADCName) pinmap_peripheral(pin, PinMap_ADC);
if (descr->adc.Instance == ((ADC_TypeDef*) tmp_instance)) {
instance = tmp_instance;
adc_pins[0] = pin;
}
}
}
}

Expand All @@ -138,14 +158,29 @@ int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_sampl
}

// Configure ADC pins.
for (size_t i=0; i<n_channels; i++) {
// Set the alternate pin names for this ADC instance.
adc_pins[i] = (PinName) (adc_pins[i] | descr->pin_alt);
// All channels must share the same instance; if not, bail out
if (instance != pinmap_peripheral(adc_pins[i], PinMap_ADC)) {
return 0;
pinmap_pinout(adc_pins[0], PinMap_ADC);
uint8_t ch_init = 1;
for (size_t i=1; i<n_channels; i++) {
for (size_t j=0; j<AN_ARRAY_SIZE(adc_pin_alt); j++) {
// Calculate alternate function pin.
PinName pin = (PinName) (adc_pins[i] | adc_pin_alt[j]);
// Check if pin is mapped.
if (pinmap_find_peripheral(pin, PinMap_ADC) == NC) {
break;
}
// Check if pin is connected to the selected ADC.
if (instance == pinmap_peripheral(pin, PinMap_ADC)) {
pinmap_pinout(pin, PinMap_ADC);
adc_pins[i] = pin;
ch_init++;
break;
}
}
pinmap_pinout(adc_pins[i], PinMap_ADC);
}

// All channels must share the same instance; if not, bail out.
if (ch_init < n_channels) {
return 0;
}

// Allocate DMA buffer pool.
Expand All @@ -157,21 +192,30 @@ int AdvancedADC::begin(uint32_t resolution, uint32_t sample_rate, size_t n_sampl
descr->dmabuf[1] = descr->pool->allocate();

// Init and config DMA.
hal_dma_config(&descr->dma, descr->dma_irqn, DMA_PERIPH_TO_MEMORY);
if (hal_dma_config(&descr->dma, descr->dma_irqn, DMA_PERIPH_TO_MEMORY) < 0) {
return 0;
}

// Init and config ADC.
hal_adc_config(&descr->adc, ADC_RES_LUT[resolution], descr->tim_trig, adc_pins, n_channels);
if (hal_adc_config(&descr->adc, ADC_RES_LUT[resolution], descr->tim_trig, adc_pins, n_channels) < 0) {
return 0;
}

// Link DMA handle to ADC handle, and start the ADC.
__HAL_LINKDMA(&descr->adc, DMA_Handle, descr->dma);
HAL_ADC_Start_DMA(&descr->adc, (uint32_t *) descr->dmabuf[0]->data(), descr->dmabuf[0]->size());
if (HAL_ADC_Start_DMA(&descr->adc, (uint32_t *) descr->dmabuf[0]->data(), descr->dmabuf[0]->size()) != HAL_OK) {
return 0;
}

// Re/enable DMA double buffer mode.
hal_dma_enable_dbm(&descr->dma, descr->dmabuf[0]->data(), descr->dmabuf[1]->data());

// Init, config and start the ADC timer.
hal_tim_config(&descr->tim, sample_rate);
HAL_TIM_Base_Start(&descr->tim);
if (HAL_TIM_Base_Start(&descr->tim) != HAL_OK) {
return 0;
}

return 1;
}

Expand Down
10 changes: 7 additions & 3 deletions src/HALConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,10 @@ int hal_adc_config(ADC_HandleTypeDef *adc, uint32_t resolution, uint32_t trigger
adc->Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
adc->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;

HAL_ADC_Init(adc);
HAL_ADCEx_Calibration_Start(adc, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED);
if (HAL_ADC_Init(adc) != HAL_OK
|| HAL_ADCEx_Calibration_Start(adc, ADC_CALIB_OFFSET, ADC_SINGLE_ENDED) != HAL_OK) {
return -1;
}

ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Offset = 0;
Expand All @@ -209,7 +211,9 @@ int hal_adc_config(ADC_HandleTypeDef *adc, uint32_t resolution, uint32_t trigger
uint32_t channel = STM_PIN_CHANNEL(function);
sConfig.Rank = ADC_RANK_LUT[rank];
sConfig.Channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel);
HAL_ADC_ConfigChannel(adc, &sConfig);
if (HAL_ADC_ConfigChannel(adc, &sConfig) != HAL_OK) {
return -1;
}
}

return 0;
Expand Down

0 comments on commit 0fcd53e

Please sign in to comment.