Перейти к содержанию
    

PWM,DMA, USB STM32F070

Имеется задача генерировать поток из 128 бит в таком коде: ячейка 62 мкс, импульс активен 17 или 45 мкс в зависимости от 0/1.  Ничего экзотического. Сделал сначала на прерываниях по апдейту таймера TIM3. Все работает, но только есть сбои, если USB подключен. Видимо длинные у него прерывания.

Решил перейти на DMA. Circular mode, 128 слов - значений для CCR1, выключаю по прерыванию "конец цикла". Теперь проблемы:

1. Пишут, что для надежной работы надо включать буфферизацию (OC1PE). Но тогда 2 первые бита неверные.

2. Если отключить OC1PE, то второй бит идет из массива по DMA, а первый - с прошлого раза. Понятно что DMA срабатывает по update, так что не удивительно.

Думаю как бороться. По первому пункту вроде работает без OC1PE, но х.з. Прошу совета. По второмы например:

а. Искуственно заставить сработать DMA. 

TIM3->EGR = 1; //UG

вроде помогает, если без буфферизации. Насколько кошерно?

б. Первое слово грузить руками, а потом запускать DMA на 127 слов. Лучше ли это?

И еще. Использую PWM mode 1. Думал что должно работать output compare mode, но не работает - на выходе пустота.

Кроме того нме надо 2 независимых канала. Можно CCR1 и CCR2 обновлять через DMA burst (=2) mode?

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, alexf сказал:

Все работает, но только есть сбои, если USB подключен. Видимо длинные у него прерывания.

Просто сделайте приоритет прерывания таймера выше, чем у USB.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

#define	SET_PRIO(irq, prio) do{ NVIC->IP[(irq) >> 2] = \
	(NVIC->IP[(irq) >> 2] & ~(0xFF << (((irq) & 3) << 3)))\
	| ((prio) << (((irq) & 3) << 3)); } while(0)


	SCB->AIRCR = 0
		| (PRI2_6 			<< SCB_AIRCR_PRIGROUP)		// 5 - 2.6
		| (SCB_AIRCR_KEY	<< SCB_AIRCR_VECTKEY);

	//---------------------------------------------------------------------------
	// SYSTEM PRIO
	//---------------------------------------------------------------------------
	SCB->SHP[0] = 0
		| (PRIO2_6_NORMAL	<< 24);		// SVCHandler

	SCB->SHP[1] = 0
		| (PRIO2_6_NORMAL	<< 16)		// PendSVC
		| (PRIO2_6_NORMAL	<< 24);		// SysTickHandler

	//---------------------------------------------------------------------------
	// HIGHEST (PRIO2_6_HIGH)
	//---------------------------------------------------------------------------
	SET_PRIO(NVIC_ISER0_USART1, 	PRIO2_6_HIGH);		// console

	SET_PRIO(NVIC_ISER0_CEC_CAN, 	PRIO2_6_LOW);		// can

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, adnega said:

Просто сделайте приоритет прерывания таймера выше, чем у USB.

Это первое что я сделал, но стали пропадать пакеты USB. Мой поток занимает 8 мс. Подозреваю что проблема с реализацией USB - взял с куба почти как есть HID. Все же хочется довести до ума DMA.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

45 минут назад, alexf сказал:

Это первое что я сделал, но стали пропадать пакеты USB. Мой поток занимает 8 мс. Подозреваю что проблема с реализацией USB - взял с куба почти как есть HID. Все же хочется довести до ума DMA.

Вы в прерывании таймера (16129 раз в секунду) бодро записываете нужные регистры и как можно быстрее выходите из обработчика - единицы мкс.

Если прерывание USB отложится или прервется на единицы мкс, то от этого ничего не должно теряться.

Разве нет?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, adnega сказал:

Просто сделайте приоритет прерывания таймера выше, чем у USB.

Не факт. Надо ещё убедиться что нигде в программе нет длинных запретов прерываний.

 

6 часов назад, alexf сказал:

Имеется задача генерировать поток из 128 бит в таком коде: ячейка 62 мкс, импульс активен 17 или 45 мкс в зависимости от 0/1.  Ничего экзотического. Сделал сначала на прерываниях по апдейту таймера TIM3. Все работает, но только есть сбои, если USB подключен. Видимо длинные у него прерывания.

Совет: Постарайтесь описать проблему техническим и общепринятым языком, с общепринятыми понятиями. Почти ничего из вашего описания не понятно. Что за "поток 128 бит"? Что за "ячейка"? Что за "импульс"? Что такое "активен"? Что такое "с прошлого раза"? ... Вы говорите на каком-то своём, Вам только понятном птичьем языке. Так никто ничего толкового не сможет Вам посоветовать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 hours ago, jcxz said:

Так никто ничего толкового не сможет Вам посоветовать.

Извините - я больше по железу, но попробую более толково. В управляемом удаленном устройстве длинный сдвиговый регистр - 128 бит, на который 10 раз в секунду посылаются пачки импульсов. В начале каждого интервала в 62 мкс ("ячейки") - фронт импульса, т.е. 1 на провод. Через 17 или 45 мкс - спад, возвращается в 0. Некий самосинхронизирующийся код, не я придумал. Нет особых хитростей - давно сделал на AVR, но в новой версии надо добавить USB, так что решил перейти на STM32.

То, как я сейчас сделал 1 канал, вроде работает, но хочу понять нет ли граблей в том, что отключил OCxPE бит. Я понимаю, что можно и без DMA довести до ума, но представляется что с DMA более правильно.

8 hours ago, adnega said:

Вы в прерывании таймера (16129 раз в секунду) бодро записываете нужные регистры и как можно быстрее выходите из обработчика - единицы мкс.

Это правда, но я в первом приближении сделал вычисление бита внутри прерывания, порядка 10 мкс. Думал USB не обидится, но, возможно, косяк в другом месте не заметил. Так или иначе, теперь хочу через DMA. Должно быть по всякому эффективнее. 

Еще вопрос: кто знает лучше реализацию USB, чем из куба? Я нашел https://github.com/dmitrystu/libusb_stm32 но на вскидку не собирается, если импортировать в Эклипс. Есть еще проверенные варианты?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 минут назад, alexf сказал:

Извините - я больше по железу, но попробую более толково. В управляемом удаленном устройстве длинный сдвиговый регистр - 128 бит, на который 10 раз в секунду посылаются пачки импульсов. В начале каждого интервала в 62 мкс ("ячейки") - фронт импульса, т.е. 1 на провод. Через 17 или 45 мкс - спад, возвращается в 0. Некий самосинхронизирующийся код, не я придумал. Нет особых хитростей - давно сделал на AVR, но в новой версии надо добавить USB, так что решил перейти на STM32.

Т.е. - период следования импульсов внутри "пачки" == 62 мкс, и таких импульсов 128 шт.? Что-то похожее на iButton интерфейс. Можно на ШИМ реализовать, можно на SPI или UART.

 

Цитата

Это правда, но я в первом приближении сделал вычисление бита внутри прерывания, порядка 10 мкс. Думал USB не обидится, но, возможно, косяк в другом месте не заметил. Так или иначе, теперь хочу через DMA. Должно быть по всякому эффективнее.

10 мкс вычисление? Многовато... Какая тактовая ядра?

 

Цитата

Еще вопрос: кто знает лучше реализацию USB, чем из куба? Я нашел https://github.com/dmitrystu/libusb_stm32 но на вскидку не собирается, если импортировать в Эклипс. Есть еще проверенные варианты?

Я обычно использовал USB-стеки из примеров IAR.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, jcxz said:

Какая тактовая ядра?

Была 16 в первом эксперименте. Ясно, что на 48 шустрее будет. Что до вычисления битов в реалном времени, то я просто портировал свой старый код на AVR. Теперь памяти много, можно спокойно держать весь массив по слову на бит.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

С 16-ю хотя-бы USB то будет работать? Имхо - маловато. И вообще полезно измерять загрузку CPU. Чтобы не гадать на кофейной гуще.

Но даже при 16МГц - 10 мкс как-то многовато. Что Вы там считаете так долго?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, jcxz said:

С 16-ю хотя-бы USB то будет работать? Имхо - маловато. И вообще полезно измерять загрузку CPU. Чтобы не гадать на кофейной гуще.

Но даже при 16МГц - 10 мкс как-то многовато. Что Вы там считаете так долго?

УСБ работал. А откуда 16? Так было на AVR, от лени оставил :)

А вот что считалось долго. Теперь все заранее делается.

 

void load_compare(){
  bl_row = (byte >> 1);
  bl_col = ((byte & 0x01) << 3) + bit;

  int8_t bit_2_blinks = ((balls_out_2[16] >> 4) == bl_row) &&
    ((bl_col == 0) || (bl_col == (balls_out_2[16] & 0x0f)));

  int8_t bit_1_blinks = ((balls_out[16] >> 4) == bl_row) &&
    ((bl_col == 0) || (bl_col == (balls_out[16] & 0x0f)));


  if(((balls_out[byte] & (1<<bit)) && !bit_1_blinks) || (BlinkOn && bit_1_blinks))
      htim22.Instance->CCR2 = 45*16;
      //OCR3A = 45*16; // 45
    else
        htim22.Instance->CCR2 = 17*16; // 17 uS

  if(flags & B_SIGNAL_ON_OUT2){ // could this be breaking B signal on intf 1 ?
    if(((balls_out_2[byte] & (1<<bit)) && !bit_2_blinks) || (BlinkOn && bit_2_blinks))
        htim22.Instance->CCR1 = 45*16; // 45
    else
        htim22.Instance->CCR1 = 17*16; // 17 uS
  }

}
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 hours ago, jcxz said:

можно на SPI или UART.

SPI у меня для другого, а на UART конечно можно. Думал об этом, но:

1. Если 10 бит - 62 мкс, то 3 бита 18.6 мкс. Не совсем 17. Скорее всего не проблема, но не хочу быть крайним, если вдруг не годится.

2. Все равно надо кормить UART каждые 62 мкс. Чем лучше чем PWM?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, alexf сказал:

2. Все равно надо кормить UART каждые 62 мкс. Чем лучше чем PWM?

Да ничем, примерно тоже. Правда если у вас нужны не произвольные, а только пара фиксированный длительностей импульсов, то можно наверное на UART делать сразу 2-3 таких импульсов. Но не суть.

Вообще - я бы советовал оптимизировать ваш код расчётов - очень уж там у Вас неоптимально написано.

Во-первых: это не AVR, так что использовать нужно в локальных расчётах int/uint, а не int8_t. Это касается любого МК - в локальных расчётах всегда лучше использовать основную разрядность МК.

Во-вторых: все эти '==', '&&' и пр. - это куча переходов. Тем более - на M0-ядре где как я понимаю нет инструкции IT. А значит - значительные тормоза. Для оптимальности нужно уменьшать кол-во ветвлений. Да и вообще - заглядывать в листинг.

В-третьих: для локальных расчётов использовать локальные переменные, а не глобальные/static. Конечно оптимизатор на высоких уровнях оптимизации сам может догадаться не читать каждый раз из глобальной переменной, но включаете ли Вы оптимизацию вообще?

В-четвёртых: убрать из выражений все ненужные и дублирующиеся операции.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 hours ago, jcxz said:

очень уж там у Вас неоптимально написано

Спасибо за все советы, но я решил вопрос более радикально. В AVR было мало памяти и биты были упакованы в ... биты. А в STM32F070 памяти много, так что я заранее инициализирую массив из uint16_u, так что из бита получается 16, но считать больше ничего не надо. Скорее всего будет все нормально и по прерываниям, но я довел DMA до ума. 

У меня 2 канала, а на чипе аж 7 каналов DMA. Так что вместо того чтобы парится с burst mode, задействовал один для  CCR1 а другой для CCR2. Все работает с нулевой нагрузкой CPU, a массив редко обновляется.  Кроме того удалось скомпилировать  libusb_stm32 перетащив на Эклипс. Работает. Заметно проще, чем библиотека от STM, но не знаю насколько стабильная. Полезно иметь про запас.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...