Статью можно рассматривать дополнением к статье про ШИМ без 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