stm32f103 непонятки с таймером

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Использую следующий скетч:

#define pin_sync  (*((volatile unsigned long *) 0x422181A4 )) // PB9

void startHS() {   // начало импульса HS, переводится из высокого состояния в низкое
  pin_sync = 0;
}

void endHS() {   // конец импульса HS
  pin_sync = 1;
}

void screenInit() {
    GPIOB_BASE->CRL = 0xb4444444;     // PB7 - альтернативный выход, остальные - вход (не исп.)
    GPIOB_BASE->CRH = 0x44444434;     // PB9 - выход, остальные - вход (не исп.)

    TIMER4_BASE->CR1 = 0x80; // выключаем таймер

    TIMER4_BASE->PSC = 0;
    TIMER4_BASE->CCER = 0x0033;  // устанавливаем только 1 и 2 каналы, причем, с инверсией
    TIMER4_BASE->CCR1 = 1;    // 1 - точка отсчета, условный 0, - начало импульса вертикальной синхронизации
      
    Timer4.attachCompare1Interrupt(startHS); // начало HS
    Timer4.attachCompare2Interrupt(endHS); // конец HS

    TIMER4_BASE->CCR2 = 119; // длительность отрицательного полупериода
    TIMER4_BASE->ARR  = 239; // длительность всего периода
    TIMER4_BASE->CNT = 0;
    TIMER4_BASE->CR1 = 0x81; // запускаем таймер
}

void setup() {
  screenInit();
}

void loop() {}

Пояснения: на пине PB7 аппаратно генерируется отрицательный импульс, на пине PB9 программно в прерываниях генерируется отрицательный импульс.

Осциллограмма (голубой - PB7, желтый - PB9):

Теперь, собственно, два вопроса:

1. Почему на PB7 "0" существенно выше, чем на PB9 и достигает почти одного вольта?

2. Почему запаздывание "программного" импульса относительно аппаратного составляет более мкс, т.е. порядка 80 тактов, тогда как по документации должно быть где-то 12+2 - 12+4?

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

Насчет уровня 0 - непонятно. Может где подтягивающие резисторы с относительно малым сопротивлением стоят?

А запаздывание - можно посмотреть в какой ассемблерный код все это оттранслировалось? Может там в обработчике прерывания преамбула длинная? Например запихивается куча всего в стек для сохранения состояния.

nik182
Offline
Зарегистрирован: 04.05.2015

Что происходит с уровнем когда на PB7 выдается сигнал прямого программного импульса? Пока только одна идея - плохо пропаянная нога. Но это в качестве бреда. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

проца не знаю, но смею предположить, что проблема эта вытекает из-за разделения пина PB7 на USART
На WAVGAT имеем именно эту проблему, где-то озвучивал здесь и приводил осциллограммы

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

andriano, проверил -у меня одинаковые уровни :) По второму вопросу скорее всего да, от входа в __irq_tim4 до выполнения Start_Hs  проходит много всяких разных команд и проверок.. Как вариант написать обработчик самостоятельно :)

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Стало любопытно, сам попробывал. Всё равно дельта 600nS получается -многовато однако,   копаться в портянках генерируемого асм-кода тяжеловато :(

#define pin_sync  (*((volatile unsigned long *) 0x422181A4 )) // PB9
void setup() {
    GPIOB_BASE->CRL = 0xb4444444;     // PB7 - альтернативный выход, остальные - вход (не исп.)
    GPIOB_BASE->CRH = 0x44444434;     // PB9 - выход, остальные - вход (не исп.)
    TIMER4_BASE->CR1 = 0x80; // выключаем таймер
    TIMER4_BASE->PSC = 0;
    TIMER4_BASE->CCER = 0x0033;  // устанавливаем только 1 и 2 каналы, причем, с инверсией
    TIMER4_BASE->CCR1 = 1;    // 1 - точка отсчета, условный 0, - начало импульса вертикальной синхронизации
    TIMER4_BASE->DIER = 6;
     nvic_irq_enable(NVIC_TIMER4);
    TIMER4_BASE->CCR2 = 119; // длительность отрицательного полупериода
    TIMER4_BASE->ARR  = 239; // длительность всего периода
    TIMER4_BASE->CNT = 0;
    TIMER4_BASE->SR = 0;
    TIMER4_BASE->CR1 = 0x81; // запускаем таймер
}

extern "C" void __irq_tim4(){     
byte x = TIMER4_BASE->SR&6;
if (x==2) pin_sync = 0;  
 else if (x==4) pin_sync = 1;
 TIMER4_BASE->SR =0; 
}

void loop() {}

ssss
Offline
Зарегистрирован: 01.07.2016

dimax пишет:

копаться в портянках генерируемого асм-кода тяжеловато :(

А надо бы... иначе - ни о чём... И второе... Зависит от задачи... от желаемого... а то - замутить хардварно и не мучаться...

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Поменял местами вывод от таймера и от прерываний: высокое напряжение нуля по прежнему на PB7, следовательно, это аппаратный дефект.

Листинг процедур прерываний такой (вариант после "поменял местами")

08002140 <_Z7startHSv>:
 8002140:	4b01      	ldr	r3, [pc, #4]	; (8002148 <_Z7startHSv+0x8>)
 8002142:	2200      	movs	r2, #0
 8002144:	601a      	str	r2, [r3, #0]
 8002146:	4770      	bx	lr
 8002148:	4221819c 	eormi	r8, r1, #156, 2	; 0x27

0800214c <_Z5endHSv>:
 800214c:	4b01      	ldr	r3, [pc, #4]	; (8002154 <_Z5endHSv+0x8>)
 800214e:	2201      	movs	r2, #1
 8002150:	601a      	str	r2, [r3, #0]
 8002152:	4770      	bx	lr
 8002154:	4221819c 	eormi	r8, r1, #156, 2	; 0x27

вроде ничего лишнего.

Добавил в loop() меандр:

void loop() {
  static int i = 1;
  PB08_Out = i = !i;
}

при этом оказалось, что при выбранной частоте прерываний (300 кГц) практически все время камень проводит в прерываниях - частота программно сгенеренного сигнала составила всего 50 кГц, т.е. лишь один проход цикла за 6 прерываний. Уменьшил частоту прерываний до 100 кГц и получил следующее:

внизу - аппаратный таймер, в середине - через прерывания, а вверху - программно сгенеренный в loop() сигнал.

Собственно, видно, что в программно сгенеренном сигнале (примерно 1 МГц) происходят выпадения длительностью по 2 мкс на каждое прерывание (т.е. выпадение происходит вокруг передних и задних фронтов, формируемых прерываниями).

В общем, непонятно, что делает камень 2 мкс (более 140 тактов) в прерывании, состоящем из четырех строчек.

 

PS. dimax, спасибо: совсем уже было запутался, как взаимодействует таймер и прерывания, но твой код многое для меня прояснил.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Оказывеается, непонятки не ушли - при попытке откомпилировать скетч из сообщения №5:

sketch_may31b.ino: In function 'void __irq_tim4()':
sketch_may31b.ino:4:17: error: previous declaration of 'void __irq_tim4()' with 'C++' linkage
sketch_may31b.ino:18:28: error: conflicts with new declaration with 'C' linkage
previous declaration of 'void __irq_tim4()' with 'C++' linkage

попытки удалить "С" либо заменить на "С++" приводят к тому, что скетч компилируется, но прерывание не вызывается.

На всякий случай:

Arduino AVR ver 1.6.21
Arduino SAM ver 1.6.11
STM32 версия не определяется, дата 02.07.2018
nik182
Offline
Зарегистрирован: 04.05.2015

У меня стоит 2 ядра под stm32 - одно под блюпилл - под неё не компилится, a ошибки:  GPIOB_BASE->CRL = 0xb4444444; error: base operand of '->' is not a pointer ... второе Generic stm32f103c series - под него компилится нормально : Скетч использует 15100 байт (23%) памяти устройства. Всего доступно 65536 байт. Глобальные переменные используют 3088 байт (15%) динамической памяти, оставляя 17392 байт для локальных переменных. Максимум: 20480 байт.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

У меня как раз джинерик...
Сейчас попытался на другом компьютере - там, вроде, все то же, только версия AVR 1.6.20. Там откомпилировалось. Теперь надо чесать репу, как перенести все это поближе к осциллографу.
Но, в принципе, раз понятно, что дело в версиях, значит, известно, куда копать.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

andriano, подозреваю, что из-за старого IDE  (у меня IDE -1.8.9 , SAM Boards 1.6.12 , аддон stm32 -текущий последний.)

Пересчитал свои  600nS в такты -примерно 43, в принципе уже не кажется так чудовищно много. Как минимум 12 тактов  забирает NVIC. Осталось понять чем занимается  МК ещё 30 тактов, хорошо бы попробывать в кейле, а то к ардуино нет доверия :)

nik182
Offline
Зарегистрирован: 04.05.2015
TIM4_IRQHandler:
    0x80003ec: 0xb580         PUSH      {R7, LR}
    0x80003ee: 0x2101         MOVS      R1, #1
    0x80003f0: 0x4840         LDR.N     R0, [PC, #0x100]        ; TIM4_CR1
    0x80003f2: 0xf7ff 0xff36  BL        TIM_ClearITPendingBit   ; 0x8000262
    0x80003f6: 0x4840         LDR.N     R0, [PC, #0x100]        ; GPIOB_ODR
    0x80003f8: 0x6800         LDR       R0, [R0]
    0x80003fa: 0xf490 0x7000  EORS.W    R0, R0, #512            ; 0x200
    0x80003fe: 0x493e         LDR.N     R1, [PC, #0xf8]         ; GPIOB_ODR
    0x8000400: 0x6008         STR       R0, [R1]
    0x8000402: 0xbd01         POP       {R0, PC}
main:....
Сделал в IAR на SPL. Прерывание превратилось в 48 тактов процессора. Без очистки бита прерывания нельзя никак.
void TIM4_IRQHandler(void)
{
 //       if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
   //     {
            TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
            GPIOB->ODR ^= GPIO_Pin_9;
     //   }
}

 

 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

nik182, а какая дельта между фронтами получилась?

nik182
Offline
Зарегистрирован: 04.05.2015

Осцилографа пока нет. Через пару дней смогу точно сказать. Я в ИАРе по тактам ядра прерывание смотрел.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

nik182, или кинь *.bin, я посмотрю :)

nik182
Offline
Зарегистрирован: 04.05.2015

https://cloud.mail.ru/public/x3TR/5iCn2gZLb

По ссылке текст и бинарник. Ноги B6 и B7 выходы первого и второго каналов. В8 программный, В9 меандр из цикла main. Если нужен другой формат - скажи какой. После включения на блюпиле должен загораться диод. У меня получилось меньше 100 наносек.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

nick182, к сожалению не получилось залить :(  Утилита заливки stlink_cli ругается:
This file type is not supported!
 Supported file type are bin, hex, s19 and srec.
Unable to open file!

Смена расширения на bin не помогла. Бутлоадер заливает без ругани, но на выходах ничего нет.

 

nik182
Offline
Зарегистрирован: 04.05.2015

https://cloud.mail.ru/public/51iC/2nGPdtW6P

Этот залился через st-link utility. На моём допотопном С-94 после танцев с внешним запуском сдвиг получился 610 нс. Тоже самое.

ssss
Offline
Зарегистрирован: 01.07.2016

nik182 пишет:

Прерывание превратилось в 48 тактов процессора. Без очистки бита прерывания нельзя никак.

Ну его же можно очистить уже после инверсии пина, как бы тоже сокращает время. Вот если бы не было изначальной проверки флага, то тогда бы пришлось тулить барьер или НОПы, а так - сброс флага можно опустить и вниз не боясь.

 

nik182
Offline
Зарегистрирован: 04.05.2015

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka16366.html

Всё может  быть в этой жизни. Советуют перенести программу прерывания в RAM, что бы такты задержки чтения флэш на суммировать во время реакции.

И косвенное подтверждение http://we.easyelectronics.ru/STM32/vypolnenie-koda-iz-ozu-v-iar.html

При переводе в RAM крутится быстрее, хотя клок 24МГц. У нас 72. 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

nik182 пишет:
  У меня получилось меньше 100 наносек.

100 ns это что? Со второй попытки заливка удалась.  Снял картинку анализатором.

В общем всё тоже самое. Дырки на сигнале B9 судя по картинке вызваны прерыванием . Просто любопытно, как узнать чем всё таки занимается МК от флажка события таймера до начала импульса в прерывании. Такое ощущение, что ничем, просто стоит и чего-то ждёт :)

ssss
Offline
Зарегистрирован: 01.07.2016

dimax пишет:

Дырки на сигнале B9 судя по картинке вызваны прерыванием . Просто любопытно, как узнать чем всё таки занимается МК от флажка события таймера до начала импульса в прерывании.

Да ничем... ядро то одно... И в доках же всё конкретно расписано... сброс конвейера, вход в прерывание, сохранение контента, выполнение тела прерывания, возврат контента, выход из прерывания... И что такое 1000ns... 72 такта всего... Там только на контенте 12+12=24 такта, как минимум, теряется... остаётся - меньше 48 тактов... И это если нет других более приоритетных прерываний... ДМА и пр. ...

Хотите быстрее... придётся осваивать хардварные решения...