Jump to content

STM8L. Пустой бесконечный цикл тормозит тайминг.


n_angelo
 Share

Recommended Posts

Привет, знатоки. Написал свою первую программу для контроллера STM8L152C6T6 (STM8L-Discovery). Это, собственно, моя первая программа для контроллеров вообще. Я многого не знаю и не понимаю. Возможно ваш ответ на мой вопрос будет банален.

Используемая периферия: DAC, DMA, TIM4, CLK, GPIO

Задача у программы такая:

В EEPROM зашит один период синусоиды с дискретизацией 44100Гц. Период занимает ровно 101 байт, что по сути должно быть равно 2,29мс (1/44100*101). В коде программы только конфигурация периферии, одно прерывание на кнопке и пустой бесконечный цикл, который ничего не делает. Всю работу выполняет таймер, который настроен выдавать запрос к DMA на каждые 1/44100 (ядро тактируется 2мГц, таймер считает до 45). В свою очередь DMA забирает из EEPROM по одному байту на каждый запрос от таймера и передаёт его в DAC. Далее DAC выводит бесконечную синусоиду на ногу PF0. Прерывание на кнопке запускает весь этот механизм и зажигает светодиод.

Проблема:

Измеряя ногу PF0 осциллографом было замечено, что период синусоиды занимает около ≈4мс. Фото под катом.

Скрытый текст

DSC_0008.JPG.d3bf50618d847e1e21b7cf30ee1b20d8.JPG

Меня это расстроило. Экспериментально выяснилось, что стоит только вписать в бесконечный цикл какую-нибудь проверку, например, [если значение текущего байта синусоиды = 0xFF, то зажечь светодиод, если 0x00, то потушить], то осциллограф показывает правильный тайминг в 2(с копейками)мс. В принципе в теле цикла может быть что угодно, кроме пустоты, и тайминг налаживается.

Я не могу отдебажить дизассемблер, т.к. его не знаю. Это у меня в планах. Но я очень хочу понять, что происходит и почему пустой цикл рушит тайминг.

Спасибо.

 

 

 

 

Edited by n_angelo
Link to comment
Share on other sites

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

21.09.2019 в 20:10, n_angelo сказал:

что происходит и почему пустой цикл рушит тайминг

вот если ты нам расскажешь, будет очень интересно. Я бы померил, например, сколько времени занимает от входа в прерыванием до установки значения в ДАКе.

Можно сделать все! Но чем больше можно, тем больше нельзя!

Link to comment
Share on other sites

5 часов назад, ruhi сказал:

вот если ты нам расскажешь, будет очень интересно. Я бы померил, например, сколько времени занимает от входа в прерыванием до установки значения в ДАКе.

К сожалению, мой осциллограф одноканальный и показывает только то, что произошло от момента триггера. А график в дебаггере показывает, что один семпл (1/44100) выводится раз в 5 секунд.

Других способов измерить я пока не придумал.

99426537_2019-09-2319_46_02.png.fe57fbaaef3751de8f53225210fa437c.png

Link to comment
Share on other sites

Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. 

Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств. Подробнее параметры и результаты тестов новой серии PLM по ссылке.

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

19 часов назад, n_angelo сказал:

то, что произошло от момента триггера.

В коде в начале функции прерывания надо поставить инструкцию переключения уровня на любую доступную ногу процессора, перед инструкцией записи в ДАК тоже поставить переключение ноги (той же или другой - как тебе удобней) и посмотри осциллографом сигнал на этой ноге (ногах). Это вряд ли даст объяснение проблеме, но возможно наведет тебя на мысли в какую стороны копать, что еще посмотреть!

Можно сделать все! Но чем больше можно, тем больше нельзя!

Link to comment
Share on other sites

Как измерить внутреннее сопротивление литиевого аккумулятора. Измеряем правильно

Внутреннее сопротивление – один из наиболее значимых параметров, от которого напрямую зависят другие характеристики литиевого аккумулятора. От этого параметра зависят такие характеристики источника питания, как напряжение на выходе под нагрузкой, максимальный выходной ток и коэффициент полезного действия (КПД). Рассмотрим как измерить действительное значение внутреннего сопротивления АКБ, используя определенные методики. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

32 minutes ago, ruhi said:

в начале функции прерывания

Там нет никакого прерывания, вся работа по переносу байтов происходит по DMA в фоновом режиме без участия программы. То есть процессор непрерывно крутит пустой цикл, а таймер сам по себе тикает и с каждым тиком заставляет DMA читать память и записывать в ЦАП.

On 9/21/2019 at 6:10 PM, n_angelo said:

почему пустой цикл рушит тайминг.

Без Вашего кода в студии трудно что-либо сказать.

Link to comment
Share on other sites

3 часа назад, ruhi сказал:

В коде в начале функции прерывания надо поставить инструкцию переключения уровня на любую доступную ногу процессора, перед инструкцией записи в ДАК тоже поставить переключение ноги (той же или другой - как тебе удобней) и посмотри осциллографом сигнал на этой ноге (ногах). Это вряд ли даст объяснение проблеме, но возможно наведет тебя на мысли в какую стороны копать, что еще посмотреть!

Намёк понял. Буду думать.

Link to comment
Share on other sites

3 часа назад, Yurkin2015 сказал:

Там нет никакого прерывания, вся работа по переносу байтов происходит по DMA в фоновом режиме без участия программы. То есть процессор непрерывно крутит пустой цикл, а таймер сам по себе тикает и с каждым тиком заставляет DMA читать память и записывать в ЦАП.

Без Вашего кода в студии трудно что-либо сказать.

Наверное он имел ввиду самое первое прерывание, которое включает таймер.

Прилагаю код. Осциллограф выдаёт полный период за ≈4мс, хотя должен за ≈2,29мс.

main.c

Скрытый текст

#include "main.h"

#pragma vector=EXTI1_vector 
__interrupt void btnInterrupt(void) 
{
  PE_ODR |= (1<<LED); // led on
  TIM4_CR1 |= TIM4_CR1_CEN;
  EXTI_SR1 |= (0x02); //reset flag on handler exit
}

int main( void )
{
  // LED init
  PE_DDR |= (1<<LED);
  PE_CR1 |= (1<<LED);
  
  // BUTTON init
  PC_DDR &= ~(1<<BUTTON);
  PC_CR1 |= (1<<BUTTON);
  PC_CR2 |= (1<<BUTTON);
  
  // CLK init
  CLK_PCKENR1 |= CLK_TIM4;
  CLK_PCKENR1 |= CLK_DAC;
  CLK_PCKENR2 |= CLK_DMA1;
  
  // TIM4 init
  TIM4_ARR = TIM4_ARR_44100HZ;
  TIM4_DER |= TIM4_DER_EN;
  
  // DMA init
  DMA1_GCSR |= DMA1_GCSR_GEN;
  DMA1_C3CR |= DMA1_C3CR_MINCDEC;
  DMA1_C3CR |= DMA1_C3CR_CIRC;
  DMA1_C3CR |= DMA1_C3CR_DIR;
  DMA1_C3CR |= DMA1_C3CR_TCIE;
  DMA1_C3NDTR = 101;
  DMA1_C3M0ARH = 0x10; //0x1000 (10 - HBS)
  DMA1_C3M0ARL = 0x00; //0x1000 (00 - LBS)
  DMA1_C3PARH_C3M1ARH = 0x53; //0x5390 (53 - HBS)
  DMA1_C3PARH_C3M1ARL = 0x90; //0x5390 (90 - LBS)
  DMA1_C3CR |= DMA1_C3CR_EN;
  
  // DAC init
  DAC_CH1CR1 |= DAC_CH1CR1_EN;
  
  // interrupt init
  EXTI_CR1 |= (1<<BUTTON);
  asm("RIM");
  
  while(1)
  { 
  }
  
}

 

 

main.h

Скрытый текст

#define PC_IDR          *(unsigned char*)0x00500B
#define PC_DDR          *(unsigned char*)0x00500C
#define PC_CR1          *(unsigned char*)0x00500D
#define PC_CR2          *(unsigned char*)0x00500E
#define PE_ODR          *(unsigned char*)0x005014
#define PE_DDR          *(unsigned char*)0x005016
#define PE_CR1          *(unsigned char*)0x005017

#define CLK_PCKENR1     *(unsigned char*)0x0050C3
#define CLK_PCKENR2     *(unsigned char*)0x0050C4
#define CLK_TIM4        0x4
#define CLK_DAC         0x80
#define CLK_DMA1        0x10

#define TIM4_CR1        *(unsigned char*)0x0052E0
#define TIM4_ARR        *(unsigned char*)0x0052E9
#define TIM4_DER        *(unsigned char*)0x0052E3
#define TIM4_CR1_CEN    0x1
#define TIM4_ARR_44100HZ        0x2D
#define TIM4_DER_EN     0x1

#define DMA1_GCSR       *(unsigned char*)0x005070
#define DMA1_C3CR       *(unsigned char*)0x005093
#define DMA1_C3SPR      *(unsigned char*)0x005094
#define DMA1_C3NDTR     *(unsigned char*)0x005095
#define DMA1_C3M0ARH    *(unsigned char*)0x005099
#define DMA1_C3M0ARL    *(unsigned char*)0x00509A
#define DMA1_C3PARH_C3M1ARH    *(unsigned char*)0x005096
#define DMA1_C3PARH_C3M1ARL    *(unsigned char*)0x005097
#define DMA1_C3CR_MINCDEC       0x20    
#define DMA1_C3CR_CIRC          0x10
#define DMA1_C3CR_DIR           0x08
#define DMA1_C3CR_TCIE          0x02
#define DMA1_C3CR_EN            0x01
#define DMA1_GCSR_GEN           0x01

#define DAC_CH1CR1     *(unsigned char*)0x005380
#define DAC_DHR8       *(unsigned char*)0x005390 
#define DAC_CH1CR1_EN   0x01

#define EXTI_CR1        *(unsigned char*)0x0050A0
#define EXTI_SR1        *(unsigned char*)0x0050A3
#define EXTI1_vector    11

#define P0 0
#define P1 1
#define P2 2
#define P3 3
#define P4 4
#define P5 5
#define P6 6
#define P7 7
#define LED P7
#define BUTTON P1

 

Edited by n_angelo
Link to comment
Share on other sites

On 9/21/2019 at 6:10 PM, n_angelo said:

почему пустой цикл рушит тайминг.

Думается, дело в том, что DMA и ядро процессора используют одну и ту же системную шину для доступа к памяти и периферии. Когда имеем пустой цикл, то ядро ни хрена не делает, а только непрерывно занимает шину для бесконечных прыжков по памяти программ по кругу. Если заставить ядро что-нибудь делать внутри себя со своими регистрами, типа прибавлять единичку к регистру, то шина будет освобождаться на время, и в этот момент DMA будет пропихивать свои байты по свободной шине.

Ну, или установить приоритет DMA над ядром, чтобы шина освобождалась по первому же требованию DMA.

Я так думаю.

Link to comment
Share on other sites

20 минут назад, n_angelo сказал:

Наверное он имел ввиду самое первое прерывание, которое включает таймер.

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

Можно сделать все! Но чем больше можно, тем больше нельзя!

Link to comment
Share on other sites

18 минут назад, Yurkin2015 сказал:

Думается, дело в том, что DMA и ядро процессора используют одну и ту же системную шину для доступа к памяти и периферии. Когда имеем пустой цикл, то ядро ни хрена не делает, а только непрерывно занимает шину для бесконечных прыжков по памяти программ по кругу. Если заставить ядро что-нибудь делать внутри себя со своими регистрами, типа прибавлять единичку к регистру, то шина будет освобождаться на время, и в этот момент DMA будет пропихивать свои байты по свободной шине.

Ну, или установить приоритет DMA над ядром, чтобы шина освобождалась по первому же требованию DMA.

Я так думаю.

Моя благодарность! Помогло. Установил приоритет над ядром. Теперь тайминг правильный. Насколько приоритет над ядром может замедлить саму программу? Я так понимаю здесь нужно самому выбирать, либо работа в реалтайм связки (TIM->DMA->DAC) и пустить в жертву работу программы, либо никакого реалтайма в этой связке, но зато код будет работать без задержек? Насколько RTOS помогает с этим справиться?

Link to comment
Share on other sites

On 9/21/2019 at 6:10 PM, n_angelo said:

таймер считает до 45

 

3 minutes ago, n_angelo said:

может замедлить саму программу?

Ну, вот, пока таймер считает, DMA шину не трогает. То есть 1 раз из 45 тактов ядро будет подторможено, замедление составит в худшем случае 1/45, около 2%.

Link to comment
Share on other sites

23 минуты назад, n_angelo сказал:

Помогло. Установил приоритет над ядром.

Интересно! Оно же должно задерживать вывод КАЖДОГО значения в ДАК , почему же период целого синуса (правильно понимаю?) то меняется. У меня первая мысль была про это, но оно вроде на интегральный период синуса не должно влиять, джитер в периоде вывода самплов должен наблюдаться, - интересно! Хотя другого объяснения и нет!

Можно сделать все! Но чем больше можно, тем больше нельзя!

Link to comment
Share on other sites

  • 1 month later...

спасибо, интересное замечание. надо бы конечно собранный код посмотреть, что сгенерировал компилятор на пустой цикл... если он не пустой - работает нормально, а если пустой, то такая задержка на "аппаратные", не программные передачи данных между модулями микроконтроллера (в данном случае память->DAC)

Link to comment
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...