STM32 новичку в ARM что к чему

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

VladislavS писал(а):Проблемы с даблкликом на ярлыке любимой IDE?
Лень, и не вижу смысла.
Текущая частота МК 32 МГц, на этой частоте он успевает просчитывать скрипты, хотя F0 можно гнать до 48 МГц.

Текущую количество рам что я могу использовать это 2330 байт, по чему: стек = 512 байт, буфер куда грузятся скрипты = 512 байт, регистрация функций то же в оперативке находится = 560 байт (это точное число взято из файла map), там маленько осталось не развернуться.

и так что получается, а получается на 254 светика:
- светики 254*5 = 1270 байт
- буфер светиков 254*3 = 762 байт
- буфер дма 24 байта
Сумма = 2310 байта.

Ладно, хватает, может попробую, возможно, лень.
Реклама
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3916977#p3916977"]стек = 512 байт, буфер куда грузятся скрипты = 512 байт, регистрация функций то же в оперативке находится = 560 байт[/uquote]
Первоначально у ARV использовалась mega8 у которой 1КБ RAM, как думаешь сколько там под стек было выделено? И что такое регистрация функций? Какая-то огромная таблица колбеков из HAL? Практически 1КБ потрачен на непонятную фигню, зато массив диодов всего 24 байта :)
Реклама
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Reflector писал(а):И что такое регистрация функций?
А вот если бы вы в исходный код ARV заглянули, то наверное поняли что я имею ввиду под "регистрацией функций".
Подскажу
Спойлер

Код: Выделить всё

	/* Внедрение моих секций для таблиц */
    __my_tbl_start = __fini_array_end ;
    *(.my_table)
    *(.my_table*)
    KEEP (*(.my_table))
    __my_tbl_end = . ;
    KEEP (SORT(*)(.my_table))
    /* Конец моих секций для таблиц */
Reflector писал(а):Какая-то огромная таблица колбеков из HAL?
HAL не использую, ни куб ни HAL.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="Dimon456",url=""]А вот если бы вы в исходный код ARV заглянули, то наверное поняли что я имею ввиду под "регистрацией функций".[/uquote]
Насколько я помню когда ARV рассказывал про этот "трюк", то речь шла о таблице во флеше. На AVR адреса 16-ти битные, потратить 280 байт из 1КБ ради удобства в проекте с недостачей RAM было бы не очень разумно. А у тебя таблица в RAM.

Это же оно?
[uquote="ARV",url=""]элементы таблицы объявлены именно с указанием секции. смысл этих телодвижений - создать некий аналог массива во FLASH, элементы которого разбросаны по отдельным модулям проекта - где нужно объявить, там и объявляется, а в итоге все эти разрозненные объвления оказываются в одном массиве.[/uquote]
Реклама
Эиком - электронные компоненты и радиодетали
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Мне не удалось эту таблицу во флеш запихнуть, может вам удастся.
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3916977#p3916977"]Лень, и не вижу смысла.[/uquote]Что и требовалось доказать. Десяток строк С++ кода тупо повторить на Си выливается в целое мероприятие. При том что выигрыш в скорости очевиден - лень перевешивает.
Реклама
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

VladislavS писал(а):Десяток строк С++ кода тупо повторить на Си
и 2 кило флеша на таблицу.
Если и писать, то надо полностью "движок" переписать, загнать его в прерывание дма, что бы получилось что-то вроде псевдо-параллельного процесса.
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

VladislavS, кое что я состряпал:
Спойлер

Код: Выделить всё

/* Handles DMA1_Channel1 interrupt request */
void WS2812B_DMA_HANDLER(void)
{	static uint8_t algoritm_cnt = 0;	// счетчик алгоритма
	static uint8_t pixel_cnt = 0;		// счетчик пикселей

    WS2812B_DMA->IFCR = WS2812B_DMA_IT_GL;	// обрабатываем обе половинки

    if(pixel_cnt == PIXEL_CNT+1)			// процедура передачи закончена
    {
    	algoritm_cnt=10;
    }

    switch (algoritm_cnt) {
    	case 0:	algoritm_cnt=1; break;	// формирование сигнала сброс
    	case 1:	algoritm_cnt=2; break;
    	case 2:	algoritm_cnt=3; break;
    	case 3:	algoritm_cnt=4; break;
    	case 4:	algoritm_cnt=5; 		// грузим первую половину буфера
    			memcpy(&DMABuffer[0], &tablica[pixeld[pixel_cnt].g], 8);	// грузим 8 байт из таблицы
    			memcpy(&DMABuffer[8], &tablica[pixeld[pixel_cnt].r], 4);	// грузим первые 4 байта из таблицы
    							break;
    	case 5:	algoritm_cnt=4;			// грузим вторую половину буфера
    			memcpy(&DMABuffer[12], (&tablica[pixeld[pixel_cnt].r])+4, 4);	// грузим вторые 4 байта из таблицы
    			memcpy(&DMABuffer[16], &tablica[pixeld[pixel_cnt].b], 8);	// грузим 8 байт из таблицы
    			pixel_cnt++;			// увеличить счетчик пикселей
    							break;
    	default: pixel_cnt=0; algoritm_cnt=0; ws_status = 0;
    	// Disable Timer	выключение канала
    	WS2812B_TIM->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
    	// DMA1 Channel1 disable
    	WS2812B_DMA_CHANNEL->CCR &= (uint16_t)(~DMA_CCR1_EN);
    							break;
    }

}

void ws281x_send(void)
{
	if(ws_status) return;	// пока алгоритм не отработал пропускаем

	memset(DMABuffer, 0, (sizeof(DMABuffer))); // очистить буфер дма для сигнала сброс

	// копирование карты пикселей в предварительный буфер дма
	for (uint8_t tp = 0; tp < PIXEL_CNT; tp++) {

		uint16_t bright = (pixels[tp].bright + 1);

		pixeld[tp].b = ((pixels[tp].b * bright)>>8);
		pixeld[tp].g = ((pixels[tp].g * bright)>>8);
		pixeld[tp].r = ((pixels[tp].r * bright)>>8);
	}

	/* бит сброса алгоритма */
	ws_status = 1;

	// запуск канала
	WS2812B_DMA_CHANNEL->CNDTR = sizeof(DMABuffer);
	// DMA1 Channel1 enable
	WS2812B_DMA_CHANNEL->CCR |= DMA_CCR1_EN;
	// clear counter for clean start
	WS2812B_TIM->CNT = 0;
	WS2812B_TIM->CCR1 = 0;
	// Enable Timer
	WS2812B_TIM->CR1 |= TIM_CR1_CEN;

}
Код работает, но не так как хотелось, мой старый вариант лучше работает.
Какие будут соображения по поводу оптимизации, короче, кто что подскажет?
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

К чему весь этот судорог? Берём два буфера для dma. В прерывании dma "пол-передачи" копируем текущий диод в первый буфер, а в прерывании "конец передачи" во второй буфер. Если диод++ больше длины ленты, то вместо диода копируем в буфер состояние стоп. Сам счётчик можно не останавливать, он 32-битный и переполнится очень не скоро.

Для фонового процесса: если счётчик превысил длину ленты на 2, то можно следующий кадр запускать обнулением счётчика. Массив диодов при этом можно либо обновлять перед запуском, либо подсовывать адрес предварительно заполненного в фоне второго массива.

Добавлено after 1 hour 39 minutes 12 seconds:
Смотри как просто и красиво это на плюсах выглядит. Для начала чуть поправим буфер dma, чтобы он мог принимать состояние STOP.

Код: Выделить всё

struct STOP_STATE {};

struct PWM_RGB_BUF
{
  uint64_t g;
  uint64_t r;
  uint64_t b;

  inline PWM_RGB_BUF& operator=(RGB rgb)
  {
    this->g = color2pwm(rgb.g);
    this->r = color2pwm(rgb.r);   
    this->b = color2pwm(rgb.b);
    return *this; 
  } 
 
  inline PWM_RGB_BUF& operator=(STOP_STATE stst)
  {
    this->g = 0;
    this->r = 0;   
    this->b = 0;
    return *this; 
  } 
};
Собственно прерывание DMA
Добавлено after 36 minutes 13 seconds:
Немного оптимизировал код в прерывании. Работать напрямую с volatile counter дорого, так же как и с DMA1->ISR. С копией оптимальнее получается. Берите на заметку!

Код: Выделить всё

void DMA1_Channel1_IRQHandler()
{
  uint32_t isr = DMA1->ISR;
  uint32_t tmp = counter;
  DMA1->IFCR = DMA_IFCR_CHTIF1 | DMA_IFCR_CTCIF1;
  if(STOP_STATE stop; tmp<STRIP_LEN) 
    dma_pwm_buf[isr&DMA_ISR_HTIF1?0:1] = leds[tmp]; 
  else 
    dma_pwm_buf[isr&DMA_ISR_HTIF1?0:1] = stop;
  counter=tmp+1;
}
Листинг. Там я руками команды и комментарии переставлял для наглядности. Реально компилятор немного не так сделал, наверняка для скорости.
Спойлер

Код: Выделить всё

//extern "C" void DMA1_Channel1_IRQHandler()
//{
DMA1_Channel1_IRQHandler:
        PUSH     {R4-R7}
//  uint32_t isr = DMA1->ISR;
        LDR.N    R1,??DataTable2  ;; 0x40020000
        LDR      R0,[R1, #+0]
//  uint32_t tmp=counter;
        LDR.N    R2,??DataTable2_1
        LDR      R3,[R2, #+300]
//  DMA1->IFCR = DMA_IFCR_CHTIF1 | DMA_IFCR_CTCIF1;
        MOVS     R4,#+6
        STR      R4,[R1, #+4]
//  if(STOP_STATE stop; tmp<STRIP_LEN) 
        CMP      R3,#+100
        BCS.N    ??DMA1_Channel1_IRQHandler_0
//    dma_pwm_buf[isr&DMA_ISR_HTIF1?0:1] = leds[tmp]; 
        LSLS     R0,R0,#+29
        ADD      R1,R3,R3, LSL #+1
        ITE      MI 
        MOVMI    R0,#+0
        MOVPL    R0,#+24
        ADD      R1,R2,R1
        ADD      R5,R2,#+304
        ADD      R5,R5,R0
        LDRB     R6,[R1, #+1]
        LDRB     R4,[R1, #+2]
        LDRB     R0,[R1, #+0]
        LDR.N    R7,??DataTable2_2
        ADD      R1,R7,R0, LSL #+3
        ADD      R6,R7,R6, LSL #+3
        LDRD     R0,R1,[R1, #+0]
        STRD     R0,R1,[R5, #+0]
        LDRD     R0,R1,[R6, #+0]
        STRD     R0,R1,[R5, #+8]
        ADD      R0,R7,R4, LSL #+3
        LDRD     R0,R1,[R0, #+0]
        STRD     R0,R1,[R5, #+16]
//  counter=tmp+1;
        ADDS     R3,R3,#+1
        STR      R3,[R2, #+300]
        POP      {R4-R7}
        BX       LR               ;; return
//  else 
//    dma_pwm_buf[isr&DMA_ISR_HTIF1?0:1] = stop;
??DMA1_Channel1_IRQHandler_0:
        LSLS     R0,R0,#+29
        ITE      MI 
        MOVMI    R0,#+0
        MOVPL    R0,#+24
        ADD      R1,R2,#+304
        ADD      R0,R1,R0
        MOVS     R4,#+0
        STRD     R4,R4,[R0, #+0]
        STRD     R4,R4,[R0, #+8]
        STRD     R4,R4,[R0, #+16]
//  counter=tmp+1;
        ADDS     R3,R3,#+1
        STR      R3,[R2, #+300]
        POP      {R4-R7}
        BX       LR               ;; return
//}

Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Запустил на железе. На 64 МГц время выполнения прерывания (дергаю ногой и смотрю на осциллографе) коло 1,5 мкc (5% времени) во время паузы 0.7 мкс (2,5% времени). Это уже с гамма-коррекцией "на лету".

Опустил тактовую процессора до 4 МГц. Успевает, гад.
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Изменил код, буфер сделал 48 байт, что бы пиксель влазил.
Померил с разными вариантами, также осциллографом, на частоте 24МГц:
Вариант 1, 4mks
Спойлер

Код: Выделить всё

/* Handles DMA1_Channel1 interrupt request */
void WS2812B_DMA_HANDLER(void)
{	static uint8_t pixel_cnt = 0;		// счетчик пикселей

	GPIOA->ODR |= GPIO_Pin_2;

    if(WS2812B_DMA->ISR & WS2812B_DMA_IT_HT)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;

    memcpy(&DMABuffer[0], &tablica[pixeld[pixel_cnt].g], 8);	
	memcpy(&DMABuffer[8], &tablica[pixeld[pixel_cnt].r], 8);	
	memcpy(&DMABuffer[16], &tablica[pixeld[pixel_cnt].b], 8);	
    }
    else if(WS2812B_DMA->ISR & WS2812B_DMA_IT_TC)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;

	memcpy(&DMABuffer[24], &tablica[pixeld[pixel_cnt].g], 8);	
	memcpy(&DMABuffer[32], &tablica[pixeld[pixel_cnt].r], 8);	
	memcpy(&DMABuffer[40], &tablica[pixeld[pixel_cnt].b], 8);	
    }

    if(pixel_cnt++ == (PIXEL_CNT+1))			// процедура передачи закончена
    {
    	pixel_cnt=0; ws_status = 0;
    	// Disable Timer	выключение канала
    	WS2812B_TIM->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
    	// DMA1 Channel1 disable
    	WS2812B_DMA_CHANNEL->CCR &= (uint16_t)(~DMA_CCR1_EN);
    	// Clear DMA1_Channel1 interrupts
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;
    }

    GPIOA->ODR &= ~GPIO_Pin_2;
}
Вариант 2, 6mks
Спойлер

Код: Выделить всё

/* Handles DMA1_Channel1 interrupt request */
void WS2812B_DMA_HANDLER(void)
{	static uint8_t pixel_cnt = 0;		// счетчик пикселей

	GPIOA->ODR |= GPIO_Pin_2;

    if(WS2812B_DMA->ISR & WS2812B_DMA_IT_HT)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;
 	 	
    	DMABuffer[0] = tablica[pixeld[pixel_cnt].g][0];
		DMABuffer[1] = tablica[pixeld[pixel_cnt].g][1];
		DMABuffer[2] = tablica[pixeld[pixel_cnt].g][2];
		DMABuffer[3] = tablica[pixeld[pixel_cnt].g][3];
		DMABuffer[4] = tablica[pixeld[pixel_cnt].g][4];
		DMABuffer[5] = tablica[pixeld[pixel_cnt].g][5];
		DMABuffer[6] = tablica[pixeld[pixel_cnt].g][6];
		DMABuffer[7] = tablica[pixeld[pixel_cnt].g][7];
		DMABuffer[8] = tablica[pixeld[pixel_cnt].r][0];
		DMABuffer[9] = tablica[pixeld[pixel_cnt].r][1];
		DMABuffer[10] = tablica[pixeld[pixel_cnt].r][2];
		DMABuffer[11] = tablica[pixeld[pixel_cnt].r][3];
		DMABuffer[12] = tablica[pixeld[pixel_cnt].r][4];
		DMABuffer[13] = tablica[pixeld[pixel_cnt].r][5];
		DMABuffer[14] = tablica[pixeld[pixel_cnt].r][6];
		DMABuffer[15] = tablica[pixeld[pixel_cnt].r][7];
		DMABuffer[16] = tablica[pixeld[pixel_cnt].b][0];
		DMABuffer[17] = tablica[pixeld[pixel_cnt].b][1];
		DMABuffer[18] = tablica[pixeld[pixel_cnt].b][2];
		DMABuffer[19] = tablica[pixeld[pixel_cnt].b][3];
		DMABuffer[20] = tablica[pixeld[pixel_cnt].b][4];
		DMABuffer[21] = tablica[pixeld[pixel_cnt].b][5];
		DMABuffer[22] = tablica[pixeld[pixel_cnt].b][6];
		DMABuffer[23] = tablica[pixeld[pixel_cnt].b][7];

    }
    else if(WS2812B_DMA->ISR & WS2812B_DMA_IT_TC)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;

    DMABuffer[24] = tablica[pixeld[pixel_cnt].g][0];
	DMABuffer[25] = tablica[pixeld[pixel_cnt].g][1];
	DMABuffer[26] = tablica[pixeld[pixel_cnt].g][2];
	DMABuffer[27] = tablica[pixeld[pixel_cnt].g][3];
	DMABuffer[28] = tablica[pixeld[pixel_cnt].g][4];
	DMABuffer[29] = tablica[pixeld[pixel_cnt].g][5];
	DMABuffer[30] = tablica[pixeld[pixel_cnt].g][6];
	DMABuffer[31] = tablica[pixeld[pixel_cnt].g][7];
	DMABuffer[32] = tablica[pixeld[pixel_cnt].r][0];
	DMABuffer[33] = tablica[pixeld[pixel_cnt].r][1];
	DMABuffer[34] = tablica[pixeld[pixel_cnt].r][2];
	DMABuffer[35] = tablica[pixeld[pixel_cnt].r][3];
	DMABuffer[36] = tablica[pixeld[pixel_cnt].r][4];
	DMABuffer[37] = tablica[pixeld[pixel_cnt].r][5];
	DMABuffer[38] = tablica[pixeld[pixel_cnt].r][6];
	DMABuffer[39] = tablica[pixeld[pixel_cnt].r][7];
	DMABuffer[40] = tablica[pixeld[pixel_cnt].b][0];
	DMABuffer[41] = tablica[pixeld[pixel_cnt].b][1];
	DMABuffer[42] = tablica[pixeld[pixel_cnt].b][2];
	DMABuffer[43] = tablica[pixeld[pixel_cnt].b][3];
	DMABuffer[44] = tablica[pixeld[pixel_cnt].b][4];
	DMABuffer[45] = tablica[pixeld[pixel_cnt].b][5];
	DMABuffer[46] = tablica[pixeld[pixel_cnt].b][6];
	DMABuffer[47] = tablica[pixeld[pixel_cnt].b][7];

    }

    if(pixel_cnt++ == (PIXEL_CNT+1))			// процедура передачи закончена
    {
    	pixel_cnt=0; ws_status = 0;
    	// Disable Timer	выключение канала
    	WS2812B_TIM->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
    	// DMA1 Channel1 disable
    	WS2812B_DMA_CHANNEL->CCR &= (uint16_t)(~DMA_CCR1_EN);
    	// Clear DMA1_Channel1 interrupts
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;
    }

    GPIOA->ODR &= ~GPIO_Pin_2;
}
Вариант 3, 10mks
Спойлер

Код: Выделить всё

/* Handles DMA1_Channel1 interrupt request */
void WS2812B_DMA_HANDLER(void)
{	static uint8_t pixel_cnt = 0;		// счетчик пикселей

	GPIOA->ODR |= GPIO_Pin_2;

    if(WS2812B_DMA->ISR & WS2812B_DMA_IT_HT)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;

    for (uint8_t i = 0; i < 8; i++)
	{
		DMABuffer[i] = tablica[pixeld[pixel_cnt].g][i];	
	}
    for (uint8_t i = 8; i < 16; i++)
    {
    	DMABuffer[i] = tablica[pixeld[pixel_cnt].r][i-8];	

    }
	for (uint8_t i = 16; i < 24; i++)
	{
		DMABuffer[i] = tablica[pixeld[pixel_cnt].b][i-16];	
	}

    }
    else if(WS2812B_DMA->ISR & WS2812B_DMA_IT_TC)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;

	for (uint8_t i = 24; i < 32; i++)
	{
		DMABuffer[i] = tablica[pixeld[pixel_cnt].g][i-24];	
	}
    for (uint8_t i = 32; i < 40; i++)
    {
    	DMABuffer[i] = tablica[pixeld[pixel_cnt].r][i-32];	

    }
	for (uint8_t i = 40; i < 48; i++)
	{
		DMABuffer[i] = tablica[pixeld[pixel_cnt].b][i-40];	
	}

    }

    if(pixel_cnt++ == (PIXEL_CNT+1))			// процедура передачи закончена
    {
    	pixel_cnt=0; ws_status = 0;
    	// Disable Timer	выключение канала
    	WS2812B_TIM->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
    	// DMA1 Channel1 disable
    	WS2812B_DMA_CHANNEL->CCR &= (uint16_t)(~DMA_CCR1_EN);
    	// Clear DMA1_Channel1 interrupts
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;
    }

    GPIOA->ODR &= ~GPIO_Pin_2;
}
И вариант 4, без таблицы, 14mks
Спойлер

Код: Выделить всё

/* Handles DMA1_Channel1 interrupt request */
void WS2812B_DMA_HANDLER(void)
{	static uint8_t pixel_cnt = 0;		// счетчик пикселей

	GPIOA->ODR |= GPIO_Pin_2;

    if(WS2812B_DMA->ISR & WS2812B_DMA_IT_HT)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;

    uint8_t mask = 128;
    for (uint8_t i = 0; i < 8; i++)
    {
    	DMABuffer[i] = pixeld[pixel_cnt].g & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;	
        mask >>= 1;
    }
    mask = 128;
    for (uint8_t i = 8; i < 16; i++)
    {
    	DMABuffer[i] = pixeld[pixel_cnt].r & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;	
        mask >>= 1;
    }
    mask = 128;
    for (uint8_t i = 16; i < 24; i++)
    {
    	DMABuffer[i] = pixeld[pixel_cnt].b & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;	
        mask >>= 1;
    }

    }
    else if(WS2812B_DMA->ISR & WS2812B_DMA_IT_TC)
    {	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;

    uint8_t mask = 128;
    for (uint8_t i = 24; i < 32; i++)
    {
    	DMABuffer[i] = pixeld[pixel_cnt].g & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;	
        mask >>= 1;
    }
    mask = 128;
    for (uint8_t i = 32; i < 40; i++)
    {
    	DMABuffer[i] = pixeld[pixel_cnt].r & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;	
        mask >>= 1;
    }
    mask = 128;
    for (uint8_t i = 40; i < 48; i++)
    {
    	DMABuffer[i] = pixeld[pixel_cnt].b & mask ? WS2812B_PULSE_HIGH : WS2812B_PULSE_LOW;	
        mask >>= 1;
    }

    }

    if(pixel_cnt++ == (PIXEL_CNT+1))			// процедура передачи закончена
    {
    	pixel_cnt=0; ws_status = 0;
    	// Disable Timer	выключение канала
    	WS2812B_TIM->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
    	// DMA1 Channel1 disable
    	WS2812B_DMA_CHANNEL->CCR &= (uint16_t)(~DMA_CCR1_EN);
    	// Clear DMA1_Channel1 interrupts
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;
    }

    GPIOA->ODR &= ~GPIO_Pin_2;
}
На передачу 1 светика тратится где-то 30мкс, так что в самом худшем варианте получается 47% времени, в самом лучшем 13%.
Замерил вариант 1 частоту прибором DM90A = 31кГц, процент заполнения 13%, так что осциллографом точно померил.
Могу замерить для 64МГц, если надо, если F100 раскочегарится, по моему этот чип разгоняется только до 56МГц?
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Попробуй вот такую конструкцию, должно быстрее стать

Код: Выделить всё

uint32_t isr = DMA1->ISR;
DMA1->IFCR = isr;

if(isr & DMA_ISR_HTIF1)
{

}
else
{

}
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3918465#p3918465"]Вариант 1, 4mks[/uquote]
Зачем там memcpy если копировать нужно один 64-х битный элемент массива? :)
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

На разных ядрах, кстати, будет разный результат. Я попался на том, что листинг смотрел на Cortex-M4, а замеры скорости делал на Cortex-M0+ и результаты не бились.

ЗЫ: В вашем варианте ws_status тоже можно выкинуть. Наличие TIM_CR1_CEN можно использовать как флаг.
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Reflector писал(а):Зачем там memcpy если копировать нужно один 64-х битный элемент массива?
Я не силен в программировании, в этих магических числах, но
Спойлер

Код: Выделить всё

/* Handles DMA1_Channel1 interrupt request */
void WS2812B_DMA_HANDLER(void)
{	static uint8_t pixel_cnt = 0;		// счетчик пикселей

	GPIOA->ODR |= GPIO_Pin_2;

	uint32_t isr = WS2812B_DMA->ISR;
	WS2812B_DMA->IFCR = isr;

	if(isr & WS2812B_DMA_IT_HT)
    {	
	*((uint64_t*)&DMABuffer[0]) = *((uint64_t*)&tablica[pixeld[pixel_cnt].g]);
	*((uint64_t*)&DMABuffer[8]) = *((uint64_t*)&tablica[pixeld[pixel_cnt].r]);
	*((uint64_t*)&DMABuffer[16]) = *((uint64_t*)&tablica[pixeld[pixel_cnt].b]);
    }
    else 
    {	
	*((uint64_t*)&DMABuffer[24]) = *((uint64_t*)&tablica[pixeld[pixel_cnt].g]);
	*((uint64_t*)&DMABuffer[32]) = *((uint64_t*)&tablica[pixeld[pixel_cnt].r]);
	*((uint64_t*)&DMABuffer[40]) = *((uint64_t*)&tablica[pixeld[pixel_cnt].b]);
    }

    if(pixel_cnt++ == (PIXEL_CNT+1))			// процедура передачи закончена
    {
    	pixel_cnt=0; ws_status = 0;
    	// Disable Timer	выключение канала
    	WS2812B_TIM->CR1 &= (uint16_t)(~((uint16_t)TIM_CR1_CEN));
    	// DMA1 Channel1 disable
    	WS2812B_DMA_CHANNEL->CCR &= (uint16_t)(~DMA_CCR1_EN);
    	// Clear DMA1_Channel1 interrupts
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_HT;
    	WS2812B_DMA->IFCR = WS2812B_DMA_IT_TC;
    }

    GPIOA->ODR &= ~GPIO_Pin_2;
}
Единственное, предупреждение warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
На частоте МК 24МГц осциллограф мерит где-то 3мкс, прибор DM90A показывает процент заполнения 10,7%, в общем 11%, не особо, выигрыш 2%.
При добавлении кода
VladislavS писал(а):Попробуй вот такую конструкцию
выигрыш во времени так же есть. С ws_status потом займусь.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3918663#p3918663"]На частоте МК 24МГц осциллограф мерит где-то 3мкс[/uquote]
Было 4 us, стало 3 us, уже хорошо. Если еще в таблице и массиве сразу хранить uint64_t, то никакие приведения не потребуются.
Последний раз редактировалось Reflector Вт ноя 03, 2020 22:56:03, всего редактировалось 1 раз.
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

[uquote="Reflector",url="/forum/viewtopic.php?p=3918674#p3918674"]Было 4 us, стало 3 us, уже хорошо.[/uquote]Ну да, каких-то 2% :)))
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Если pixel_cnt хранить, например, в CNDTR незадействованного DMA-канала, то можно сэкономить на загрузке его адреса.

Ну и, конечно же, выполнение прерывания в SRAM ещё неплохой прирост даст. Я думаю, ваш алгоритм можно в 2 мкс на тактовой 24 МГц утоптать.

Добавлено after 5 hours 43 minutes 28 seconds:
Ещё от компилятора зависит результат. Перенёс эксперименты на F303 на штатной частоте 72 МГц. На IAR для ленты RGBW (4 цвета) прерывание утопталось в 1 мкс. GCC и ARM Compiler 1.1-1.2 мкс на том же коде. Для трёхцветных диодов где-то на 150 нс меньше.
Спойлер

Код: Выделить всё

void DMA2_Channel3_IRQHandler()
{
  TEST_PIN::set(); __NOP();
  
  uint32_t isr = DMA2->ISR;
  DMA2->IFCR = isr;
  uint32_t tmp = DMA2_Channel2->CNDTR;  // static uint16_t pixel_counter :)
  if(STOP_STATE stop; tmp<STRIP_LEN)   
    dma_pwm_buf[isr&DMA_ISR_HTIF3?0:1] = leds[tmp++];
  else
  { 
    dma_pwm_buf[isr&DMA_ISR_HTIF3?0:1] = stop; 
    tmp++; 
  }
  DMA2_Channel2->CNDTR = tmp;
  
  TEST_PIN::clear();
}

Код: Выделить всё

//void DMA2_Channel3_IRQHandler()
//{
DMA2_Channel3_IRQHandler:
        PUSH     {R4-R7,LR}
//  TEST_PIN::set(); __NOP();
        LDR.N    R2,??DMA2_Channel3_IRQHandler_0  ;; 0x48000818
        MOVS     R0,#+128
        STR      R0,[R2, #+0]
        Nop      
 
//  uint32_t isr = DMA2->ISR; 
        LDR.N    R3,??DMA2_Channel3_IRQHandler_0+0x4  ;; 0x40020400
        LDR      R1,[R3, #+0]

//  DMA2->IFCR = isr;
        STR      R1,[R3, #+4]

//  uint32_t tmp = DMA2_Channel2->CNDTR;
        LDR      R0,[R3, #+32]

//  if(STOP_STATE stop; tmp<STRIP_LEN)   
        CMP      R0,#+144
        BCS.N    ??DMA2_Channel3_IRQHandler_1

//    dma_pwm_buf[isr&DMA_ISR_HTIF3?0:1] = leds[tmp++];
        LDR.N    R5,??DMA2_Channel3_IRQHandler_0+0x8
        LDR.N    R6,??DMA2_Channel3_IRQHandler_0+0xC
        LDR.N    R7,??DMA2_Channel3_IRQHandler_0+0x10
        ADDS     R4,R0,#+1
        ADD      R0,R5,R0, LSL #+2
        LDRB     LR,[R0, #+1]
        LDRB     R12,[R0, #+2]
        LDRB     R5,[R0, #+3]
        LDRB     R0,[R0, #+0]
        LSLS     R1,R1,#+21
        ITE      MI 
        MOVMI    R1,#+0
        MOVPL    R1,#+32
        ADD      R6,R6,R1
        ADD      R1,R7,R0, LSL #+3
        ADD      LR,R7,LR, LSL #+3
        LDRD     R0,R1,[R1, #+0]
        STRD     R0,R1,[R6, #+0]
        LDRD     R0,R1,[LR, #+0]
        STRD     R0,R1,[R6, #+8]
        ADD      R0,R7,R12, LSL #+3
        LDRD     R0,R1,[R0, #+0]
        STRD     R0,R1,[R6, #+16]
        ADD      R1,R7,R5, LSL #+3
        LDRD     R0,R1,[R1, #+0]
        STRD     R0,R1,[R6, #+24]

//  DMA2_Channel2->CNDTR = tmp;
        STR      R4,[R3, #+32]

//  TEST_PIN::clear();
        MOVS     R0,#+128
        STRH     R0,[R2, #+2]

        POP      {R4-R7,PC}       ;; return
               
//  else
//  { 
//    dma_pwm_buf[isr&DMA_ISR_HTIF3?0:1] = stop; 
??DMA2_Channel3_IRQHandler_1:
        LSLS     R1,R1,#+21
        ITE      MI 
        MOVMI    R1,#+0
        MOVPL    R1,#+32
        LDR.N    R4,??DMA2_Channel3_IRQHandler_0+0xC
        ADD      R1,R4,R1
        MOVS     R6,#+0
        STRD     R6,R6,[R1, #+0]
        STRD     R6,R6,[R1, #+8]
        STRD     R6,R6,[R1, #+16]
        STRD     R6,R6,[R1, #+24]
        
//    tmp++;         
        ADDS     R4,R0,#+1
        
//  }
//  DMA2_Channel2->CNDTR = tmp;        
        STR      R4,[R3, #+32]

//  TEST_PIN::clear();        
        MOVS     R0,#+128
        STRH     R0,[R2, #+2]
        
        POP      {R4-R7,PC}       ;; return
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Столкнулся с непонятностью, мне нужно было знать на какой частоте работает лента ну и таймер.
Из прерывания дма все удалил кроме флага сброса, в буфер дма загрузил WS2812B_PULSE_LOW, и кольцевой запуск без остановки дма.
Сначала проверил на какой частоте работает МК

Код: Выделить всё

  SystemCoreClockUpdate(); // Вычисление тактовой частоты ядра.

  // -- определение тактирования перефирии
      RCC_ClocksTypeDef clocks;
      RCC_GetClocksFreq(&clocks);
Какую задал, такая и есть = 24МГц.
При значении периода таймера равном 30 частота ШИМ ровно 800кГц(измерено прибором DM90A), LOW=8 HIGH=17, но лента при этой частоте не работает.
Крайние значения таймера для ленты получаются 30 - 160.
В общем частота ШИМ ленты 400кГц значения таймера = 60, а вот это LOW=8 HIGH=17 осталось без изменений.

Кто что скажет?
Собутыльник Кота
Аватара пользователя
Сообщения: 2567
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Применяем средства объективного контроля - осциллогляд и напряжометр.
Ответить

Вернуться в «ARM»