Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing ADC initialization function to work properly on Portenta H7 #48

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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