Итоговый вариант гирлянды.
STM32F303CCT6
STM32CubeIDE v.1.11.2
CMSIS
Спойлер
/**
******************************************************************************
* @file : main.c
* @author : Auto-generated by STM32CubeIDE
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include <stdint.h>
#include "f303cct6.h"
#include "f3ws281Y.h"
#include <stdlib.h>
#include "ws281eff.h"
uint32_t SystemCoreClock = 8000000UL; /*!< System Clock Frequency (Core Clock) */
unsigned char colorsData[count_bytes_in_buf];
int main(void)
{
f303cct6init(); // init RCC
f303initTIM4(); // init millis timer
DWT_Delay_Init(); // init micros delay
f303initC13led(); // init board led
f303initA0A1keys(); // init keys PGM/SET and SEL
f303initWS281X(); // init strip
while (1) {
ws281loopWork((unsigned char *) colorsData);
}
}
/*
* f303cct6.h
*
* Created on: Feb 6, 2023
* Author: Andrey
*/
#ifndef F303CCT6_H_
#define F303CCT6_H_
#define keys_short_press ((unsigned long)50)
#define keys_long_press ((unsigned long)3000)
void f303cct6init(void);
void f303initC13led(void);
void f303setC13led(unsigned char AledONOFF);
void f303initTIM4(void);
unsigned long millis(void);
void DWT_Delay_Init(void);
void DWT_Delay_us(unsigned long au32_microseconds);
void f303initA0A1keys(void);
unsigned char f303readA0(void);
unsigned char f303readA1(void);
unsigned char f303keysA0A1read(void); // out bit0 short press A0, bit1-A1, out bit2 long press A0, bit3-A1
#endif /* F303CCT6_H_ */
/*
* f303cct6.c
*
* Created on: Feb 6, 2023
* Author: Andrey
*/
#include "f303cct6.h"
#include <stm32f30x.h>
volatile unsigned long msTicks = 0;
unsigned char f303keysA0A1read(void) { // out bit0 short press A0, bit1-A1, out bit2 long press A0, bit3-A1
unsigned char outByte = 0;
static unsigned char lastLevel[] = {1, 1};
static unsigned char flagKeysDown[] = {0, 0};
static unsigned long startTimeDown[2];
unsigned char currentLevel[] = {f303readA0(), f303readA1()};
for(int i = 0; i < 2; ++i) { // 2 keys
if (currentLevel[i] != lastLevel[i]) { // change level
if (currentLevel[i] == 0) { // key down
if (flagKeysDown[i] == 0) { // no timer
flagKeysDown[i] = 1; // begin timer
startTimeDown[i] = millis();
}
} else { // key up
if (flagKeysDown[i] != 0) { // timer started
if ((millis() - startTimeDown[i]) >= 500) { // error press
flagKeysDown[i] = 0; // stop timer
} else if ((millis() - startTimeDown[i]) >= keys_short_press) { // end timer
flagKeysDown[i] = 0; // stop timer - short press
outByte |= 1 << i;
}
}
}
} else { // level not change
if ((flagKeysDown[i] != 0) && (currentLevel[i] == 0)) { // timer started and key down
if ((millis() - startTimeDown[i]) >= keys_long_press) { // end timer
flagKeysDown[i] = 0; // stop timer - long press
outByte |= 1 << (i+2);
}
} else if (currentLevel[i] != 0) { // key up
flagKeysDown[i] = 0; // reset timer
}
}
lastLevel[i] = currentLevel[i]; // save current level key
}
return outByte;
}
unsigned char f303readA0(void) {
if (GPIOA->IDR & GPIO_IDR_0) return 1; else return 0;
}
unsigned char f303readA1(void) {
if (GPIOA->IDR & GPIO_IDR_1) return 1; else return 0;
}
void f303initA0A1keys(void) {
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // Включаем тактирование порта GPIOA
GPIOA->MODER &= ~GPIO_MODER_MODER0; // для начала все сбрасываем в ноль -> 00: Input mode (reset state)
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR0; // 00: Low speed
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR0; // No pull-up, pull-down
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR0_0; // pull-up 01
GPIOA->MODER &= ~GPIO_MODER_MODER1; // для начала все сбрасываем в ноль -> 00: Input mode (reset state)
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR1; // 00: Low speed
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR1; // No pull-up, pull-down
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR1_0; // pull-up 01
}
void TIM4_IRQHandler(void) {
TIM4->SR &= ~TIM_SR_UIF;
++msTicks;
}
void f303cct6init(void) {
// TODO Auto-generated constructor stub
volatile int StartUpCounter;
RCC->CR |= RCC_CR_HSEON; //Запускаем генератор HSE
for(StartUpCounter=0; ; StartUpCounter++) { //Ждем успешного запуска или окончания тайм-аута
if(RCC->CR & RCC_CR_HSERDY) break; //Если успешно запустилось, то выходим из цикла
if(StartUpCounter > 0x1000) { //Если не запустилось, то //отключаем все, что включили
RCC->CR &= ~RCC_CR_HSEON; //Останавливаем HSE
return; //и возвращаем ошибку
}
}
RCC->CFGR |= RCC_CFGR_PLLMULL; // 0111: PLL input clock x 9 //PLL множитель равен 9 //Настраиваем и запускаем PLL
RCC->CFGR &= ~RCC_CFGR_PLLMULL_3; // 0111: PLL input clock x 9
RCC->CFGR |= RCC_CFGR_PLLSRC; // 1: HSE/PREDIV selected as PLL input clock //Тактирование PLL от HSE
RCC->CR |= RCC_CR_PLLON; // Запускаем PLL
for(StartUpCounter=0; ; StartUpCounter++) { // Ждем успешного запуска или окончания тайм-аута
if(RCC->CR & RCC_CR_PLLRDY) break; //Если успешно запустилось, то //выходим из цикла
if(StartUpCounter > 0x1000) { //Если по каким-то причинам не запустился PLL, то //отключаем все, что включили
RCC->CR &= ~RCC_CR_HSEON; //Останавливаем HSE
RCC->CR &= ~RCC_CR_PLLON; //Останавливаем PLL
return; //и возвращаем ошибку
}
}
//Настраиваем FLASH и делители
//Устанавливаем 2 цикла ожидания для Flash
//так как частота ядра у нас будет 48 MHz < SYSCLK <= 72 MHz
FLASH->ACR |= FLASH_ACR_LATENCY_1; // 010: Two wait sates, if 48 < HCLK ≤ 72 MHz
//Делители
RCC->CFGR &= ~RCC_CFGR_PPRE2; //Делитель шины APB2 отключен // 0xx: HCLK not divided
RCC->CFGR &= ~RCC_CFGR_PPRE1; //Делитель нишы APB1 равен 2 // 100: HCLK divided by 2
RCC->CFGR |= RCC_CFGR_PPRE1_2; // 100: HCLK divided by 2
RCC->CFGR &= ~RCC_CFGR_HPRE; //Делитель AHB отключен // 0xxx: SYSCLK not divided
RCC->CFGR |= RCC_CFGR_SW_1; //Переключаемся на работу от PLL // 10: PLL selected as system clock
while(!(RCC->CFGR & RCC_CFGR_SWS_1)); // Ждем, пока переключимся // 10: PLL used as system clock
//Настройка и переклбючение сисемы
//на внешний кварцевый генератор
//и PLL запершилось успехом.
SystemCoreClock = 72000000UL;
//Выходим
}
void f303initC13led(void) {
RCC->AHBENR |= RCC_AHBENR_GPIOCEN; // Включаем тактирование порта GPIOC
GPIOC->MODER &= ~GPIO_MODER_MODER13; // для начала все сбрасываем в ноль -> 00: Input mode (reset state)
GPIOC->MODER |= GPIO_MODER_MODER13_0; // 01: General purpose output mode
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_13; // 0: Output push-pull (reset state)
GPIOC->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13; // 00: Low speed
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR13; // No pull-up, pull-down
}
void f303setC13led(unsigned char AledONOFF) {
if (AledONOFF) GPIOC->BRR |= GPIO_BRR_BR_13;
else GPIOC->BSRR |= GPIO_BSRR_BS_13;
}
void f303initTIM4(void) {
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // clock Timer4 ON
TIM4->SR = 0;
TIM4->CNT = 0;
TIM4->PSC = (SystemCoreClock / 2 / 1000000) - 1; // делитель
TIM4->ARR = 1999; // значение перезагрузки -> 1 msec
TIM4->DIER |= TIM_DIER_UIE;
NVIC_EnableIRQ (TIM4_IRQn);
TIM4->CR1 |= TIM_CR1_CEN; // включаем счётчик
TIM4->SR &= ~TIM_SR_UIF;
}
unsigned long millis(void) {
return msTicks;
}
/*
* https://deepbluembedded.com/stm32-delay-microsecond-millisecond-utility-dwt-delay-timer-delay/
*/
void DWT_Delay_Init(void) {
/* Disable TRC */
CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; // ~0x01000000;
/* Enable TRC */
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // 0x01000000;
/* Disable clock cycle counter */
DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001;
/* Enable clock cycle counter */
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; //0x00000001;
/* Reset the clock cycle counter value */
DWT->CYCCNT = 0;
/* 3 NO OPERATION instructions */
__ASM volatile ("NOP");
__ASM volatile ("NOP");
__ASM volatile ("NOP");
}
/*
* This Function Provides Delay In Microseconds Using DWT
* https://deepbluembedded.com/stm32-delay-microsecond-millisecond-utility-dwt-delay-timer-delay/
*/
void DWT_Delay_us(unsigned long au32_microseconds) {
uint32_t au32_initial_ticks = DWT->CYCCNT;
uint32_t au32_ticks = (SystemCoreClock / 1000000);
au32_microseconds *= au32_ticks;
while ((DWT->CYCCNT - au32_initial_ticks) < au32_microseconds-au32_ticks);
}
/*
* f3ws281Y.h
*
* Created on: Feb 3, 2023
* Author: Andrey
*/
#ifndef INC_F3WS281Y_H_
#define INC_F3WS281Y_H_
#ifndef count_of
#define count_of(a) (sizeof(a)/sizeof((a)[0]))
#endif
#define numLEDSws281X ((unsigned short)50)
#define ws281x_bytes_per_pixel ((unsigned short)3) // RGB/RGBW // число байт на пиксель, используется для расчета максимального числа пикселей в буфере
#define ws281x_max_count_bytes ((unsigned short)16384) // size PWM DMA buffer // размер буфера в байтах -> один байт = один бит ленты
// 16384 bytes -> 600 pixels RGB
#define ws281y_max_bright_percent ((unsigned char)80) // максимально возможно выставляемая яркость в процентах из учета мощности БП и количества светодиодов
#define count_bytes_in_buf ((unsigned short)(numLEDSws281X*ws281x_bytes_per_pixel))
#define ws281x_arr_timer_125_usec ((unsigned short)89) // значение перезагрузки таймера // период 1.25 микросекунды
#define ws281x_ccr_timer_full_high ((unsigned short)(ws281x_arr_timer_125_usec+1)) // значение перезагрузки таймера для постоянной HIGH на пине
#define ws281x_ccr_th0_timer ((unsigned short)(ws281x_arr_timer_125_usec*0.32)) // краткая часть периода ws2812
#define ws281x_ccr_th1_timer ((unsigned short)(ws281x_arr_timer_125_usec-ws281x_ccr_th0_timer)) // длинная часть периода ws2812
#define ws281x_arr_timer_reset_bus ((unsigned short)((50/1.25+1)*ws281x_arr_timer_125_usec)) // время сброса шины
void f303initWS281X(void);
void f303sendWS281X(unsigned char * inData, unsigned short dataCount);
unsigned char f303busyWS281X(void);
void f303setBrBytes(unsigned char inVal);
unsigned char f303getBrBytes(void);
#endif /* INC_F3WS281Y_H_ */
/*
* f3ws281Y.c
*
* Created on: Feb 3, 2023
* Author: Andrey
*/
#include <stm32f30x.h>
#include "f3ws281Y.h"
volatile unsigned char ws281Xmode = 0; // в процессе отправки
unsigned char ws281Ybrval; // яркость в процентах
unsigned short ws281XrealSizeDMAbuf; // реальный размер данных в буфере, кратно количеству пикселей * размер пикселя * 8
unsigned char ws281XbufPWM[ws281x_max_count_bytes+1]; // сам буфер для отправки // +1 байт для подлнялия GPIO вверх после отправки последнего байта
unsigned char f303getBrBytes(void) {
return ws281Ybrval;
}
unsigned char f303busyWS281X(void) {
return ws281Xmode;
}
void f303setBrBytes(unsigned char inVal) { // установка яркости в процентах
if (inVal>ws281y_max_bright_percent) ws281Ybrval = ws281y_max_bright_percent;
else if (inVal==0) ws281Ybrval = 1;
else ws281Ybrval = inVal;
}
void prepareDMAbuf(const unsigned char * inData, const unsigned short dataCount) { // заполняем буфер для ШИМ
unsigned short maxBytesBuf = ws281x_max_count_bytes / 8; // сколько всего байт в буфер влезет
unsigned short maxPixelsBuf = maxBytesBuf / ws281x_bytes_per_pixel; // Сколько всего пикселей в буфер влезет
maxBytesBuf = maxPixelsBuf * ws281x_bytes_per_pixel; // сколько всего байт в буфер влезет, кратно одному пикселю
if (dataCount > maxBytesBuf) { // число отправляемых байт больше чем размер буфера
ws281XrealSizeDMAbuf = maxBytesBuf * 8;
} else { // все влезет в один буфер
ws281XrealSizeDMAbuf = dataCount * 8;
maxBytesBuf = dataCount;
}
for (unsigned short i=0; i<maxBytesBuf; ++i) {
unsigned char inByte = *(inData+i);
inByte = (unsigned char)(((unsigned short)((unsigned short)(inByte)*(unsigned short)(ws281Ybrval)))/100);
for (unsigned char j=8; j>0; j--) {
if (inByte & (1<<(j-1))) ws281XbufPWM[i*8+(8-j)] = ws281x_ccr_th1_timer;
else ws281XbufPWM[i*8+(8-j)] = ws281x_ccr_th0_timer;
}
}
ws281XbufPWM[maxBytesBuf*8] = 0; // последним байтом пропишем ШИМ с минимальным заполнением, опустить линию в ноль после последнего пикселя
++ws281XrealSizeDMAbuf; // на один байт увеличим количество отправляемых данных
}
void f303initPB0pwm(void) {
GPIOB->BSRR |= GPIO_BSRR_BS_0; // PB0 HIGH
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR0; // 00: Low speed
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR0; // No pull-up, pull-down
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_0; // 0: Output push-pull (reset state)
GPIOB->AFR[0] &= 0x0F; // clear AF by PB0
GPIOB->AFR[0] |= 2; // Назначаем PB0 выводу альтернативную функцию AF2
GPIOB->MODER &= ~GPIO_MODER_MODER0; // для начала все сбрасываем в ноль -> 00: Input mode (reset state)
GPIOB->MODER |= GPIO_MODER_MODER0_1; // 10: Alternate function mode
}
void f303initTIM3pwmCH3(void) {
TIM3->CR1 &= ~(TIM_CR1_CEN); //останавливаем таймер
TIM3->CNT = 0; //Очищаем счетный регистр
TIM3->PSC = 0; // делитель x1 72000000 MHz
TIM3->ARR = ws281x_arr_timer_125_usec; // значение перезагрузки // 1.25 usec
TIM3->CCR3 = ws281x_ccr_timer_full_high; // коэф. заполнения
TIM3->CCMR2 &= ~(TIM_CCMR2_OC3M); //сбрасываем все биты OCxM
TIM3->CCMR2 |= TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3PE; // настроить канал
TIM3->CCER = TIM_CCER_CC3E; // настроим на выход канал 3
TIM3->BDTR = TIM_BDTR_MOE; // разрешим использовать выводы таймера как выходы
TIM3->CR1 &= ~TIM_CR1_DIR; // считаем вверх
TIM3->CR1 &= ~TIM_CR1_CMS; // выравнивание по фронту, Fast PWM
TIM3->CR1 |= TIM_CR1_ARPE; //Регистры таймера с буферизацией
}
void DMA1_CH2_IRQHandler(void) {
DMA1->IFCR |= DMA_IFCR_CTCIF2; // Сбрасываем флаг прерываний
DMA1_Channel2->CCR &= ~(DMA_CCR_EN); //Отключаем канал DMA
TIM3->CR1 &= ~(TIM_CR1_CEN); //останавливаем таймер
ws281Xmode = 0; // закончили передачу
}
void TIM3_IRQHandler(void) { //прерывание от таймера //Сюда попадаем после завершения формирования //сигнала RET шины ws2812b
TIM3->SR &= ~(TIM_SR_UIF); //сбрасываем флаг прерывания
TIM3->CR1 &= ~(TIM_CR1_CEN); //останавливаем таймер
TIM3->DIER &= ~TIM_DIER_UIE; //прерывание не вызывать
TIM3->CNT = 0; //Очищаем счетный регистр
TIM3->ARR = ws281x_arr_timer_125_usec; // значение перезагрузки // 1.25 usec
TIM3->CCR3 = 0; // коэф. заполнения // WS2812 -> reset bus // PA8 LOW
TIM3->DIER |= TIM_DIER_CC3DE; //Разрешить запрос DMA
DMA1_Channel2->CNDTR = ws281XrealSizeDMAbuf; // передаем нужное количество
TIM3->CR1 |= TIM_CR1_CEN; //Запускаем таймер
DMA1_Channel2->CCR |= DMA_CCR_EN; // включаем передачу данных
}
void f303initDMA1CH2(void) {
DMA1_Channel2->CCR &= ~DMA_CCR_EN; // отключаем любое действие канала если идет передача
DMA1_Channel2->CPAR = (unsigned long)(&TIM3->CCR3); //заносим адрес регистра куда кидать
DMA1_Channel2->CMAR = (unsigned long)((unsigned char *)(ws281XbufPWM)); //заносим адрес данных в регистр CMAR
DMA1_Channel2->CCR &= ~DMA_CCR_MEM2MEM; //режим MEM2MEM отключен
DMA1_Channel2->CCR &= ~DMA_CCR_PL; //приоритет низкий
DMA1_Channel2->CCR &= ~DMA_CCR_MSIZE; //разрядность данных в памяти 8 бит // 00: 8-bits
DMA1_Channel2->CCR &= ~DMA_CCR_PSIZE; //разрядность регистра данных 16 бит
DMA1_Channel2->CCR |= DMA_CCR_PSIZE_0; //разрядность регистра данных 16 бит
DMA1_Channel2->CCR |= DMA_CCR_MINC; // включить инкремент адреса памяти
DMA1_Channel2->CCR &= ~DMA_CCR_PINC; //Инкремент адреса периферии отключен
DMA1_Channel2->CCR &= ~DMA_CCR_CIRC; //кольцевой режим отключен
DMA1_Channel2->CCR |= DMA_CCR_DIR; //1 - из памяти в периферию
DMA1->IFCR |= DMA_IFCR_CTCIF2; // Сбрасываем флаг прерываний
DMA1_Channel2->CCR |= DMA_CCR_TCIE; //прерывание завершения передачи
NVIC_EnableIRQ(DMA1_Channel2_IRQn); //от DMA
NVIC_SetPriority(DMA1_Channel2_IRQn,14);
}
void f303initWS281X(void) {
f303setBrBytes(30);
RCC->AHBENR |= RCC_AHBENR_GPIOBEN; // Включаем тактирование порта GPIOB
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // DMA1
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // clock Timer3 ON
f303initPB0pwm();
f303initDMA1CH2();
f303initTIM3pwmCH3();
}
void f303sendWS281X(unsigned char * inData, unsigned short dataCount) {
ws281Xmode = 1; // заняты передачей
DMA1_Channel2->CCR &= ~(DMA_CCR_EN); //Отключаем канал DMA
TIM3->CR1 &= ~(TIM_CR1_CEN); //останавливаем таймер
prepareDMAbuf(inData, dataCount);
TIM3->DIER &= ~TIM_DIER_CC3DE; // запретить запрос DMA
TIM3->CNT = 0; //Очищаем счетный регистр
TIM3->ARR = ws281x_arr_timer_reset_bus; // значение перезагрузки // 50+ usec // (50/1,25+1)*89 = 3649
TIM3->CCR3 = 0; // коэф. заполнения // WS2812 -> reset bus // PA8 LOW
TIM3->SR &= ~(TIM_SR_UIF); //сбрасываем флаг прерывания
TIM3->DIER |= TIM_DIER_UIE; //прерывание по обновлению
NVIC_EnableIRQ(TIM3_IRQn); // включим прерывание таймера
NVIC_SetPriority(TIM3_IRQn,13);
TIM3->CR1 |= TIM_CR1_CEN; //Поехали считать!
}
/*
* ws281eff.h
*
* Created on: 6 февр. 2023 г.
* Author: Andrey
*/
#ifndef WS281EFF_H_
#define WS281EFF_H_
#define control_wait_unuse_period ((unsigned long)20000) // время бездействия миллисекунд, после которого возврат в рабочий режим
#define ws281_min_period_change_pattern ((unsigned long)5) // минимальное время между сменой эффектов миллисекунд
#define ws281_max_period_change_pattern ((unsigned long)30) // максимальное время между сменой эффектов миллисекунд
#define ws281_min_work_pattern ((unsigned short)900) // число запусков одной программы до смены
void ws281loopWork(unsigned char * outBuf);
#endif /* WS281EFF_H_ */
/*
* ws281eff.c
*
* Created on: 6 февр. 2023 г.
* Author: Andrey
*/
#include <stdlib.h>
#include "ws281eff.h"
#include "f3ws281Y.h"
#include "f303cct6.h"
unsigned char stripInWork = 1; // гирлянда не в режиме настройки
unsigned char currentPattern = 0xFF; // bit7=1 -> random
unsigned char currentSpeed = 0xFF; // bit7=1 -> random
unsigned short tLoop= 0; // глобальный счетчик, должен быть минимум в 4 раза длиннее количества светодиодов, что бы все программы целиком успели отработать
typedef enum {modeChangePattern, modeChangeSpeed} ctrlMode;
void pattern_random(unsigned char * oneBuf, unsigned short tIn) {
if (tIn % 8) return;
for (unsigned short i = 0; i < count_bytes_in_buf; ++i) *(oneBuf+i) = random();
}
void pattern_snakes(unsigned char * oneBuf, unsigned short tIn) {
for (unsigned short i = 0; i < numLEDSws281X; ++i) {
unsigned short x = (i + (tIn >> 1)) % 64;
if (x < 10){
*(oneBuf+i*ws281x_bytes_per_pixel) = 0xFF; // R
} else if (x >= 15 && x < 25) {
*(oneBuf+i*ws281x_bytes_per_pixel+1) = 0xFF; // G
} else if (x >= 30 && x < 40) {
*(oneBuf+i*ws281x_bytes_per_pixel+2) = 0xFF; // B
} else {
*(oneBuf+i*ws281x_bytes_per_pixel) = 0x00; // R
*(oneBuf+i*ws281x_bytes_per_pixel+1) = 0x00; // G
*(oneBuf+i*ws281x_bytes_per_pixel+2) = 0x00; // B
}
if (ws281x_bytes_per_pixel>3) *(oneBuf+i*ws281x_bytes_per_pixel+3) = 0x00; // W
}
}
void pattern_sparkle(unsigned char * oneBuf, unsigned short tIn) {
if (tIn % 8) return;
for (unsigned short i = 0; i < numLEDSws281X; ++i) {
unsigned char putByte = random() % 16 ? 0 : 0xFF;
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+i*ws281x_bytes_per_pixel+j) = putByte;
}
}
void pattern_greys(unsigned char * oneBuf, unsigned short tIn) {
for (unsigned short i = 0; i < numLEDSws281X; ++i) {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+i*ws281x_bytes_per_pixel+j) = (unsigned char)tIn;
}
}
void pattern_blink(unsigned char * oneBuf, unsigned short tIn) {
static unsigned char cR, cG, cB;
static unsigned short currPos = 0;
static unsigned short currPeriod;
if (!currPos) { // start new
cR = random(); cG = random(); cB = random();
for (unsigned short i = 0; i < numLEDSws281X; ++i) {
*(oneBuf+i*ws281x_bytes_per_pixel) = cR;
*(oneBuf+i*ws281x_bytes_per_pixel+1) = cG;
*(oneBuf+i*ws281x_bytes_per_pixel+2) = cB;
if (ws281x_bytes_per_pixel>3) *(oneBuf+i*ws281x_bytes_per_pixel+3) = cR+cG+cB;
}
++currPos;
currPeriod = 20 + (random() >> 2);
} else { // cont
if (currPos == 3) for (unsigned short i = 0; i < count_bytes_in_buf; ++i) *(oneBuf+i) = 0;
if ((++currPos) >= currPeriod) currPos = 0;
}
}
void pattern_fillr(unsigned char * oneBuf, unsigned short tIn) {
if (tIn % 4) return;
static unsigned short i = 0;
static unsigned char fillMode = 0;
switch (fillMode) {
case 0: {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+i*ws281x_bytes_per_pixel+j) = random();
break;
}
case 1: {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+i*ws281x_bytes_per_pixel+j) = 0;
break;
}
case 2: {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+(numLEDSws281X-i-1)*ws281x_bytes_per_pixel+j) = random();
break;
}
case 3: {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+(numLEDSws281X-i-1)*ws281x_bytes_per_pixel+j) = 0;
break;
}
default: {}
}
if ((++i) >= numLEDSws281X) {
i = 0;
if ((++fillMode) >= 4) fillMode = 0;
}
}
void pattern_bomb(unsigned char * oneBuf, unsigned short tIn) {
static unsigned short s = 16;
if (tIn % s) return;
static unsigned short i = 0;
static unsigned char fillMode = 0;
static unsigned char c1R, c2R, c1G, c2G, c1B, c2B;
if ((!i) && (!fillMode)) {
c1R = random(); c1G = random(); c1B = random();
c2R = random(); c2G = random(); c2B = random();
s = 16;
}
switch (fillMode) {
case 0: {
*(oneBuf+i*ws281x_bytes_per_pixel) = c1R;
*(oneBuf+i*ws281x_bytes_per_pixel+1) = c1G;
*(oneBuf+i*ws281x_bytes_per_pixel+2) = c1B;
*(oneBuf+(numLEDSws281X-i-1)*ws281x_bytes_per_pixel) = c2R;
*(oneBuf+(numLEDSws281X-i-1)*ws281x_bytes_per_pixel+1) = c2G;
*(oneBuf+(numLEDSws281X-i-1)*ws281x_bytes_per_pixel+2) = c2B;
if (ws281x_bytes_per_pixel>3) {
*(oneBuf+i*ws281x_bytes_per_pixel+3) = c1R+c1G+c1B;
*(oneBuf+(numLEDSws281X-i-1)*ws281x_bytes_per_pixel+3) = c2R+c2G+c2B;
}
if (i) { // clear prev pixel
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+(i-1)*ws281x_bytes_per_pixel+j) = 0;
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+(numLEDSws281X-i)*ws281x_bytes_per_pixel+j) = 0;
}
break;
}
case 1: { // full fill pixel
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+((numLEDSws281X >> 1)-1-i)*ws281x_bytes_per_pixel+j) = 0xFF;
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(oneBuf+((numLEDSws281X >> 1)+i)*ws281x_bytes_per_pixel+j) = 0xFF;
break;
}
default: {}
}
if ((++i) >= (numLEDSws281X >> 1)) { // half strip
i = 0;
if ((++fillMode) >= 2) {
fillMode = 0;
for (unsigned short i = 0; i < count_bytes_in_buf; ++i) *(oneBuf+i) = 0;
} else {
s = 4;
}
}
}
typedef void (*pattern)(unsigned char * oneBuf, unsigned short tIn);
const struct {
pattern pat;
} pattern_table[] = {
{pattern_snakes},
{pattern_random},
{pattern_sparkle},
{pattern_greys},
{pattern_blink},
{pattern_fillr},
{pattern_bomb}
};
void showOnePattern(unsigned char * outBuf, unsigned char onePat, unsigned short inTloop) {
pattern_table[onePat].pat(outBuf, inTloop);
f303sendWS281X(outBuf, count_bytes_in_buf);
while(f303busyWS281X());
}
unsigned char getCountPattern() {
return count_of(pattern_table);
}
void clearStrip(unsigned char * outBuf) {
for (unsigned short i = 0; i < numLEDSws281X; ++i) {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(outBuf+i*ws281x_bytes_per_pixel+j) = 0;
}
f303sendWS281X(outBuf, count_bytes_in_buf);
while(f303busyWS281X());
}
void keysControlLoop(unsigned char * outBuf) {
unsigned char outKeys = f303keysA0A1read();
static unsigned char ctrlCurrentPattern; // bit7=1 -> random
static unsigned char ctrlCurrentSpeed; // bit7=1 -> random
static ctrlMode currentMode;
static unsigned long timerControl = 0;
if (outKeys & 1) { // short press A0
timerControl = millis();
if (stripInWork) { // start control mode
stripInWork = 0;
currentMode = modeChangePattern;
ctrlCurrentPattern = 0xFF;
ctrlCurrentSpeed = 0xFF;
clearStrip(outBuf);
} else { // save selected mode and goto next param
if (!stripInWork) { // in control - change params
switch (currentMode) {
case modeChangePattern: {
currentPattern = ctrlCurrentPattern;
clearStrip(outBuf);
++currentMode;
break;
}
case modeChangeSpeed: {
currentSpeed = ctrlCurrentSpeed;
stripInWork = 1;
break;
}
default:{}
}
tLoop = 2 * ws281_min_work_pattern; // reset counter
}
}
} else if (outKeys & 2) { // short press A1
timerControl = millis();
if (!stripInWork) { // in control - change params
switch (currentMode) {
case modeChangePattern: {
if (ctrlCurrentPattern & 0x80) {
ctrlCurrentPattern = 0;
} else if ((++ctrlCurrentPattern) >= getCountPattern()) {
ctrlCurrentPattern = 0xFF;
}
break;
}
case modeChangeSpeed: {
if (ctrlCurrentSpeed & 0x80) {
ctrlCurrentSpeed = 0;
} else {
ctrlCurrentSpeed += 10;
if (ctrlCurrentSpeed >= 101) {
ctrlCurrentSpeed = 0xFF;
}
}
break;
}
}
} else { // in strip mode work - change brightness
static unsigned char bigVal = 0;
unsigned char currBr = f303getBrBytes();
currBr += 10;
if (currBr >= ws281y_max_bright_percent) {
if (bigVal) {
currBr = 0; bigVal = 0;
} else {
bigVal = 1;
}
}
f303setBrBytes(currBr);
}
}
if (!stripInWork) {
if ((millis() - timerControl) >= control_wait_unuse_period) { // unuse control
tLoop = 2 * ws281_min_work_pattern; // reset counter
stripInWork = 1;
} else { // show current parameters
static unsigned long showControl = 0;
unsigned char showPat;
unsigned long showSpeed;
if ((ctrlCurrentPattern & 0x80)) {
showPat = random() % getCountPattern();
} else {
showPat = ctrlCurrentPattern;
}
if (ctrlCurrentSpeed & 0x80) {
showSpeed = ws281_min_period_change_pattern + (random() % 101) * (ws281_max_period_change_pattern - ws281_min_period_change_pattern) / 100;
} else {
showSpeed = ws281_min_period_change_pattern + (ctrlCurrentSpeed % 101) * (ws281_max_period_change_pattern - ws281_min_period_change_pattern) / 100;
}
if ((millis() - showControl) >= showSpeed) {
showControl = millis();
switch (currentMode) {
case modeChangePattern: {
showOnePattern(outBuf, showPat, tLoop);
break;
}
case modeChangeSpeed: {
static unsigned char i = 0;
static unsigned char outByte = 0xFF;
if (!(tLoop % 8)) {
if (ctrlCurrentSpeed & 0x80) {
for (unsigned short i = 0; i < 10; ++i) {
unsigned char bb;
if (i & 0x01) bb = 0xFF; else bb = 0x00;
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(outBuf+i*ws281x_bytes_per_pixel+j) = bb;
f303sendWS281X(outBuf, 10 * ws281x_bytes_per_pixel);
while(f303busyWS281X());
}
} else {
for (unsigned char j = 0; j < ws281x_bytes_per_pixel; ++j) *(outBuf+i*ws281x_bytes_per_pixel+j) = outByte;
f303sendWS281X(outBuf, 10 * ws281x_bytes_per_pixel);
while(f303busyWS281X());
if ((++i) >= 10) {
i = 0;
outByte = ~outByte;
}
}
}
break;
}
}
++tLoop;
}
if (tLoop >= ws281_min_work_pattern) tLoop = 0;
}
}
}
void ws281loopWork(unsigned char * outBuf) {
static unsigned char currentPat = 0; // текущая рабочая программа
static unsigned short patChange = ws281_min_work_pattern; // окончание глобального счетчика, после чего устанавливается скорость и программа
static unsigned long timerSpeed = 0; // таймер запуска каждой программы в цикле
static unsigned long periodSpeed = ws281_min_period_change_pattern; // период запуска программы в цикле
keysControlLoop(outBuf);
if (!stripInWork) return;
if (tLoop >= patChange) { // закончился глобальный счетчик
patChange = ws281_min_work_pattern + (unsigned char)random(); // в небольших пределах меняем время смены программ
if (currentPattern & 0x80) { // если случайный выбор программы
currentPat = random() % getCountPattern();
} else { // если фиксированная программа
currentPat = currentPattern;
}
if (currentSpeed & 0x80) { // если случайный выбор скорости
periodSpeed = ws281_min_period_change_pattern + (random() % 101) * (ws281_max_period_change_pattern - ws281_min_period_change_pattern) / 100;
} else { // фиксированная скорость
periodSpeed = ws281_min_period_change_pattern + (currentSpeed % 101) * (ws281_max_period_change_pattern - ws281_min_period_change_pattern) / 100;
}
tLoop = 0;
}
if ((millis() - timerSpeed) >= periodSpeed) {
timerSpeed = millis();
showOnePattern(outBuf, currentPat, tLoop);
++tLoop;
}
}