Jump to content
n_angelo

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

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

Share this post


Link to post
Share on other sites
21.09.2019 в 20:10, n_angelo сказал:

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

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

Share this post


Link to post
Share on other sites
5 часов назад, ruhi сказал:

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

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

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

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

Share this post


Link to post
Share on other sites

Литиевые батарейки Fanso для систем телеметрии и дистанционного контроля

Системы телеметрии находят все более широкое применение во многих отраслях на промышленных и коммунальных объектах. Требования, предъявляемые к условиям эксплуатации приборов телеметрии и, как следствие, источников питания для них, могут быть довольно жесткими. Fanso предоставляет широкую линейку продукции, рассчитанной на различные условия эксплуатации, что позволяет подобрать батарейку для каждого конкретного применения, в том числе и для устройств телеметрии.

Подробнее

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

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

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

Share this post


Link to post
Share on other sites
32 minutes ago, ruhi said:

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

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

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

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

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

Share this post


Link to post
Share on other sites
                     

Приглашаем на вебинар Решения для построения ультразвуковых счетчиков жидкостей и газов на базе MSP430

Компэл совместно с Texas Instruments 23 октября 2019 приглашают на вебинар, посвященный системам-на-кристалле для построения ультразвуковых расходомеров жидкостей и газов на базе ядра MSP430. Вебинар проводит Йоханн Ципперер – эксперт по ультразвуковым технологиям, непосредственно участвовавший в создании данного решения. На вебинаре компания Texas Instruments представит однокристальное решение, позволяющее создавать точные недорогие счетчики жидкостей и газов.

Подробнее...

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

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

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

Share this post


Link to post
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

Share this post


Link to post
Share on other sites
On 9/21/2019 at 6:10 PM, n_angelo said:

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

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

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

Я так думаю.

Share this post


Link to post
Share on other sites
20 минут назад, n_angelo сказал:

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

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

Share this post


Link to post
Share on other sites
18 минут назад, Yurkin2015 сказал:

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

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

Я так думаю.

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

Share this post


Link to post
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%.

Share this post


Link to post
Share on other sites
1 минуту назад, Yurkin2015 сказал:

1 раз из 45 тактов ядро будет подторможено

Да, логично) Еще раз спасибо.

Share this post


Link to post
Share on other sites
23 минуты назад, n_angelo сказал:

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

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

Share this post


Link to post
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
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...

  • Similar Content

    • By n_angelo
      Привет. Хочу узнать ваше мнение. Я новичок в embedded. Можно сказать, что пришел с веба. Малость Python, JS, C. Меня, конечно, предупреждали начать с AVR, но я уверенный в себе решил сразу залезть на STM32. Вынашивая идею для проекта, параллельно курив Reference Manual и Data Sheet по STM32, я понял что его будет слишком жирно для проекта. Я перескочил на STM8L. И тут меня начал огорчать мир embedded. При переходе между stm8 и stm32 нужно менять IDE (TrueStudio на STVD). Во избежание таких курьёзов я пересаживаюсь на IAR. В процессе подключения родной библиотеки от ST, понимаю что библиотека от IAR для того же самого STM8L152C6T6 дико отличается (макросы, структуры). Привет веб-разработка. Как такое могло произойти, что под один и тот же контроллер ST даёт одну библиотеку, а IAR другую. И нигде в уроках тебя не предупредят об этом. Ну, ребят, у меня всего одна жизнь. Вы уже договоритесь там между собой? Придите к единому стандарту. Или они так решили новичков завендерлочить? Моё мнение (не претендует на правильное): пробираясь сквозь тернии популярной архитектуры ARM, инфраструктуры, инструментария, забываешь про бизнес-логику устройств. А еще просто пропасть между "я ничего не понимаю" и "господи, я зажег светодиод". Речь не о копипастерах с уроков, а действительно понимая что ты делаешь, в каком регистре, что меняешь. Это путь в 2000 (а то и больше) страниц на английском перечитанных по несколько раз, чтобы отоложилось. И в конце тебя ждут разные версии одной и той же библиотки в разных IDE. И сидишь вдупляешь... ну почему... я же в правильный регистр кладу правильную маску... ох, наболело. Такое ощущение что не для людей это всё делали, не для людей.
      Ваше мнение?
    • By German Churilin
      Добрый день всем! Столкнулся с проблемой, над решением которой бьюсь уже месяц - суть в том, что при пересылке аудио данных с контроллера в внешний цап на выходе цапа звучит белый шум(именно когда летят данные). Уже перепробовал разные конфигурации и параметры, да даже другой цап ставил - всё равно та же самая проблема. Может кто сталкивался, или у кого есть идеи почему так происходит?
      Контроллер stm32f407ve китайский, но вроде как рабочий, цап - pcm1606, вытащенный из двд-плеера.

      код инициализации i2s
      RCC_PLLI2SCmd(DISABLE); RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S); RCC_PLLI2SConfig(200,5); RCC_PLLI2SCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY) == RESET){}; //WS - word clock output GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_SPI2); GPIO_StructInit(&gpioInit); gpioInit.GPIO_Pin = GPIO_Pin_9; gpioInit.GPIO_Mode = GPIO_Mode_AF; gpioInit.GPIO_OType = GPIO_OType_PP; gpioInit.GPIO_PuPd = GPIO_PuPd_NOPULL; gpioInit.GPIO_Speed = GPIO_Speed_100MHz; GPIO_Init(GPIOB, &gpioInit); //BCLK - shift clock output GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2); gpioInit.GPIO_Pin = GPIO_Pin_10; GPIO_Init(GPIOB, &gpioInit); //SD - serial audio data GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2); gpioInit.GPIO_Pin = GPIO_Pin_3; GPIO_Init(GPIOC, &gpioInit); //MCO - master clock output GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_SPI2); gpioInit.GPIO_Pin = GPIO_Pin_6; GPIO_Init(GPIOC, &gpioInit); I2S_StructInit(&i2sInit); i2sInit.I2S_CPOL = I2S_CPOL_Low; i2sInit.I2S_Mode = I2S_Mode_MasterTx; i2sInit.I2S_MCLKOutput = I2S_MCLKOutput_Enable; i2sInit.I2S_Standard = I2S_Standard_Phillips; i2sInit.I2S_AudioFreq = I2S_AudioFreq_48k; i2sInit.I2S_DataFormat = I2S_DataFormat_24b; I2S_Init(SPI2, &i2sInit); SPI2->I2SPR = (uint16_t)((uint16_t)512 | (uint16_t)12 | (uint16_t)1); //for 48k Fs SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE); I2S_Cmd(SPI2, ENABLE);  
      код инициализации dma
      DMA_InitTypeDef dmaInit; NVIC_InitTypeDef nvicInit; DMA_DeInit(DMA1_Stream4); DMA_Cmd(DMA1_Stream4, DISABLE); while(DMA_GetCmdStatus(DMA1_Stream4) == ENABLE){}; __ISB(); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); //To dmaInit.DMA_PeripheralBaseAddr = (uint32_t) &(SPI2->DR); //From dmaInit.DMA_Memory0BaseAddr = (uint32_t)&AUDIO_SAMPLE; dmaInit.DMA_BufferSize = 20480; dmaInit.DMA_Channel = DMA_Channel_0; dmaInit.DMA_DIR = DMA_DIR_MemoryToPeripheral; dmaInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dmaInit.DMA_MemoryInc = DMA_MemoryInc_Enable; dmaInit.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; dmaInit.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; dmaInit.DMA_Mode = DMA_Mode_Circular; dmaInit.DMA_Priority = DMA_Priority_High; dmaInit.DMA_FIFOMode = DMA_FIFOMode_Enable; dmaInit.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; dmaInit.DMA_MemoryBurst = DMA_MemoryBurst_Single; dmaInit.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; while(DMA_GetCmdStatus(DMA1_Stream4) == ENABLE); DMA_Init(DMA1_Stream4, &dmaInit); DMA_ITConfig(DMA1_Stream4, DMA_IT_TC | DMA_IT_HT, ENABLE); //configure interrupt nvicInit.NVIC_IRQChannel = DMA1_Stream4_IRQn; nvicInit.NVIC_IRQChannelPreemptionPriority = 0; nvicInit.NVIC_IRQChannelSubPriority = 0; nvicInit.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvicInit); DMA_Cmd(DMA1_Stream4, ENABLE); while(DMA_GetCmdStatus(DMA1_Stream4) != ENABLE){}; AUDIO_SAMPLE просто wav скопированный через hex редактор и живущий в памяти контроллера, крутиться по кругу. Вроде как, в теории всё должно работать(снизу скрин из pulseview - снято логическим анализатором), к сожалению осциллограф пока что не приобрёл, так что проверить правильность частот не представляется возможнымб но всё же всё вроде как красиво.
      Формат i2s на контроллере совпадает с форматом на цапе - первый бит передаётся через 1 тик sck после смены ws
       
      П.С. подозреваю сразу вопросы про MSB/LSB - wav файл сам по себе little-endian, собственно как и контроллер, то есть тут не должно быть проблем, а i2s стандарт передаёт MSB первым, соответственно если у меня в памяти записано 0x64, 0x61, 0x61, 0x66, 0x02, 0x00, 0x85... то, судя по скрину всё передаётся правильно? или я что то не понимаю в этом всём? Но не в этом суть - в один момент тоже подумал что порядок не тот, но, к сожалению, попытки свапать байты местами(и побайтово, и по словам) не дали ни какого результата

×
×
  • Create New...