STM32F0. 3-фазный DMA PWM. Настройка TIM1, TIM2, TIM14, DMA, ADC, обработка их прерываний. Настройка функции DMA Burst

Статью можно рассматривать дополнением к статье про ШИМ без DMA, как стремление к совершенству! )))))))))))))))))

Модное слово DMA… Частота несущей сразу существенно подсела. Обработка прерываний имеет время. Занятая шина данных, тоже имеет значение.

Именно этот эксперимент открыл глаза на то, что прерывания TIM1 нужно выключать.

Синусоида в конце полупериода получалась несколько смятой. Причин не нашел.

const int TIM1_DMA_number = 3;

static volatile short Duty_Cycle_Tab_03 [3] = {0, 0, 0};

// Буфер в ОЗУ, содержащий данные, которые должны быть переданы DMA в регистры CCR1, CCR2, CCR3

//------------------------------------------------------------------------------

void  TIM1_BRK_UP_TRG_COM_IRQHandler (void) // Прерывание при сбое, обновлении, пуске и коммутации таймера TIM1

{

If (TIM1->SR & TIM_SR_UIF)   {

TIM1->SR &= ~TIM_SR_UIF;    // Сбрасываем флаг прерывания по переполнению

}

If (TIM1->SR & TIM_SR_BIF)   {

Driver_Off      // Запретить работу ключей драйверов полевиков "оборвать провод"

Mod_F = 0;      // Программный стоп

Act_F = 0;      // до обработки включения

TIM1->SR &= ~TIM_SR_BIF;     // Сбрасываем флаг прерывания

// Сбрасываем флаг аварийного останова, причем он не сбросится, пока PB12 != 0.

}

}

//------------------------------------------------------------------------------

void  TIM2_IRQHandler (void)    // Глобальное прерывание от таймера TIM2

{

If (TIM2->SR & TIM_SR_UIF)   {

TIM2->SR &= ~TIM_SR_UIF;     // Сбрасываем флаг прерывания

if (Sin_counter-- > 0)  {

Duty_Cycle_Tab_03 [0] = *(p_Sin_A++);

Duty_Cycle_Tab_03 [1] = *(p_Sin_B++);

Duty_Cycle_Tab_03 [2] = *(p_Sin_C++);

//TIM1->DIER |= TIM_DIER_UDE;

}

else { if (Fl_Rev == 0) {

p_Sin_A = &Sin_A [0];

p_Sin_B = &Sin_B [0];

p_Sin_C = &Sin_C [0];

}

else {

p_Sin_A = &Sin_B [0];      // Реверс

p_Sin_B = &Sin_A [0];

p_Sin_C = &Sin_C [0];

}

Sin_counter = Sin_abcissa;

}

}

}

//----------------------------------------------------------

void  ADC1_COMP_IRQHandler (void)    // Прерывания от сторожевого таймера

{

if(ADC1->ISR & ADC_ISR_AWD)   {

TIM1->EGR |= TIM_EGR_BG;        // Установить флаг прерывания аварийного останова TIM1

ADC1->ISR |= ADC_ISR_AWD;     // Написано сбрасывается записью 1

ADC1->CR |= ADC_CR_ADSTP;       // ADC stop of conversion command

DMA1_Channel1->CCR &= ~DMA_CCR_EN;      // Выключить канал DMA1

// Не факт, что выключается до конца цикла опроса

}

}

//------------------------------------------------------------------

void  DMA1_Channel1_IRQHandler (void)    // Прерывания DMA ADC1

{

if (DMA1->ISR & DMA_ISR_HTIF1)   {    // Channel 1 Half Transfer flag

DMA1->IFCR |= DMA_IFCR_CHTIF1;

m_d->I_A        =  ADC1_Frame [0];      // Ток фазы A

m_d->I_B        =  ADC1_Frame [1];      // Ток фазы B

}

if (DMA1->ISR & DMA_ISR_TCIF1)   {    // Channel 1 Transfer Complete flag

DMA1->IFCR |= DMA_IFCR_CTCIF1;

m_d->I_C        =  ADC1_Frame [2];      // Ток фазы C

m_d->U_A_B      =  ADC1_Frame [3];      // Напряжение между фазами A и B

}

}

//------------------------------------------------------------------------------

// конфигурация таймера TIM1

// Генерация несущей. 3-фазный ШИМ с выравниванием по центру

// Установка предварительного делителя

//TIM1->PSC = PSC_Val - 1;   //      F=fCK_PSC/(PSC[0:15]+1)

// В SM32 внутренний RC генератор с частотой 48 МГц.

// Установим предварительный делитель равным 32.

// Счетчик считае 2 раза (симметричный режим).

// Значения счетчика будут инкрементироваться с частотой 41 Кгц:

// Коэффициент деления равен 1 при нулевом значении предварительного делителя

// Для получения коэффициента деления N, необходимо установить в N-1.

// Установка границы счета

//TIM1->ARR = Sin_Number;

// TIMx_ARR – регистр автоматической перезагрузки,

// счётчик считает от 0 до TIMx_ARR, или наоборот в зависимости

// от направления счёта, изменяя это значение, мы изменяем частоту ШИМ.

// Предварительная установка скважности

//TIM1->CCR1 = Sin_A [0];

//TIM1->CCR2 = Sin_B [0];

//TIM1->CCR3 = Sin_C [0];

// TIMx_CCRy[x – номер таймера, y – номер канала] – определяет коэффициент

// заполнения ШИМ. То есть, если в ARR мы запишем 1000, а в CCRy 300,

// то коэффициент заполнения при положительном активном уровне

// и прямом ШИМ будет равен 0.3 или 30%.

// Включаем режим канал в режим ШИМ

TIM1->CCMR1 |= TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;     // ШИМ режим 1 (OC1M = 110) | TIM_CCMR1_OC1M_0

TIM1->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1;     // ШИМ режим 1 (OC1M = 110) | TIM_CCMR1_OC2M_0

TIM1->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1;     // ШИМ режим 1 (OC1M = 110) | TIM_CCMR2_OC3M_0

// Это прямой ШИМ. Если записать "111" будет инверсный

TIM1->CR1 |= TIM_CR1_ARPE;//Включен режим предварительной записи регистра автоперезагрузки

TIM1->CCMR1 |= TIM_CCMR1_OC1PE; // Включаем регистр предварительной загрузки компатратора канала 1

TIM1->CCMR1 |= TIM_CCMR1_OC2PE; // Включаем регистр предварительной загрузки компатратора канала 2

TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // Включаем регистр предварительной загрузки компатратора канала 3

//TIM1->CR1 |= TIM_CR1_DIR; // Если установить, будет считать вниз

TIM1->CR1 |= TIM_CR1_CMS_0 | TIM_CR1_CMS_1; // Режим 3 выравнивания по центру

// (Center-aligned mode 3). Счетчик считает вверх и вниз поочередно.

// Флаги прерывания сравнения выхода каналов, настроенных на выход

// (CCxS=00 в регистре TIMx_CCMRx) устанавливаются только тогда,

// когда счетчик считает вверх или вниз

// Select active high polarity on OC1 (CC1P = 0, reset value), enable the output on OC1 (CC1E = 1)

// Выберите активную высокую полярность в OC1 (CC1P = 0, значение сброса), включите выход на OC1 (CC1E = 1)

// CC1P & CC1NP - выбор полярности 0: активный уровень высокий - "1". 1: активный уровень низкий - "0".

TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC1NE;

TIM1->CCER |= TIM_CCER_CC2E | TIM_CCER_CC2NE;

TIM1->CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE;

TIM1->BDTR |= Deadtime;       // Мертвое время. Константа расчитана из задержек конкретного железа.

TIM1->BDTR |= TIM_BDTR_BKE | TIM_BDTR_BKP;      // break input, active polarity = high

// Настройка функции DMA Burst

TIM1->DCR = (2 << 8) + ((((uint32_t)(&TIM1->CCR1)) - ((uint32_t)(&TIM1->CR1))) >> 2);

//TIM1->DCR |= (3 << 8) | 0xD;      // | TIM_DCR_DBA_1 Регистр управления DMA TIM1 (TIMx_DCR)

// TIM_DCR_DBL_0        00000000 00000001 xxxxxxxx xxxxxxxx  Длина вспышки DMA

// TIM_DCR_DBL_1        00000000 00000010 xxxxxxxx xxxxxxxx  Длина вспышки DMA

// Итого ТРИ регистра

// 00000: 1 пересылка, 00001: 2 пересылки, 00010: 3 пересылки... 10001: 18 пересылок

// TIM_DCR_DBA_1   xxxxxxxx xxxxxxxx 00000000 00000010

// DBA[4:0] : Базовый адрес DMA

// DBA определяется как смещение начиная с адреса регистра TIMx_CR1.

// 00000: TIMx_CR1,

// 00001: TIMx_CR2,

// 00010: TIMx_CR3,

// TIM1->CCR1 - 13-й по счету значит его адрес 0xD

//TIM1->BDTR |= TIM_BDTR_MOE;     // Разрешаем вывод сигнала на выводы

//TIM1->EGR |= TIM_EGR_UG;         // генерация обновления

TIM1->DIER |= TIM_DIER_BIE;      // Разрешить прерывание по аварийному останову

//TIM1->DIER |= TIM_DIER_UIE;

TIM1->DIER |= TIM_DIER_UDE;      // UDE: Разрешение запроса обновления DMA

//TIM1->CR1 |= TIM_CR1_CEN; // Запускаем счет

//NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn);

NVIC->ISER[0] = (1 << ((TIM1_BRK_UP_TRG_COM_IRQn) & 0x1D)); // Разрешить TIM1_BRK_UP_TRG_COM_IRQn, если что № 13  & 0x1F

// NVIC->ISER[1] |= 0x00000002; // Разрешить IRQ33, если бы у нас было больше 30 векторов. ))))

//NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 1);

//NVIC->IP[3] |= 0x00004000; // Приоритет TIM1_BRK_UP_TRG_COM_IRQn - ОДИН

//NVIC->IP[M] M = 13 DIV 4 = 3

//N = 13 MOD 4 = 1. Байты о-5 обнуляются контроллером.

//Остаются байты 6-7 под 4 уровня приоритета 0x0000С000 - приоритет ТРИ.

//i = NVIC_GetPriority(TIM1_BRK_UP_TRG_COM_IRQn);

//NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 1);

//------------------------------------------------------------------------------

// конфигурация таймера TIM2

// Установка предварительного делителя

//TIM2->PSC = 0;  // F=fCK_PSC/(PSC[0:15]+1) 100000

// Установка границы счета

//TIM2->ARR = Fr_Loc_Tim2 [0];      // TIMx_ARR – регистр автоматической перезагрузки,

TIM2->CR1 |= TIM_CR1_ARPE;        //Включен режим предварительной записи регистра автоперезагрузки

TIM2->CR1 |= TIM_CR1_DIR;         // считить вниз

//TIM2->EGR |= TIM_EGR_UG;          // генерация обновления

TIM2->DIER |= TIM_DIER_UIE;       // Разрешить прерывание по переполнению

NVIC->ISER[0] = (1 << ((TIM2_IRQn) & 0x1F));     // Разрешить TIM2_IRQn  & 0x1F

//NVIC_SetPriority(TIM2_IRQn, 1);                // Приоритет TIM3_IRQn - ТРИ

//k = NVIC_GetPriority(TIM2_IRQn);

//------------------------------------------------------------------------------

//------------------------------------------------------------------------------

// Настройка DMA ADC1

//  Включить передачу DMA на АЦП и круговом режиме

// Примечание: бит DMAEN в регистре ADC_CFGR1 должен быть установлен после фазы калибровки ADC

//      В этом примере, взятом из мануала, не было написано кого сначала нужно настраивать.

//      Это привело к тому, что запись DMA1 начиналась не с первого, а с последнего датчика регулярной группы!!!

DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));                 //  Настроить адрес регистра периферийных данных

//  В регистре SYSCFG прописано какой канал для какого устройства можно юзать см. Тав 29 мануала

DMA1_Channel1->CMAR = (uint32_t)(&(ADC1_Frame));                //  Настроить адрес памяти

DMA1_Channel1->CNDTR = N_Ch_ADC1;               //  Настроить количество байтDMA-передачи, которое должно выполняться по каналу DMA 1

//  NDT [15: 0]: Количество передаваемых данных (от 0 до 65535). Этот регистр может быть записан только в том случае,

//  если канал отключен. Как только канал включен, этот регистр доступен только для чтения, указывая оставшиеся байты,

//  которые должны быть переданы. Этот регистр уменьшается после каждой передачи DMA

DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_1 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC;

//      Не совсем понятно, почему в примере из мануала стоял DMA_CCR_MSIZE_0

//      Если данные считываются в память - массив int [n], то они 32-х разрядные.

//DMA_CCR_MEM2MEM       Memory to memory mode                   Hежим памяти в память

//      0: режим памяти в память отключен

//      1: включен режим памяти в память

//DMA_CCR_PL            Bits(Channel Priority level             Уровень приоритета канала

//      00: Низкий

//      01: Средний

//      10: Высокий

//      11: Очень высокий

//DMA_CCR_MSIZE         MSIZE[1:0] bits (Memory size)           Размер памяти

//      00: 8 бит

//      01: 16 бит

//      10: 32 бит

//DMA_CCR_PSIZE         PSIZE[1:0] bits (Peripheral size)       Размер периферии

//      00: 8 бит

//      01: 16 бит

//      10: 32 бит

//DMA_CCR_MINC          Memory increment mode                   Инкрементный режим адреса памяти

//      0: Режим увеличения памяти отключен

//      1: включен режим увеличения памяти

//DMA_CCR_PINC          Peripheral increment mode               Инкрементный режим адреса периферии

//      0: отключен периферийный режим увеличения

//      1: включен режим расширенного периферийного устройства

//DMA_CCR_CIRC          Circular mode                           Циклический режим

//DMA_CCR_DIR           Data transfer direction                 Направление передачи данных

//      0: чтение с периферийных устройств

//      1: чтение из памяти

//DMA1_Channel1->CCR |= DMA_CCR_TEIE;                             //  Разрешение прерывания ошибки передачи

//      DMA_CCR_TEIE          Transfer error interrupt enable   Разрешение прерывания ошибки передачи

//      0: прерывание TE отключено

//      1: прерывание TE включено

DMA1_Channel1->CCR |= DMA_CCR_HTIE;                             //  Разрешение прерывания с половинной передачей

//      DMA_CCR_HTIE    Half Transfer interrupt enable          Разрешение прерывания с половинной передачей

DMA1_Channel1->CCR |= DMA_CCR_TCIE;                             //  Разрешение прерывания с полной передачей

//      DMA_CCR_TCIE    Transfer complete interrupt enable      Разрешение прерывания с полной передачей

DMA1_Channel1->CCR |= DMA_CCR_EN;                               //  Включить канал DMA 1

//      DMA_CCR_EN      Channel enable                          Включить канал

NVIC_EnableIRQ(DMA1_Channel1_IRQn); // Разрешаем прерывание по каналу 1 DMA

//------------------------------------------------------------------------------

// Настройка DMA TIM1

DMA1_Channel5->CPAR = (uint32_t) (&(TIM1->DMAR));       // периферийный адрес канала DMA является адресом регистра DMAR

DMA1_Channel5->CMAR = (uint32_t)(&Duty_Cycle_Tab_03 [0]);

// Установить адрес памяти канала DMA - адрес буфера в ОЗУ, содержащий данные, которые должны быть переданы DMA в регистры CCRx

DMA1_Channel5->CNDTR = TIM1_DMA_number;

DMA1_Channel5->CCR |= DMA_CCR_MINC | DMA_CCR_CIRC | DMA_CCR_DIR | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_1 | DMA_CCR_EN;

//DMA1_Channel5->CCR |= DMA_CCR_HTIE;

//DMA1_Channel5->CCR |= DMA_CCR_TCIE;

//NVIC_EnableIRQ(DMA1_Channel4_5_IRQn); // Разрешаем прерывание по каналу 2 DMA

//------------------------------------------------------------------------------

// Настройка ADC

RCC->CR2 |= RCC_CR2_HSI14ON;                    // Активировать внутренний генератор HSI14 для АЦП

while ((RCC->CR2 & RCC_CR2_HSI14RDY) == 0)      // Дождаться

// RC-генератор HSI 14 МГц не может быть включен с помощью интерфейса АЦП, когда часы APB выбраны как часы ядра АЦП.

// HSI14-генератор (тактирование АЦП). Данный RC-генератор является одним из двух способов тактирования блока АЦП (ADC)

// (второй  способ – через тактовую частоту PCLK, делённую на 2 или 4)

/*

// Выключение. В мануале рекомендуют выключать прежде чем включать на всякий пожаоный.

if ((ADC1->CR & ADC_CR_ADEN) != 0) ADC1->CR |= ADC_CR_ADDIS;    // Если включен, выключить

while ((ADC1->CR & ADC_CR_ADEN) != 0) asm("nop");               // Дождаться

ADC1->CFGR1 &= ~ADC_CFGR1_DMAEN;                                // Выключить

// ADC_CR_ADEN - ADC enable control (Управление ADC включено)

// ADC_CR_ADDIS - ADC disable command (Команда отключения ADC)

// ADC_CFGR1_DMAEN - Direct memory access enable (Прямой доступ к памяти включен)

*/

// Калибровка

ADC1->CR |= ADC_CR_ADCAL;                                       // Калибровка АЦП

while ((ADC1->CR & ADC_CR_ADCAL) != 0) asm("nop");              // Дождаться

// ADC_CR_ADCAL - ADC calibration (Калибровка АЦП)

// Включение

ADC1->CR |= ADC_CR_ADEN;

while ((ADC1->ISR & ADC_ISR_ADRDY) == 0) asm("nop");

// ADC_ISR_ADRDY - ADC Ready (Управление ADC включено)

// ADC_CR_ADEN - ADC enable control (Управление ADC включено)

// Сторожевой таймер. Выше еще и обработчик прерываний, аналогичный кнопке "Стоп"

ADC1->CFGR1 |= ADC_CFGR1_AWDEN;         //  Включить сторожевой таймер во всей регулярной группе

ADC1->CFGR1 &= ~ADC_CFGR1_AWDSGL;       //  Включить сторожевой таймер по отдельному каналу

ADC1->TR = (vrefint_high << 16) + vrefint_low;          // 3767 = 4095-128

ADC1->IER = ADC_IER_AWDIE;           //  Разрешить прерывания от сторожевого таймера

// Бит 27:16 HT [11: 0]: Высокий порог аналогового сторожевого таймера

// Бит 11: 0 LT [11: 0]: Нижний порог аналогового сторожевого таймера

//ADC1->CFGR1 |= ADC_CFGR1_DISCEN;      // Прерывистый режим. Для запуска каждого преобразования,

// определенного в последовательности, требуется событие запуска оборудования или программного обеспечения

//ADC1->CFGR1 |= ADC_CFGR1_AUTOFF;        // Автоматически отключать по окончании последовательности

//ADC1->CFGR1 |= ADC_CFGR1_WAIT;        // Преобразование режима ожидания. Новое преобразование может начаться

// только в том случае, если предыдущие данные были обработаны, как только регистр ADC_DR был прочитан или бит EOC был очищен

// Штука приятная и позиций кода и потребления энергии. Но какналов в последовательности может быть всего 3 (Три).

//ADC1->CFGR1 |= ADC_CFGR1_OVRMOD;

// OVRMOD     0: Регистр ADC_DR сохраняется со старыми данными при обнаружении переполнения.

// OVRMOD     1: Регистр ADC_DR перезаписывается последним результатом преобразования при обнаружении переполнения.

//  Включить передачу DMA на АЦП и круговом режиме

ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | ADC_CFGR1_CONT;

//  ADC_CFGR1_DMAEN     Когда режим DMA включен, запрос DMA генерируется после преобразования каждого канала

//  ADC_CFGR1_DMACFG    Кольцевой режим DMA (DMACFG = 1)

//  ADC_CFGR1_CONT      Непрерывное преобразование

//ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE;     // Выберите HSI14, записав 00 в CKMODE (значение сброса)

// Источник тактирования                CKMODE[1:0]     Задержка между событием триггера

//                                                      и началом преобразования

// Выделенный 14 МГц тактовый сигнал            00      Задержка не является детерминированной (дребезг)

// PCLK, деленный на 2                          01      Задержка детерминирована (без дребезга)

//                                                                                                          и равна 2.75 тактов ADC

// PCLK, деленный на 4                                                              10           Задержка является детерминированной (без дребезга)

//                                                      и равна 2.625 тактам ADC

// Выбор регулярной группы

ADC1->CHSELR = ADC_CHSELR_CHSEL10 | ADC_CHSELR_CHSEL11 | ADC_CHSELR_CHSEL12 | ADC_CHSELR_CHSEL13;      // Выбрать каналы

// ADC1->CFGR1 ^= ADC_CFGR1_SCANDIR;    // изменить порядок опроса

// Порядок, в котором каналы будут сканироваться, можно настроить, запрограммировав бит бит SCANDIR в регистре ADC_CFGR1:

//      SCANDIR = 0: прямое сканирование. Канал 0 — канал 18

//      SCANDIR = 1: обратное сканирование. Канал 18 — канал 0

// Датчик температуры подключен к каналу ADC_IN16.

// Внутренний опорный сигнал напряжения VREFINT подключен к каналу ADC_IN17.

// Канал VBAT подключен к каналу ADC_IN18.

//ADC1->CHSELR = ADC_CHSELR_CHSEL18;// Выбрать VREFINT

// Как-то странно работает... в составе регулярной группы. Такое чувство, что его нужно опрашивать отдельно

ADC1->SMPR |= ADC_SMPR1_SMPR_0 | ADC_SMPR1_SMPR_1 | ADC_SMPR1_SMPR_2;      //

//      000:    1,5     тактов ADC

//      001:    7.5     ADC тактов

//      010:    13,5    тактов ADC

//      011:    28,5    тактовые циклы ADC

//      100:    41,5    тактов ADC

//      101:    55,5    тактов ADC

//      110:    71,5    тактов ADC

//      111:    239.5   Циклы синхронизации ADC

// Это время выборки должно быть достаточно для того, чтобы источник входного напряжения заряжал образец

// и удерживал конденсатор до уровня входного напряжения.

// С одним из 4 каналов в воздухе 55,5 тактов хватает

//ADC->CCR |= ADC_CCR_VREFEN; // Wake-up the VREFINT (only for VBAT, Temp sensor and VRefInt)

//Пробуждение VREFINT (только для VBAT, Temp sensor и VRefInt)

NVIC_EnableIRQ(ADC1_COMP_IRQn); // Разрешаем прерывание сторожевого таймера

//------------------------------------------------------------------------------

// Фрагмент кода включения и выключения конвертера. Выключение и включение так же происходит при изменении направления вращения.

if ((Act_F == 0) && (Mod_F == 0)) {

Driver_Off      // Запретить работу ключей дорайверов полевиков "оборвать провод"

TIM1->BDTR      &= ~TIM_BDTR_MOE;     // Отключаем выводы от TIM1

// Прекращаем тактирование таймеров TIM1, TIM2, TIM14

TIM1->CR1       &= ~TIM_CR1_CEN;

TIM2->CR1       &= ~TIM_CR1_CEN;

TIM14->CR1      &= ~TIM_CR1_CEN;

// Сбрасываем счетчики в обработчиках прерываний

Sin_counter = 0;

Div_St_Cr = 0; Mod_St_Cr = 0;

// Сбрасываем флаг включения

/*

// Выключаем ADC1. В мануале рекомендуют выключать прежде чем включать на всякий пожаоный.

if ((ADC1->CR & ADC_CR_ADEN) != 0) ADC1->CR |= ADC_CR_ADDIS;    // Если включен, выключить

while ((ADC1->CR & ADC_CR_ADEN) != 0) asm("nop");               // Дождаться

ADC1->CFGR1 &= ~ADC_CFGR1_DMAEN;                                // Выключить

// ADC_CR_ADEN - ADC enable control (Управление ADC включено)

// ADC_CR_ADDIS - ADC disable command (Команда отключения ADC)

// ADC_CFGR1_DMAEN - Direct memory access enable (Прямой доступ к памяти включен)

*/

ADC1->CR |= ADC_CR_ADSTP; // ADC stop of conversion command

M_Job = 0;

}

//------------------------------------------------------------------------------

// Включение

if ((Act_F == 1) && (Mod_F == 1) && (M_Job == 0)) {

// После останова по сторожевому таймеру в случае выключения ADC

// ADC1->CR |= ADC_CR_ADSTP; // ADC stop of conversion command

// Текущее преобразования отменяется, а счетчик DMA делает тик

// Массив регулярных значений сползает на 1 по строке.

DMA1_Channel1->CMAR = (uint32_t)(&(ADC1_Frame));        //  Настроить адрес памяти

DMA1_Channel1->CNDTR = N_Ch_ADC1;               //  Настроить количество байтDMA-передачи, которое должно выполняться по каналу DMA 1

DMA1_Channel1->CCR |= DMA_CCR_EN;       //  Включить канал DMA 1

ADC1->CR |= ADC_CR_ADSTART; // Start the ADC conversion

TIM2->PSC = 0;  // Без предделителя

// Установка границы счета

TIM2->ARR = Fr_Loc_Tim2 [1];    //      1600000

TIM2->EGR |= TIM_EGR_UG;        // генерация обновления

// При генерации обновления оьработчик загружает указатели массива синусов

TIM1->PSC = PSC_Val;            //      1

// Установка границы счета

TIM1->ARR = Sin_ordinate;         //      749

TIM1->EGR |= TIM_EGR_UG;        // генерация обновления

// Линейное приращение TIM14 не отрабатывается, так как не от куда считать.

TIM14->PSC = Tim14_Acc_PSC - 1; //      1200

// Установка границы счета

// Fr_Ass_Tim14[7] = 247

TIM14->ARR =  Fr_Ass_Tim14[10 - m_d->Acc_eng];  // Загружаем счетчик из массива ускорений

TIM14->EGR |= TIM_EGR_UG;       // генерация обновления

TIM1->BDTR |= TIM_BDTR_MOE;     // Разрешаем вывод сигнала на выводы

TIM1->CR1       |= TIM_CR1_CEN; // Запускаем счет

TIM2->CR1       |= TIM_CR1_CEN; // Запускаем счет

TIM14->CR1      |= TIM_CR1_CEN; // Запускаем счет

Driver_On

M_Job = 1;

}

}

// Проверена работа TIM1 под управлением DMA. Вернулся к генерации PWM с использованием прерываний.

// Синусоида при исползовании DMA несколько смята в конце полупериода на всех выходах таймера.

// Думаю, что DMA лучше использовать для генерации быстрого ШИМ с изменением частоты предделителем.

// Типичный пример такого использования - блок питания.

С уважением Петр.

Реализуем коммерческие проекты.
Возможна работа по договору подряда.

t654rk@mail.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.