Столкнулись с проблемой и пока не одолели ее. Есть 6 датчиков тока для двух трехканальных преобразователя напряжения. Мы ими управляем ШИМ сделанных на двух таймерах, у каждого таймера используется три верхних и нижних ключа. АЦП тактируется от таймера 6, так что при снятии сигнала ШИМ с фазы, мы запускаем канал АЦП и оцифровываем сигнал с датчика тока. Проблема в том, что при каждом новом такте нам нужно опрашивать новый канал АЦП. Для этого задали по рангу опрос каналов - 6 шт. Которые должны поидее при приходе каждого нового тактового импульса от таймера 6 опрашиватся каждый в свой тактовый импульс. Но вот этого не происходит. Ставим режим ADC_Mode_Independent вроде другой не подходит. ADC_RegularChannelConfig(ADC1, 2, 1, ADC_SampleTime_7Cycles5); данной функцией выбираем 6 каналов с рангом от 1 до 6. ADC_ContinuousConvMode_Disable - преобразование ставим одиночное NbrOfRegChannel = 6; В итоге при запуске АЦП от триггера быстренько преобразовывает 6 каналов и вываливается в прерывание по ДМА. Но нам такое не нужно! Надо чтобы при каждом запуске преобразовывал один канал, при следующем запуске - другой. Ставили NbrOfRegChannel = 1; - он преобразует только первый канал. Хотя вот пишут, что есть биты, которые Как видим, каналы можно опрашивать в любой последовательности. Любой канал можно опрашивать несколько раз. Задаешь список, по которому опрашивать каналы, и все.
Количество каналов в этом списке задается разрядами L[3:0] регистра SQR1 . но вот как эти биты запрограммировать??? И правильно ли мы текст понимаем?
Кто-то с таким сталкивался??? Подскажите? Если надо код выложу.
победили. Два бита надо было инициализировать. Нашли наконец-то функции. Вот теперь чтобы усовершенствовать программу есть такая задача: Выдача 3-х фазного ШИМ (треугольный). Для этого регистры сравнения надо обновлять по прерываниям по переполнению моментально. Это возможно при использовании ДМА. Смогли настроить два канала ДМА. И выдавать в два регистра сравнения. Но вот в третий никак не получается. Запуск каналов ДМА идет по одному событию - переполнение, второго по сравнения регистра ССR4. можно было бы помудрить и с запуском по триггеру, но не удается пока. Кому код нужен - пишете кину. Просто запуск второго канала ДМА идет как по сравнению, так и по триггеру.
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
Да там всё просто. Настраивается таймер. Разрешается работа ДМА в DIER таймера. Настраивается DCR таймера где указывается начальный адрес регистра таймера и количество регистров которые нужно заполнять. Дальше настраивается ДМА. Адрес периферии это TIMx->DMAR. Остальное как обычно. По событию таймера ДМА перебросит значения из таблицы в памяти в регистры таймера с адреса и в количестве которые были указаны в DCR.
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
Вроде нашел данный режим в литературе - называется вспышка ДМА. Но чтобы работал как как мне нужно - не получается! Задание массива unsigned int PWMArr1[9] = {1200, 60, 1200, 1200, 1200, 60, 60, 1200, 1200}; // массив для формирования скважности по прерываниям таймера 1
// Настроим Таймер ШИМ-драйвера // // 1. Настроим двоичный делитель тактирующей частоты TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 2. Настроим поглощающий счетчик счетных импульсов TIM_TimeBaseStructure.TIM_Prescaler = 0; // почему было 0??? // 3. Активируем режим реверсивного счёта (inc/dec) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_CenterAligned1; // 4. Установим максимальное число для счёта TIM_TimeBaseStructure.TIM_Period = pwm->PeriodMax-0; // 5. Настроим поглощающий прерывания счетчик реверсов TIM_TimeBaseStructure.TIM_RepetitionCounter = 1 - 1; // и как ... сконфигурируем! TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
// Настроим выходы каналов Захвата / Сравнения таймера // // 1. Установим режим сравнения CCR >= CNT или CCR <= CNT TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM1 // 2. Активируем выход OCx Регистра Сравнения (верхний ключ) TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 3. Активируем выход OCNx Регистра Сравнения (нижний ключ) TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; // 4. Установим полярность сигнала OCx для верхнего ключа TIM_OCInitStructure.TIM_OCPolarity = pwm->CnfgReg.bit.SW_H_Polarity ? TIM_OCPolarity_High : TIM_OCPolarity_Low; // 5. Установим полярность сигнала OCNx для нижнего ключа TIM_OCInitStructure.TIM_OCNPolarity = pwm->CnfgReg.bit.SW_L_Polarity ? TIM_OCNPolarity_High : TIM_OCNPolarity_Low; // 6. Определим состояние выходов OCx для IDLE режима TIM_OCInitStructure.TIM_OCIdleState = pwm->CnfgReg.bit.SW_H_IdleState ? TIM_OCIdleState_Set : TIM_OCIdleState_Reset; // 7. Определим состояние выходов OCNx для IDLE режима TIM_OCInitStructure.TIM_OCNIdleState = pwm->CnfgReg.bit.SW_L_IdleState ? TIM_OCNIdleState_Set : TIM_OCNIdleState_Reset; // Установим число в Регистра Сравнения = половину от ARR TIM_OCInitStructure.TIM_Pulse = pwm->HalfPerMax; // TIM1, 8: Конфигурируем каналы TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC2Init(TIM1, &TIM_OCInitStructure); TIM_OC3Init(TIM1, &TIM_OCInitStructure); TIM_OC1Init(TIM8, &TIM_OCInitStructure); TIM_OC2Init(TIM8, &TIM_OCInitStructure); TIM_OC3Init(TIM8, &TIM_OCInitStructure);
// Настроим защиту моста (ST.com: DM00080497.pdf DM00042534.pdf) // // 1. Определим величину бестоковой паузы TIM_BDTRInitStructure.TIM_DeadTime = pwm->Deadband; // 2. [Де]Активируем Компаратор Защиты TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable; // 3. Укажем активный уровень для Компаратора Защиты TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; // 4. Установим уровень срабатывания Компаратора Защиты TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; // 5. Не используем однотактный режим управления мостом (Run-режим) TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; // 6. Не отключаем таймер от выходов при выключении ШИМ-а (Idle-режим) TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; // 7. Не используем автоматическое включение после срабатывания защиты TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Disable; // и как ... сконфигурируем! TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure); TIM_BDTRConfig(TIM8, &TIM_BDTRInitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_CtrlPWMOutputs(TIM8, ENABLE); // TIM1: прерывания по переполнению //TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); // TIM8: прерывания по переполнению TIM_ITConfig(TIM8, TIM_IT_Update, ENABLE);
//Начальное значение счетчика обеспечивающего фазовый сдвиг на 180 градусов TIM_SetCounter(TIM8, pwm->PeriodMax-1); //
//Конфигурация для таймера синхронизации АПЦ1
// 1. Настроим двоичный делитель тактирующей частоты TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 2. Настроим поглощающий счетчик счетных импульсов TIM_TimeBaseStructure.TIM_Prescaler = 0; // почему было 0??? // 3. Активируем режим реверсивного счёта (inc/dec) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 4. Установим максимальное число для счёта TIM_TimeBaseStructure.TIM_Period = pwm->PeriodMax-1; // 5. Настроим поглощающий прерывания счетчик реверсов TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // и как ... сконфигурируем! TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
// TIM6: Подаем на Триггер Событий (TRGO) сигнал обновления TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update); // TIM6: прерывания по переполнению TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
//включение ДМА по переполнению таймера 1 TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE); // указание места записи для ДМА и количество записываемых байт за раз TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_3Transfers);
Инициализация ДМА // Настроим контроллер ПДП для обслуживания TIM1_CH1 DMA_DeInit(DMA1_Channel5); // Укажем адрес периферийного устройства (УВВ) т.е. АЦП DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &TIM1->DMAR; // Укажем адрес массива в ОЗУ (для результатов преобразования) DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) &PWMArr1; // Укажем направление передачи данных: из ОЗУ в TIM1_CCR1 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Укажем размер массива для результатов преобразования DMA_InitStructure.DMA_BufferSize = 9; //3 // Укажем, что адрес TIM1_CH1 инкрементировать не нужно DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Укажем, что адрес в ОЗУ необходимо инкрементировать DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Укажем размер данных по указанному адресу периферии DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // Укажем размер данных по указанному адресу в ОЗУ DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // Укажем режим кольцевого буфера для канала ПДП DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// DMA_Mode_Normal; // // Установим приоритет для канала ПДП DMA_InitStructure.DMA_Priority = DMA_Priority_High; // Укажем, что канал не используется для передачи данных из ОЗУ в ОЗУ DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // Инициализируем канал контроллера ПДП DMA_Init(DMA1_Channel5, &DMA_InitStructure); // Сбрасываем флаг прерывания по завершению передачи DMA_ClearFlag(DMA1_FLAG_TC5); // Разрешаем прерывание по завершению передачи //DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE); // Включаем канал контроллера ПДП DMA_Cmd(DMA1_Channel5, ENABLE);
Вроде переписывает данные из массива в регистры сравнения, но как-то не так. Должен поидее по 3 ячейки памяти в три регистра сравнения, потом еще три, и еще три, а потом с самого начала. unsigned int PWMArr1[9] = {1200, 60, 1200, 1200, 1200, 60, 60, 1200, 1200}; На первом шаге д.б. ССR1=1200 CCR2=60 CCR3=1200 На втором шаге д.б. ССR1=1200 CCR2=1200 CCR3=60 На третьем шаге д.б. ССR1=60 CCR2=1200 CCR3=1200
А получается примерно так - переписал ячейки из массива, но в какой-то произвольной последовательности То так ССR1=1200 CCR2=1200 CCR3=1200 То так ССR1=1200 CCR2=60 CCR3=60 и т.д. Когда верно, когда нет.
Попробуйте настроить таймеры для остановки при отладке и посмотреть в чем дело. DBG_TIM8_STOP DBG_TIM1_STOP биты
Тогда в отладчике можно посмотреть, что происходит. Вы в отладчике видите состояние на момент просмотра регистров, но таймер постоянно идет, и этот момент может соответствовать любому состоянию таймера. Конечно силовые ключи нужно отключить, а то ведь таймеры будут остановлены вместе с процессором. При отладке таймеров есть еще один момент. Таймеру все равно, кто прочитает регистры, процессор или отладчик. Это касается тех флагов что сбрасываются при чтении какого либо регистра.
Попробуйте настроить таймеры для остановки при отладке и посмотреть в чем дело. ........ Конечно силовые ключи нужно отключить, а то ведь таймеры будут остановлены вместе с процессором. ........ При отладке таймеров есть еще один момент. Таймеру все равно, кто прочитает регистры, процессор или отладчик. Это касается тех флагов что сбрасываются при чтении какого либо регистра.
Всё это чревато и безнадёжно... Взять другую плату... без ключей и прочего... даже можно с другим МК... подключить логанализатор... обрезать текст проги до минимума... и гонять её с нуля как обычно без всяких там тормозов... Там всё дело в правильных настройках... ну и понимании принципа... А всякие там попытки извращения... это сродни футболу по минному полю...
_________________ "Я не даю готовых решений, я заставляю думать!"(С)
можно и так судить о содержимом регистров таймера.
ИМХО это самый правильный и надёжный вариант... всё происходит в реальном времени и ничто ничему не мешает...
Цитата:
Обратите внимание на Bit OC1PE: Output Compare 1 preload enable. Это даст фору во времени.
Если это мне... то спасибо, я в курсе... Я очень активно применяю таймера и ДМА... Вот пример применения DMAR в многоканальном 1-wire... хардварная реализация... На 7-ом канале висит датчик... на остальных пусто... На 6-ом диаграмма чтения порта на предмет состояния шин всех датчиков...
Еще раз все проверил. Неправильно работали таймеры в предыдущем проекте. Поменял в действующем, рабочем в итоге все сразу заработало и так, как надо. Вот это добавил и все работает. А таймер действительно показывает содержимое регистров в разный момент времени.
//включение ДМА по переполнению таймера 1 TIM_DMACmd(TIM1, TIM_DMA_Update, ENABLE); // указание места записи для ДМА и количество записываемых байт за раз TIM_DMAConfig(TIM1, TIM_DMABase_CCR1, TIM_DMABurstLength_3Transfers);
// Укажем адрес периферийного устройства (УВВ) т.е. TIM1 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &TIM1->DMAR;
Так что в предыдущем моем коде все было правильно, просто где-то не так работали таймеры.
Указанная тема успешно решена и закрыта, но в процессе работы выявился толи глюк, толи баг, толи обычный косяк в программе.
Есть плата с кучей всего. Есть усилители выводы с которых идут на АЦП. Так получается, что задействованы все 4 АЦП у микроконтроллера. По 3 вывода каждый АЦП оцифровывает. По сигналу от таймера запускается АЦП 1 и 3 и оцифровывает свой вывод, указанный в секвенсоре. АЦП работают попарно. АЦП1 и АЦП2, АЦП3 и АЦП4. Есть вывод РВ14 - ADC4_IN4. Вот с которым проблемы. А также используется вывод РВ15 - ADC4_IN5 с которым проблем нету. В чем проблема: все 3 сигнала с АЦП4 должны быть равны или около значения 2290. Так вот с вывода РВ14 - ADC4_IN4- сигнал будет 2100. Если обрываю провод к этой ножке, подцепляю на общий или на плюс питания - сигнал не меняется. Если два рабочих вывода подключаю к общему, то сигнал РВ14 - ADC4_IN4 будет 1700. Менял этот "корявый" пин на другой - все нормально работает.
Также этот же проект залил в дискавери. И такая же хрень. Понимаю, что дело не в схеме, а в программе. Но где и как неясно. Все просмотрели. Главное соседний вывод РВ15 работает нормально. Инициализация одинаковая для РВ14 и РВ15. Но РВ14 не работает как надо!!!
Помогите! может кто-то сталкивался с такой проблемой. Или ей подобной. Если надо будет - код предоставлю.
Приветствую уважаемые форумчане!!! Опять столкнулся с проблемой. Сижу уже вторую неделю и наконец-то определил в чем дело, но как это исправить и из-за чего случается пока не понял. Может кто сталкивался и поможет. микроконтроллер использую следующий - STM32F303RB. Частота кварца 16 МГц. Используется для преобразователя напряжения. Есть несколько аналоговых входов - АЦП1 - 1, 4, 5(ток по каждой из фаз); АЦП2 - 4, 6 (входное и выходное напряжение). Рулит всем АЦП1. опрашивает с частотой 48 кГц по одному каналу 1-1,4,5; 2-6,4,6. и через 3 опроса прерывание по ДМА. т.е. частота прерываний 16кГц.При отсутствии напряжения входного и выходного сигнал с АЦП2 поидее должен быть около 0. Но это оказывается не так. Сигнал входного напряжения - нулевой с АЦП2 канал 4. А сигнал выходного напряжения с АПЦ2 канал 6 не нулевой. около 360. В пересчете на коэффициенты входного усилителя - около 5В. Но на входах 0!!! Если работает 1 канал преобразователя, то напряжение на выходе соответствует задаваемому напряжению. Но если включены 2 или 3 канал вместе, то уже напряжение на выходе на 5В меньше задаваемого. Но тоже все зависит от уровня задаваемого напряжения - 10-15В стоит мертво 5-10В, если выше 20В, то на 5В меньше сначала, а потом со временем становиться равным задаваемому. Вот в этом вся странность. Чем выше напряжение задаваемое - до 50В, тем быстрее выходит на правильное напряжение, но глюк этот есть все равно. В общем мысли в том, что как-то неправильно работает 6 канал АЦП2 (или 1). Но настройка не отличается от других каналов и АЦП и проектов, которые работают исправно. Даже все дорожки пропаял, но ничего не изменилось. Не пойму в чем дело. Было у кого-то такое?!
Приведите схему включения входов АЦП. У STM32 используется АЦП с уравновешиванием заряда, а не уравновешиванием тока. Во время выборок на входе создатся импульс тока, старающийся подтянуть вывод к середине напряжения питания. О причинах и следствии - гугл поможет. Если вход подключен через резисторы БЕЗ буферного усилителя (выход ОУ -> вход АЦП) - то напряжение будет при достаточно больших номиналах делителя.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 20
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения