В предыдущей части урока мы написали несколько тестов и проверили их на практике.
Следующий тест будет очень интересный. Там будет бежать по ленте маска насыщенности, то есть плавно бежим плавным белым пятном.
Только вот с белым пятном будет уже наоборот, если мы применим закон прямой линии, то есть примем долю как есть, то будет мало цветного, то есть белое пятно будет слишком большим, а если применим квадрат, то ещё больше, поэтому мы применим функцию корня, причём квадратного также маловато, применим кубический. Для начала в файле ws2812.c подключим следующую библиотеку
1 2 |
#include "ws2812.h" #include "math.h" |
Только в данной библиотеке нет функции кубического корня. Но есть функция любой степени. А из курса математики мы помним, что кубический корень — это ничто иное, как число в степени 1/3. Вот эту функцию мы и будем использовать в нашем коде. Вот такая вот горизонтальная парабола является графиком функции кубического корня
Вот такой получится код теста
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//---------------------------------------------------------------------------- void ws2812_test09(uint8_t n, uint8_t nm) { uint16_t i; uint16_t j; uint16_t ks,kv; for(i=0;i<LED_COUNT;i++) { back_buf[i].H = i * 360 / LED_COUNT* n % 360; back_buf[i].S = 255, back_buf[i].V = BRIGHT; } ws2812_hsv_to_rgb(); for(i=0;i<LED_COUNT;i++) { if((i%(LED_COUNT/n)) <= LED_COUNT/n/2) { ks = 255 * pow(i%(LED_COUNT/n),1.0f/3.0f) / pow(LED_COUNT/n/2,1.0f/3.0f); } else { ks = 255 * pow(LED_COUNT/n - (i%(LED_COUNT/n)),1.0f/3.0f) / pow(LED_COUNT/n/2,1.0f/3.0f); } back_buf[i].S = ks; kv = (rgb_temp2[i].R + rgb_temp2[i].G + rgb_temp2[i].B)/3; back_buf[i].V = kv; } for(j=0;j<LED_COUNT*nm;j++) { back_buf_temp.S = back_buf[LED_COUNT-1].S; back_buf_temp.V = back_buf[LED_COUNT-1].V; for(i=0;i<(LED_COUNT-1);i++) { back_buf[LED_COUNT-i-1].S = back_buf[LED_COUNT-i-2].S; back_buf[LED_COUNT-i-1].V = back_buf[LED_COUNT-i-2].V; } back_buf[0].S = back_buf_temp.S; back_buf[0].V = back_buf_temp.V; ws2812_hsv_to_rgb(); ws2812_rgb_copy_buf_to_dma(); ws2812_light(); HAL_Delay(20); } } //---------------------------------------------------------------------------- |
Также есть ещё одна достопримечательность в этом коде. Когда мы убавляем насыщенность, мы стремимся к белому цвету, а белый цвет — это равенство всех цветов по яркости, а яркость у нас только одна, поэтому при белом цвете все цвета начнут светиться максимальном цвете, это надо как-то уравнять. Для этого мы и находим среднюю яркость, складывая все три яркости всех цветов в пикселе и деля их на 3. Благодаря этому, яркость на всём протяжении ленты будет равномерная.
Вызовем функцию теста
1 2 |
//ws2812_test08(4,12); ws2812_test09(4,2); |
Посмотрим результат
Следующий тест проделывает то же самое, но в обратном направлении
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
//---------------------------------------------------------------------------- void ws2812_test10(uint8_t n, uint8_t nm) { uint16_t i; uint16_t j; uint16_t ks,kv; for(i=0;i<LED_COUNT;i++) { back_buf[i].H = i * 360 / LED_COUNT* n % 360; back_buf[i].S = 255, back_buf[i].V = BRIGHT; } ws2812_hsv_to_rgb(); for(i=0;i<LED_COUNT;i++) { if((i%(LED_COUNT/n)) <= LED_COUNT/n/2) { ks = 255 * pow(i%(LED_COUNT/n),1.0f/3.0f) / pow(LED_COUNT/n/2,1.0f/3.0f); } else { ks = 255 * pow(LED_COUNT/n - (i%(LED_COUNT/n)),1.0f/3.0f) / pow(LED_COUNT/n/2,1.0f/3.0f); } back_buf[i].S = ks; kv = (rgb_temp2[i].R + rgb_temp2[i].G + rgb_temp2[i].B)/3; back_buf[i].V = kv; } for(j=0;j<LED_COUNT*nm;j++) { back_buf_temp.S = back_buf[0].S; back_buf_temp.V = back_buf[0].V; for(i=0;i<(LED_COUNT-1);i++) { back_buf[i].V = back_buf[i+1].V; back_buf[i].S = back_buf[i+1].S; } back_buf[LED_COUNT-1].V = back_buf_temp.V; back_buf[LED_COUNT-1].S = back_buf_temp.S; ws2812_hsv_to_rgb(); ws2812_rgb_copy_buf_to_dma(); ws2812_light(); HAL_Delay(20); } } //---------------------------------------------------------------------------- |
Вызовем данную функцию
1 2 |
//ws2812_test09(4,2); ws2812_test10(4,2); |
Следующий тест будет бежать плавными огоньками одного цвета. У нас подобный тест был ранее, теперь мы его сделаем с применением модели HSV. В параметрах будет количество цветовых пятен на ленту, тон — один из девяти возможных, а также количество вращений
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
//---------------------------------------------------------------------------- void ws2812_test11(uint8_t n, uint8_t col, uint8_t nm) { uint16_t i; uint16_t j; uint32_t k; for(i=0;i<LED_COUNT;i++) { switch(col) { case 1: back_buf[i].H = 0; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 2: back_buf[i].H = 40; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 3: back_buf[i].H = 80; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 4: back_buf[i].H = 120; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 5: back_buf[i].H = 160; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 6: back_buf[i].H = 200; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 7: back_buf[i].H = 240; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 8: back_buf[i].H = 280; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 9: back_buf[i].H = 320; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; } } ws2812_hsv_to_rgb(); for(i=0;i<LED_COUNT;i++) { if((i%(LED_COUNT/n)) <= LED_COUNT/n/2) { k = BRIGHT * (i%(LED_COUNT/n)) / (LED_COUNT/n/2) * (i%(LED_COUNT/n)) / (LED_COUNT/n/2); } else { k = BRIGHT * (LED_COUNT/n - (i%(LED_COUNT/n))) / (LED_COUNT/n/2) * (LED_COUNT/n - (i%(LED_COUNT/n))) / (LED_COUNT/n/2); } back_buf[i].V = k; } for(j=0;j<LED_COUNT*nm;j++) { back_buf_temp.V = back_buf[0].V; for(i=0;i<(LED_COUNT-1);i++) { back_buf[i].V = back_buf[i+1].V; } back_buf[LED_COUNT-1].V = back_buf_temp.V; ws2812_hsv_to_rgb(); ws2812_rgb_copy_buf_to_dma(); ws2812_light(); HAL_Delay(20); } } //---------------------------------------------------------------------------- |
Для плавности затухания также применена парабола.
Вызовем функцию с разными видами тона
1 2 3 4 5 6 7 8 9 10 |
//ws2812_test10(4,2); ws2812_test11(4,1,1); ws2812_test11(4,2,1); ws2812_test11(4,3,1); ws2812_test11(4,4,1); ws2812_test11(4,5,1); ws2812_test11(4,6,1); ws2812_test11(4,7,1); ws2812_test11(4,8,1); ws2812_test11(4,9,1); |
Посмотрим результат выполнения
Следующий тест такой же, только движение там происходит в обратном направлении
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
//---------------------------------------------------------------------------- void ws2812_test12(uint8_t n, uint8_t col, uint8_t nm) { uint16_t i; uint16_t j; uint32_t k; for(i=0;i<LED_COUNT;i++) { switch(col) { case 1: back_buf[i].H = 0; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 2: back_buf[i].H = 40; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 3: back_buf[i].H = 80; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 4: back_buf[i].H = 120; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 5: back_buf[i].H = 160; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 6: back_buf[i].H = 200; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 7: back_buf[i].H = 240; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 8: back_buf[i].H = 280; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; case 9: back_buf[i].H = 320; back_buf[i].S = 255, back_buf[i].V = BRIGHT; break; } } ws2812_hsv_to_rgb(); for(i=0;i<LED_COUNT;i++) { if((i%(LED_COUNT/n)) <= LED_COUNT/n/2) { k = BRIGHT * (i%(LED_COUNT/n)) / (LED_COUNT/n/2) * (i%(LED_COUNT/n)) / (LED_COUNT/n/2); } else { k = BRIGHT * (LED_COUNT/n - (i%(LED_COUNT/n))) / (LED_COUNT/n/2) * (LED_COUNT/n - (i%(LED_COUNT/n))) / (LED_COUNT/n/2); } back_buf[i].V = k; } for(j=0;j<LED_COUNT*nm;j++) { back_buf_temp.V = back_buf[LED_COUNT-1].V; for(i=0;i<(LED_COUNT-1);i++) { back_buf[LED_COUNT-i-1].V = back_buf[LED_COUNT-i-2].V; } back_buf[0].V = back_buf_temp.V; ws2812_hsv_to_rgb(); ws2812_rgb_copy_buf_to_dma(); ws2812_light(); HAL_Delay(20); } } //---------------------------------------------------------------------------- |
Вызовем также по очереди данный тест с различными параметрами
1 2 3 4 5 6 7 8 9 10 |
//ws2812_test11(4,9,1); ws2812_test12(4,1,1); ws2812_test12(4,2,1); ws2812_test12(4,3,1); ws2812_test12(4,4,1); ws2812_test12(4,5,1); ws2812_test12(4,6,1); ws2812_test12(4,7,1); ws2812_test12(4,8,1); ws2812_test12(4,9,1); |
Также попробуем их в работе. Здесь показывать нет смысла, так как будет одно и то же. Направление здесь не покажешь.
В последнем тесте код практически не претерпел изменений и сделан он с использованием цветовой модели RGB, разве что у него только сменился номер и в качестве множителя мы используем яркость по умолчанию
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
//---------------------------------------------------------------------------- void ws2812_test13(void) { uint16_t i; uint8_t j,jj; int k=0; for(jj=0;jj<40;jj++) { for(j=0;j<32;j++) { for(i=0;i<LED_COUNT;i++) { if(j<16) k=1000*(j)/16; else k=1000*(32-j)/16; if(i%10==0) ws2812_pixel_rgb_to_buf_dma(BRIGHT*k/1000,BRIGHT*(1000-k)/1000,0,i); if(i%10==1) ws2812_pixel_rgb_to_buf_dma(0,BRIGHT*k/1000,BRIGHT*(1000-k)/1000,i); if(i%10==2) ws2812_pixel_rgb_to_buf_dma(BRIGHT*k/1000,0,BRIGHT*(1000-k)/1000,i); if(i%10==3) ws2812_pixel_rgb_to_buf_dma(BRIGHT*(1000-k)/1000,BRIGHT*k/2000,255*k/2000,i); if(i%10==4) ws2812_pixel_rgb_to_buf_dma(BRIGHT*k/2000,BRIGHT*k/2000,BRIGHT*(1000-k)/1000,i); if(i%10==5) ws2812_pixel_rgb_to_buf_dma(BRIGHT*k/2000,BRIGHT*(1000-k)/1000,BRIGHT*k/2000,i); if(i%10==6) ws2812_pixel_rgb_to_buf_dma(BRIGHT*(1000-k)/2000,BRIGHT*(1000-k)/2000,BRIGHT*k/1000,i); if(i%10==7) ws2812_pixel_rgb_to_buf_dma(BRIGHT*k/1000,BRIGHT*(1000-k)/2000,BRIGHT*(1000-k)/2000,i); if(i%10==8) ws2812_pixel_rgb_to_buf_dma(BRIGHT*(1000-k)/2000,BRIGHT*k/1000,BRIGHT*(1000-k)/2000,i); if(i%10==9) ws2812_pixel_rgb_to_buf_dma(BRIGHT*(1000-k)/3000,BRIGHT*(1000-k)/1333,BRIGHT*k/2000,i); } ws2812_light(); HAL_Delay(20); } } } //---------------------------------------------------------------------------- |
Вызовем наш тест
1 2 |
//ws2812_test12(4,9,1); ws2812_test13(); |
Посмотрим результат работы теста
Отлично! Все тесты у нас работают.
Распределим теперь их вперемешку, чтобы было интереснее
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
/* USER CODE BEGIN 3 */ ws2812_test01(1,1); ws2812_test02(1,1); ws2812_test11(4,1,1); ws2812_test12(4,1,1); ws2812_test01(2,1); ws2812_test02(2,1); ws2812_test11(4,2,1); ws2812_test12(4,2,1); ws2812_test03(4,6); ws2812_test04(4,6); ws2812_test11(4,3,1); ws2812_test12(4,3,1); ws2812_test05(4,2); ws2812_test06(4,2); ws2812_test11(4,4,1); ws2812_test12(4,4,1); ws2812_test07(4,12); ws2812_test08(4,12); ws2812_test11(4,5,1); ws2812_test12(4,5,1); ws2812_test09(4,2); ws2812_test10(4,2); ws2812_test11(4,6,1); ws2812_test12(4,6,1); ws2812_test13(); ws2812_test11(4,7,1); ws2812_test12(4,7,1); ws2812_test01(8,1); ws2812_test02(8,1); ws2812_test11(4,8,1); ws2812_test12(4,8,1); ws2812_test01(12,1); ws2812_test02(12,1); ws2812_test11(4,9,1); ws2812_test12(4,9,1); |
Проверим ещё раз работу кода на всех тестах, затем выставим в файле w2812.h параметры для уличной ленты, увеличив там количество светодиодов ленты и также установив максимальную яркость
#define BRIGHT 255
#define LED_COUNT 300
Отнесём контроллер на чердак, подключим его к уличной ленте и посмотрим, как работают наши новые красивые эффекты уже на ней
Итак, в данном занятии мы произвели оптимизацию кода, нацеленную на использование цветовой модели HSV, благодаря которому нам стало намного легче оперировать нужными величинами в наших тестах (эффектах). Также использование 8-битного буфера для DMA позволило нам значительно сэкономить пространство в оперативной памяти!
Благодарю всех за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Светодиодные ленты 1м-5м 1м-5м 150-300 светодиодов Защита IP60-IP67 можно приобрести здесь WS2812B
Импульсный источник питания 5 В в 40A 200 Вт можно приобрести здесь 5 В в 40A 200 Вт
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий