Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2739
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Цифро-аналоговое преобразование

396

13.2. Модуль HAL_DAC

После краткого ознакомления с наиболее важными функциями, предлагаемыми периферийным устройством ЦАП в микроконтроллерах STM32, самое время погрузиться в связанные с ним API-интерфейсы CubeHAL.

Чтобы манипулировать периферийным устройством ЦАП, HAL объявляет структуру Си DAC_HandleTypeDef, которая определена следующим образом:

typedef struct {

 

 

 

DAC_TypeDef

*Instance;

/* Указатель на дескриптор ЦАП

*/

__IO HAL_DAC_StateTypeDef

State;

/* Состояние работы ЦАП

*/

HAL_LockTypeDef

Lock;

/* Блокировка объекта ЦАП

*/

DMA_HandleTypeDef

*DMA_Handle1; /* Указатель на дескриптор DMA

 

 

 

для канала 1

*/

DMA_HandleTypeDef

*DMA_Handle2; /* Указатель на дескриптор DMA

 

 

 

для канала 2

*/

__IO uint32_t

ErrorCode;

/* Код ошибки ЦАП

*/

} DAC_HandleTypeDef;

 

 

 

Давайте проанализируем наиболее важные поля данной структуры.

Instance (экземпляр): это указатель на дескриптор ЦАП, который мы будем ис-

пользовать. Например, DAC1 является дескриптором первого ЦАП.

DMA_Handle{1,2}: это указатель на дескриптор DMA, сконфигурированный для вы-

полнения цифро-аналоговых преобразований в режиме DMA. В ЦАП с двумя выходными каналами существуют два независимых дескриптора DMA, используемых для выполнения преобразований для каждого канала.

Как видите, структура DAC_HandleTypeDef отличается от дескрипторов других периферийных устройств, используемых до сих пор. Фактически, она не предоставляет специальный параметр Init, используемый функцией HAL_DAC_Init() для конфигурации ЦАП. Это связано с тем, что действующая конфигурация ЦАП выполняется на уровне канала, и она требуется для структуры DAC_ChannelConfTypeDef, которая определена следующим образом:

typedef struct {

 

 

uint32_t DAC_Trigger;

/* Задает внешний источник запуска для выбранного

 

канала ЦАП */

 

uint32_t DAC_OutputBuffer;

/* Определяет, будет ли выходной буфер канала ЦАП

 

включен или отключен

*/

} DAC_ChannelConfTypeDef;

 

 

DAC_Trigger: задает источник, используемый для запуска преобразования ЦАП.

Может принимать значение DAC_TRIGGER_NONE, когда ЦАП управляется вручную с

помощью функции HAL_DAC_SetValue(); значение DAC_TRIGGER_SOFTWARE, когда ЦАП

управляется в режиме DMA без таймера для «синхронизации» преобразований; значение DAC_TRIGGER_Tx_TRGO, определяющее преобразование, управляемое предназначенным для этого таймером.

DAC_OutputBuffer: включает выделенный выходной буфер.

Цифро-аналоговое преобразование

397

Для фактической конфигурации канала ЦАП мы используем функцию:

HAL_StatusTypeDef HAL_DAC_ConfigChannel(DAC_HandleTypeDef* hdac, DAC_ChannelConfTypeDef* sConfig, uint32_t Channel);

которая принимает указатель на экземпляр структуры DAC_HandleTypeDef, указатель на экземпляр структуры DAC_ChannelConfTypeDef, рассмотренной ранее, и макрос DAC_CHANNEL_1 для конфигурации первого канала или DAC_CHANNEL_2 – для второго.

В некоторых более новых микроконтроллерах STM32, таких как STM32L476, ЦАП также предоставляет дополнительные функции с пониженным энергопотреблением. Например, можно включить схему выборки и хранения, которая позволяет поддерживать стабильное выходное напряжение, даже если ЦАП отключен. Это чрезвычайно полезно в приложениях с батарейным питанием. В данных микроконтроллерах структура DAC_ChannelConfTypeDef отличается, и она позволяет конфигурировать эти дополнительные функции. Обратитесь к исходному коду HAL для микроконтроллера, который вы рассматриваете.

13.2.1. Управление ЦАП вручную

Периферийное устройство ЦАП может управляться вручную или с использованием контроллера DMA и источника запуска (например, предназначенный для этого таймер). Сейчас мы собираемся проанализировать первый метод, который используется, когда мы не нуждаемся в преобразованиях на высоких частотах.

Первый шаг состоит в запуске периферийного устройства путем вызова функции:

HAL_StatusTypeDef HAL_DAC_Start(DAC_HandleTypeDef* hdac, uint32_t Channel);

Функция принимает указатель на экземпляр структуры DAC_HandleTypeDef и активируе-

мый канал (DAC_CHANNEL_1 или DAC_CHANNEL_2).

Как только канал ЦАП включится, мы можем выполнить преобразование, вызвав функцию:

HAL_StatusTypeDef HAL_DAC_SetValue(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t Alignment, uint32_t Data);

где параметр Alignment может принимать значение DAC_ALIGN_8B_R для управления ЦАП в 8-разрядном режиме, DAC_ALIGN_12B_L или DAC_ALIGN_12B_R для управления ЦАП в 12-разрядном режиме, передавая выходное значение выравнивания по левому или правому краю соответственно.

В следующем примере, предназначенном для работы на Nucleo-F072RB, показано, как управлять периферийным устройством ЦАП вручную. Пример основан на том факте, что в большинстве плат Nucleo, предоставляющих периферийное устройство ЦАП, один из выходных каналов соответствует выводу PA5, который подключен к светодиоду LD2. Это позволяет нам включать/выключать LD2 с помощью ЦАП.

Цифро-аналоговое преобразование

398

Имя файла: src/main-ex1.c

8 DAC_HandleTypeDef hdac;

9

10/* Прототипы функций ---------------------------------------------------------*/

11static void MX_DAC_Init(void);

12

13int main(void) {

14HAL_Init();

15Nucleo_BSP_Init();

17/* Инициализация всей сконфигурированной периферии */

18

MX_DAC_Init();

19

 

20HAL_DAC_Init(&hdac);

21HAL_DAC_Start(&hdac, DAC_CHANNEL_2);

23while(1) {

24int i = 2000;

25while(i < 4000) {

26HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, i);

27HAL_Delay(1);

28i+=4;

29}

30

31while(i > 2000) {

32HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, i);

33HAL_Delay(1);

34i-=4;

35}

36}

37}

38

39/* Функция инициализации ЦАП */

40void MX_DAC_Init(void) {

41DAC_ChannelConfTypeDef sConfig;

42GPIO_InitTypeDef GPIO_InitStruct;

44

__HAL_RCC_DAC1_CLK_ENABLE();

45

 

46/* Инициализация ЦАП */

47hdac.Instance = DAC;

48HAL_DAC_Init(&hdac);

49

50/**Конфигурация канала OUT2 ЦАП */

51sConfig.DAC_Trigger = DAC_TRIGGER_NONE;

52sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

53HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2);

55/* Конфигурация GPIO ЦАП

56PA5 ------> DAC_OUT2

Цифро-аналоговое преобразование

399

57*/

58GPIO_InitStruct.Pin = GPIO_PIN_5;

59GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

60GPIO_InitStruct.Pull = GPIO_NOPULL;

61HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

62}

Код достаточно прост. Строки [40:62] конфигурируют ЦАП таким образом, чтобы канал 2 использовался в качестве выходного канала. По этой причине PA5 сконфигурирован в качестве аналогового выхода (строки [58:61]). Обратите внимание, что, поскольку мы собираемся управлять преобразованиями ЦАП вручную, для источника запуска канала установлено значение DAC_TRIGGER_NONE (строка 51). Наконец, main() – это не что иное, как бесконечный цикл, который увеличивает/уменьшает выходное напряжение, так что LD2 плавно включается/выключается.

13.2.2.Управление ЦАП в режиме DMA с использованием таймера

Наиболее распространенным использованием периферийного устройства ЦАП является генерация аналогового сигнала с заданной частотой (например, в аудио приложениях). Если это так, то лучший способ управлять ЦАП – использовать DMA и таймер для запуска преобразований.

Чтобы запустить ЦАП и выполнить передачу в режиме DMA, нам нужно сконфигурировать соответствующую пару канал/поток DMA и использовать функцию:

HAL_StatusTypeDef HAL_DAC_Start_DMA(DAC_HandleTypeDef* hdac, uint32_t Channel, uint32_t* pData, uint32_t Length, uint32_t Alignment);

которая принимает указатель на экземпляр структуры DAC_HandleTypeDef, активируемый канал (DAC_CHANNEL_1 или DAC_CHANNEL_2), указатель на массив значений для передачи в режиме DMA, его размер и выравнивание выходных значений в памяти, которое может принимать значение DAC_ALIGN_8B_R для управления ЦАП в 8-разрядном режиме, DAC_ALIGN_12B_L или DAC_ALIGN_12B_R для управления ЦАП в 12-разрядном режиме, передавая выходное значение с выравниванием по левому или правому краю соответственно.

Например, мы можем легко генерировать синусоидальный сигнал, используя ЦАП. В Главе 11 мы проанализировали, как использовать режим ШИМ таймера для генерации синусоидальных сигналов. Если наш микроконтроллер предоставляет ЦАП, то эту же операцию можно выполнить проще. Более того, в зависимости от конкретного приложения, включив выходной буфер, мы можем вообще избежать внешних пассивных фильтров.

Чтобы сгенерировать синусоидальный сигнал, работающий на заданной частоте, мы должны поделить полный период на несколько шагов. Обычно более 200 шагов являются хорошим приближением для выходного сигнала. Это означает, что если мы хотим сгенерировать синусоидальный сигнал частотой 50 Гц, нам нужно выполнять преобразование каждые:

fсинусоиды = 50 Гц 200 = 10 кГц [2]

Цифро-аналоговое преобразование

400

Поскольку ЦАП STM32 имеет разрядность 12 бит, мы должны разделить значение 4095, соответствующее максимальному выходному напряжению, на 200 шагов, используя следующую формулу:

 

ЦАП

 

 

 

2

+1

 

4096

 

 

OUT

= sin x

 

 

 

 

 

 

 

 

 

 

n

 

 

 

2

 

 

 

 

 

 

 

 

 

 

 

 

s

 

 

где ns

– количество выборок, то есть 200 в нашем примере.

[3]

Используя приведенную выше формулу, мы можем сгенерировать вектор инициализации, подаваемый на ЦАП в режиме DMA. Как и для периферийного устройства АЦП, мы можем использовать таймер, сконфигурированный для запуска линии TRGO на частоте, указанной в [2]. В следующем примере показано, как генерировать синусоидальный сигнал частотой 50 Гц с использованием ЦАП в микроконтроллере STM32F072.

 

Имя файла: src/main-ex1.c

 

 

 

7

#define PI

3.14159

8

#define SAMPLES

200

9

 

 

10/* Переменные ----------------------------------------------------------------*/

11DAC_HandleTypeDef hdac;

12TIM_HandleTypeDef htim6;

13DMA_HandleTypeDef hdma_dac_ch1;

14

15/* Прототипы функций ---------------------------------------------------------*/

16static void MX_DAC_Init(void);

17static void MX_TIM6_Init(void);

18

19int main(void) {

20uint16_t IV[SAMPLES], value;

22HAL_Init();

23Nucleo_BSP_Init();

25/* Инициализация всей сконфигурированный периферии */

26MX_TIM6_Init();

27MX_DAC_Init();

29for (uint16_t i = 0; i < SAMPLES; i++) {

30value = (uint16_t) rint((sinf(((2*PI)/SAMPLES)*i)+1)*2048);

31IV[i] = value < 4096 ? value : 4095;

32

}

33

 

34HAL_DAC_Init(&hdac);

35HAL_TIM_Base_Start(&htim6);

36HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*)IV, SAMPLES, DAC_ALIGN_12B_R);

38while(1);

39}

40

41 /* Функция инициализации ЦАП */

Цифро-аналоговое преобразование

401

42void MX_DAC_Init(void) {

43DAC_ChannelConfTypeDef sConfig;

44GPIO_InitTypeDef GPIO_InitStruct;

46

__HAL_RCC_DAC1_CLK_ENABLE();

47

 

48/**Инициализация ЦАП */

49hdac.Instance = DAC;

50HAL_DAC_Init(&hdac);

51

52/**Конфигурация канала OUT1 ЦАП */

53sConfig.DAC_Trigger = DAC_TRIGGER_T6_TRGO;

54sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;

55HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1);

57/**Конфигурация GPIO ЦАП

58PA4 ------> DAC_OUT1

59*/

60GPIO_InitStruct.Pin = GPIO_PIN_4;

61GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

62GPIO_InitStruct.Pull = GPIO_NOPULL;

63HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

65/* Инициализация DMA периферии*/

66hdma_dac_ch1.Instance = DMA1_Channel3;

67hdma_dac_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;

68hdma_dac_ch1.Init.PeriphInc = DMA_PINC_DISABLE;

69hdma_dac_ch1.Init.MemInc = DMA_MINC_ENABLE;

70hdma_dac_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

71hdma_dac_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

72hdma_dac_ch1.Init.Mode = DMA_CIRCULAR;

73hdma_dac_ch1.Init.Priority = DMA_PRIORITY_LOW;

74

HAL_DMA_Init(&hdma_dac_ch1);

75

 

76__HAL_LINKDMA(&hdac,DMA_Handle1,hdma_dac_ch1);

77}

78

79

80/* Функция инициализации TIM6 */

81void MX_TIM6_Init(void) {

82TIM_MasterConfigTypeDef sMasterConfig;

84

__HAL_RCC_TIM6_CLK_ENABLE();

85

 

86htim6.Instance = TIM6;

87htim6.Init.Prescaler = 0;

88htim6.Init.CounterMode = TIM_COUNTERMODE_UP;

89htim6.Init.Period = 4799;

90HAL_TIM_Base_Init(&htim6);

91