Недостатки:
1. Тут вообще ничего нет кроме зажигания всей ленты одним цветом и включения/выключения конкретных диодов.
2. Встроенных эффектов нет. (Для эффектов можно использовать разные цветовые модели (HSV,...))
3. В функции отправки данных на ленту есть цикл ожидания завершения этой операции ( while(!ws2812b_transferComplete); ). Его можно перекинуть в отдельную функцию и делать проверку на уровне приложения(убрать цикл).
Ещё:
Длина адресной диодной ленты задаётся в заголовочном файле ws2812b.h константой NumberOfLEDS. Но к длине нужно прибавить секцию сброса и конечный технический байт. Не стал добавлять ещё одну константу для использования в приложении. Поэтому размер массива имеет такой вид(используется в пользовательском коде):
uint8_t tbuf[48 + NumberOfLEDS * 24 + 1];
в коде используются:
Канал 2 таймера 2; Канал 7 DMA1; пин PA1 для передачи нанных на ленту.
/* ws2812b.h */#ifndef WS2812b_H_#define WS2812b_H_#define NumberOfLEDS 9 //устанавливается пользователем#define On 1#define Off 0struct color {
uint8_t G;
uint8_t R;
uint8_t B;
};
// Инициализацияvoid ws2812b_init(constvoid *tbuf);
//Отправить данные на адресную ленту.//(указатель на массив данных был определён в функции ws2812b_init()// параметром tbuf)void ws2812b_showLEDsArray(void);
//Заполнить массив определённым цветом или обнулить массив.// tbuf - массив диодов// color - указатель на цвет или 0 для обнуления массиваvoid ws2812b_fillLEDsArray(uint8_t *tbuf, struct color * Colr);
//Установить цвет определённого диода или выключить диод.// tbuf - массив диодов// on : 1 - вкл, 0 - выкл// number - номер диода. от нуля// color - цвет ( если on=0, то в color передать 0 )void ws2812b_changeLed(uint8_t *tbuf, uint8_t on, uint16_t number, struct color * Colr);
//void dma_initial(constvoid *tbuf);
void init_timers(void);
#endif
/* ws2812b.c */#include <stm32f10x.h>#include <stm32f10x_rcc.h>#include <stm32f10x_gpio.h>#include <stm32f10x_tim.h>#include <stm32f10x_dma.h>#include <misc.h>#include "ws2812b.h"#define bitValue0 29#define bitValue1 61uint8_t ws2812b_transferComplete = 1;
void init_timers(void) {
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//Настройка PA1 как выхода для канала 2 таймера 2.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// Включить тактирование таймера
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM2, ENABLE );
// configure timer// PWM frequency = 50 hz with 24 ,000 ,000 hz system clock// 24 ,000 ,000/480 = 50 ,000// 100 ,000/100 = 50
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure );
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 72 MHz
TIM_TimeBaseStructure.TIM_Period = 90 - 1; // 1,242 мкс. нужно 1,25 мкс
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit (TIM2, &TIM_TimeBaseStructure );
// CRR == 29 // 0.4 мкс "0"// CRR == 61 // 0.85 мкс "1"//для 24 МГц://период = 30 (1.251 мкс);//CCR 0.4 мкс = 10 (0,417 мкс)//CCR 0.85 мкс = 21 (0,875 мкс)// PWM1 Mode configuration : Channel2//Edge - aligned ; not single pulse mode
TIM_OCStructInit (&TIM_OCInitStructure );
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
//вкл выход на соответствующий выходной пин
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC2Init(TIM2, &TIM_OCInitStructure );
// вкл. Загрузка значения CCR2 в теневой регистр при событии обновления.
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM2->CNT = 0;
TIM2->CCR2 = 0;
TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE); //Разрешаем запросы DMA
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE); // Разрешаем возникновение прерывания// Enable Timer//TIM_Cmd(TIM2, ENABLE ); //вынесена в отдельную функцию//TIM2->CCER |= 0x10; // делается выше ( TIM_OutputState_Enable )
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // DISABLE
NVIC_Init(&NVIC_InitStructure);
}
//массив tbuf со значениями битов для диодной ленты.//Каждый бит задаётся байтом в массиве. Массив разбит на:// 1.секция сброса (после сброса передаваемые на ленту данные зажигают диоды С НАЧАЛА ленты).// Занимает 24 байта с нулевыми значениями. Линия будет притянута к земле// 2. секция данных. Тут находятся значения GBR для каждого диода.// 3. последний технический нулевой байт. Прижимает линию к земле после включения массива диодов.// (диоды остаются включёнными)// Пользователь работает с секцией данных через tbuf.//// Порядок передачи байтов на ленту в порядке GRB и первым идёт старший бит.void dma_initial(constvoid *tbuf){
DMA_InitTypeDef DMA_InitStructure;
DMA_Channel_TypeDef *txChan;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
txChan = DMA1_Channel7; //для канала 2 таймера 2
DMA_DeInit(txChan);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(TIM2->CCR2));
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
//
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)tbuf;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_Init(txChan, &DMA_InitStructure);
//после установки флага будет происходить прерывание от DMA
DMA_ITConfig(txChan, DMA_IT_TC, ENABLE);
// Enable channel//DMA_Cmd(txChan, ENABLE);// Wait for completion//while (DMA_GetFlagStatus(DMA1_FLAG_TC7) == RESET);// Disable channels//DMA_Cmd(txChan, DISABLE);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // DISABLE
NVIC_Init(&NVIC_InitStructure);
}
void ws2812b_init(constvoid *tbuf){
uint8_t i;
for ( i = 0; i < 48; i++ ) {
((uint8_t*)tbuf)[i] = 0; //заполнение секции Сброса
}
//конечный технический байт. Прижимает линию.
((uint8_t*)tbuf)[48 + NumberOfLEDS * 24] = 0;
init_timers();
dma_initial(tbuf);
}
void ws2812b_showLEDsArray(void){
ws2812b_transferComplete = 0;
DMA1_Channel7->CNDTR = 49 + NumberOfLEDS * 24;
// Enable DMA channel
DMA_Cmd(DMA1_Channel7, ENABLE);
TIM2->EGR |= 0x01; // для обновления регистров при включении таймера// Enable Timer
TIM_Cmd(TIM2, ENABLE);
while(!ws2812b_transferComplete);
}
// GRB и данные передаются старшим битом вперёдvoid ws2812b_fillLEDsArray(uint8_t *tbuf, struct color * Colr){
uint8_t colors8bit[8], threeColors[3];
uint8_t bit;
uint8_t * data = tbuf + 48;
uint8_t i, z;
uint16_t y;
if( Colr != (struct color *)0 ){
threeColors[0] = Colr->G;
threeColors[1] = Colr->R;
threeColors[2] = Colr->B;
}else {
threeColors[0] = 0;
threeColors[1] = 0;
threeColors[2] = 0;
}
for ( z = 0; z < 3; z++ ){
bit = 0x80;
for( i = 0; i < 8; i++ ){
if( threeColors[z] & bit ){
colors8bit[i] = bitValue1;
}else {
colors8bit[i] = bitValue0;
}
bit = bit >> 1;
}
for ( y = 0; y < NumberOfLEDS; y++ ){
for ( i = 0; i < 8; i++){
*(data + z*8 + y*24 + i) = colors8bit[i];
}
}
}
}
void ws2812b_changeLed(uint8_t *tbuf, uint8_t on, uint16_t number, struct color * Colr){
uint8_t colors8bit[8], threeColors[3];
uint8_t bit;
uint8_t * data = tbuf + 48;
uint8_t i, z;
if ( number >= NumberOfLEDS )
return;
if ( on ){
threeColors[0] = Colr->G;
threeColors[1] = Colr->R;
threeColors[2] = Colr->B;
}else{
threeColors[0] = 0;
threeColors[1] = 0;
threeColors[2] = 0;
}
for ( z = 0; z < 3; z++ ){
bit = 0x80;
for( i = 0; i < 8; i++ ){
if( threeColors[z] & bit ){
colors8bit[i] = bitValue1;
}else {
colors8bit[i] = bitValue0;
}
bit = bit >> 1;
}
for ( i = 0; i < 8; i++){
*(data + number*24 + z*8 + i) = colors8bit[i];
}
}
}
void DMA1_Channel7_IRQHandler(void){
if ( DMA1->ISR & DMA1_IT_TC7 ){
DMA1->IFCR |= (DMA1_IT_TC7 | DMA1_IT_HT7);
ws2812b_transferComplete = 1;
}
}
void TIM2_IRQHandler(void){
if( TIM2->SR & TIM_FLAG_CC2 ){
TIM2->SR &= ~TIM_FLAG_CC2; // rc_w0if ( ws2812b_transferComplete == 1 ){
// Disable Timer
TIM_Cmd(TIM2, DISABLE);
DMA_Cmd(DMA1_Channel7, DISABLE);
}
}
}
Комментарии