Кто любит RISC в жизни, заходим, не стесняемся.
Пн дек 14, 2015 22:04:12
Столкнулись с проблемой и пока не одолели ее.
Есть 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 .
но вот как эти биты запрограммировать???
И правильно ли мы текст понимаем?
Кто-то с таким сталкивался???
Подскажите?
Если надо код выложу.
Вт дек 15, 2015 12:50:12
победили. Два бита надо было инициализировать. Нашли наконец-то функции.
Вот теперь чтобы усовершенствовать программу есть такая задача:
Выдача 3-х фазного ШИМ (треугольный). Для этого регистры сравнения надо обновлять по прерываниям по переполнению моментально.
Это возможно при использовании ДМА.
Смогли настроить два канала ДМА. И выдавать в два регистра сравнения.
Но вот в третий никак не получается.
Запуск каналов ДМА идет по одному событию - переполнение, второго по сравнения регистра ССR4.
можно было бы помудрить и с запуском по триггеру, но не удается пока. Кому код нужен - пишете кину.
Просто запуск второго канала ДМА идет как по сравнению, так и по триггеру.
Вт дек 15, 2015 12:53:37
А в сторону DMAR смотрели?
Пт дек 18, 2015 07:45:40
Нет не смотрели.
Глянули, но пока как сделать толком не поняли.
Сможете просветить?!
Заранее спасибо!
Пт дек 18, 2015 09:03:56
Да там всё просто. Настраивается таймер. Разрешается работа ДМА в DIER таймера. Настраивается DCR таймера где указывается начальный адрес регистра таймера и количество регистров которые нужно заполнять. Дальше настраивается ДМА. Адрес периферии это TIMx->DMAR. Остальное как обычно. По событию таймера ДМА перебросит значения из таблицы в памяти в регистры таймера с адреса и в количестве которые были указаны в DCR.
Чт дек 31, 2015 11:38:33
Вроде нашел данный режим в литературе - называется вспышка ДМА.
Но чтобы работал как как мне нужно - не получается!
Задание массива
unsigned int PWMArr1[9] =
{1200, 60, 1200,
1200, 1200, 60,
60, 1200, 1200}; // массив для формирования скважности по прерываниям таймера 1
Инициализация Таймера
static void TIM_Configuration(PWMDRV_InitTypeDef* pwm) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
// Настроим Таймер ШИМ-драйвера
//
// 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
и т.д.
Когда верно, когда нет.
Не могу понять в чем дело!
Вс янв 03, 2016 14:53:40
pos13 писал(а):То так
То так
Попробуйте настроить таймеры для остановки при отладке и посмотреть в чем дело.
DBG_TIM8_STOP
DBG_TIM1_STOP биты
Тогда в отладчике можно посмотреть, что происходит. Вы в отладчике видите состояние на момент просмотра регистров, но таймер постоянно идет, и этот момент может соответствовать любому состоянию таймера. Конечно силовые ключи нужно отключить, а то ведь таймеры будут остановлены вместе с процессором.
При отладке таймеров есть еще один момент. Таймеру все равно, кто прочитает регистры, процессор или отладчик. Это касается тех флагов что сбрасываются при чтении какого либо регистра.
Вс янв 03, 2016 16:16:44
Galizin писал(а):Попробуйте настроить таймеры для остановки при отладке и посмотреть в чем дело.
........
Конечно силовые ключи нужно отключить, а то ведь таймеры будут остановлены вместе с процессором.
........
При отладке таймеров есть еще один момент. Таймеру все равно, кто прочитает регистры, процессор или отладчик. Это касается тех флагов что сбрасываются при чтении какого либо регистра.
Всё это чревато и безнадёжно... Взять другую плату... без ключей и прочего... даже можно с другим МК... подключить логанализатор... обрезать текст проги до минимума... и гонять её с нуля как обычно без всяких там тормозов... Там всё дело в правильных настройках... ну и понимании принципа... А всякие там попытки извращения... это сродни футболу по минному полю...
Пн янв 04, 2016 12:57:23
HHIMERA писал(а):Всё это чревато и безнадёжно
- излишнее обобщение
HHIMERA писал(а):подключить логанализатор
можно и так судить о содержимом регистров таймера.
Обратите внимание на Bit OC1PE: Output Compare 1 preload enable. Это даст фору во времени.
Пн янв 04, 2016 13:54:36
Galizin писал(а):излишнее обобщение
Может быть...
можно и так судить о содержимом регистров таймера.
ИМХО это самый правильный и надёжный вариант... всё происходит в реальном времени и ничто ничему не мешает...
Обратите внимание на Bit OC1PE: Output Compare 1 preload enable. Это даст фору во времени.
Если это мне... то спасибо, я в курсе... Я очень активно применяю таймера и ДМА...
Вот пример применения DMAR в многоканальном 1-wire... хардварная реализация... На 7-ом канале висит датчик... на остальных пусто... На 6-ом диаграмма чтения порта на предмет состояния шин всех датчиков...
- Вложения
-
- TIM_DMAR.png
- (99.13 KiB) Скачиваний: 810
Пт янв 08, 2016 10:37:12
Еще раз все проверил.
Неправильно работали таймеры в предыдущем проекте.
Поменял в действующем, рабочем в итоге все сразу заработало и так, как надо.
Вот это добавил и все работает. А таймер действительно показывает содержимое регистров в разный момент времени.
//включение ДМА по переполнению таймера 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;
Так что в предыдущем моем коде все было правильно, просто где-то не так работали таймеры.
Пн сен 26, 2016 18:29:12
Указанная тема успешно решена и закрыта, но в процессе работы выявился толи глюк, толи баг, толи обычный косяк в программе.
Есть плата с кучей всего. Есть усилители выводы с которых идут на АЦП.
Так получается, что задействованы все 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 не работает как надо!!!
Помогите! может кто-то сталкивался с такой проблемой. Или ей подобной. Если надо будет - код предоставлю.
Вт апр 17, 2018 21:48:15
Приветствую уважаемые форумчане!!!
Опять столкнулся с проблемой. Сижу уже вторую неделю и наконец-то определил в чем дело, но как это исправить и из-за чего случается пока не понял.
Может кто сталкивался и поможет.
микроконтроллер использую следующий - 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). Но настройка не отличается от других каналов и АЦП и проектов, которые работают исправно.
Даже все дорожки пропаял, но ничего не изменилось.
Не пойму в чем дело.
Было у кого-то такое?!
Вс апр 22, 2018 10:47:07
Приведите схему включения входов АЦП.
У STM32 используется АЦП с уравновешиванием заряда, а не уравновешиванием тока. Во время выборок на входе создатся импульс тока, старающийся подтянуть вывод к середине напряжения питания. О причинах и следствии - гугл поможет.
Если вход подключен через резисторы БЕЗ буферного усилителя (выход ОУ -> вход АЦП) - то напряжение будет при достаточно больших номиналах делителя.
Powered by phpBB © phpBB Group.
phpBB Mobile / SEO by Artodia.