наводи мышу на эти желтые/красные там всплывающие подсказки везде
У меня и стоит 4…
SPI1 зелененький, значит включен.
Хм, ну это же копия (старая). Видимо в той “версии” я еще не настраивал делитель у SPI ))
Да, но точно с такими же настройками у другого проекта всё отлично…
только если использовать UART2 в синхроном режиме, патамушта ему будет нужна еще одна нога, которая сейчас занял NSS.
в асинхронном (обычном) режиме будет работать без проблем
еще раз, если интерфейс пожелтел, значит есть ограничение на его работу, если красный, тут полный конфликт и использовать его не получится.
конечно, я ж и написал - тщательно проверить все параметры периферии.
@xDriver , может быть тебе полезно будет:
Застрял вроде на пустом месте, кто разбирается в таймерах и DMA посмотрите плиз:
Изобретаю заново велосипед, пытаюсь светодиодную ленту запустить через таймеры, код по образу и подобию, который я ранее писал для F103 и F303, ломаться вроде негде.
STM32F411
DMA1 Channel 3 Stream 6
TIM2 Channel 2
GPIO PA1 AF01
Зажигаю 10 светодиодов RGBW
Соотвественно как и положено сначала сброс шины 50 мкСек
Потом отправка данных 404,5 мкСек
10 светодиодов * 4 байта * 8 бит * 1.25 мкСек = 400 мкСек + один байт нулефой отправляется чтоб шину в ноль отпустить.
Но самих данных (ШИМ) по факту нет
Спойлер
/*
* f4ws2811.h
*
* Created on: Oct 29, 2024
* Author: seleznev_a
*/
#ifndef F4WS2811_H_
#define F4WS2811_H_
#ifndef count_of
#define count_of(a) (sizeof(a)/sizeof((a)[0]))
#endif
#define numLEDSws281X ((unsigned short)10)
#define ws281x_bytes_per_pixel ((unsigned short)4) // 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)100) // максимально возможно выставляемая яркость в процентах из учета мощности БП и количества светодиодов
#define count_bytes_in_buf ((unsigned short)(numLEDSws281X*ws281x_bytes_per_pixel))
#define ws281x_arr_timer_125_usec ((unsigned short)125) // значение перезагрузки таймера // период 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 f4initWS281X(void);
void f4sendWS281X(unsigned char * inData, unsigned short dataCount);
bool f4busyWS281X(void);
void f4setBrBytes(unsigned char inVal);
unsigned char f4getBrBytes(void);
#endif /* F4WS2811_H_ */
/*
* f4ws2811.cpp
*
* Created on: Oct 29, 2024
* Author: seleznev_a
*/
#include "stm32f4xx.h"
#include "f4ws2811.h"
#include <stdint.h>
#include <stdio.h>
volatile bool ws281Xmode = false; // в процессе отправки
unsigned char ws281Ybrval; // яркость в процентах
unsigned long ws281XrealSizeDMAbuf; // реальный размер данных в буфере, кратно количеству пикселей * размер пикселя * 8
volatile unsigned char ws281XbufPWM[ws281x_max_count_bytes+1]; // сам буфер для отправки // +1 байт для подлнялия GPIO вверх после отправки последнего байта (или вниз, не помню)
void f4initPA1pwmT2_CH2_AF01(void) {
GPIOA->BSRR |= GPIO_BSRR_BS1; // PA1 HIGH
GPIOA->MODER |= GPIO_MODER_MODER1_1; // 10 — режим альтернативной функции.
GPIOA->MODER &= ~GPIO_MODER_MODER1_0; // 10 — режим альтернативной функции.
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_1; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1; // 11 — 50 MHz
GPIOA->AFR[0] |= GPIO_AFRL_AFRL1_0; // pin 1 alt func 1
}
void f4initDMA1CH3S6_T2_CH2(void) {
DMA1_Stream6->CR &= ~DMA_SxCR_EN; // отключаем DMA
DMA1_Stream6->PAR = (unsigned long)(&TIM2->CCR2); //заносим адрес регистра куда кидать
DMA1_Stream6->M0AR = (unsigned long)((unsigned char *)(ws281XbufPWM)); //заносим адрес данных в регистр CMAR
DMA1_Stream6->M1AR = 0; //(unsigned long)((unsigned char *)(ws281XbufPWM)); //заносим адрес данных в регистр CMAR
DMA1_Stream6->CR &= ~DMA_SxCR_PL; //приоритет низкий
DMA1_Stream6->CR &= ~DMA_SxCR_MSIZE; // Memory data size // 00: byte (8-bit)
DMA1_Stream6->CR &= ~DMA_SxCR_PSIZE; // Peripheral data size // 00: byte (8-bit)
DMA1_Stream6->CR |= DMA_SxCR_MINC; // 1: Memory address pointer is incremented after each data transfer (increment is done according to MSIZE)
DMA1_Stream6->CR &= ~DMA_SxCR_PINC; // 0: Peripheral address pointer is fixed
DMA1_Stream6->CR &= ~DMA_SxCR_CIRC; // 0: Circular mode disabled
DMA1_Stream6->CR &= ~DMA_SxCR_DIR; // 00: Peripheral-to-memory
DMA1_Stream6->CR |= DMA_SxCR_DIR_0; // 01: Memory-to-peripheral
}
void f4initTIM2pwmCH2(void) {
TIM2->CR1 &= ~(TIM_CR1_CEN); //останавливаем таймер
TIM2->CNT = 0; // Очищаем счетный регистр
TIM2->PSC = 0; // делитель x1 100 MHz
TIM2->ARR = ws281x_arr_timer_125_usec; // значение перезагрузки // 1.25 usec
TIM2->CCR2 = ws281x_ccr_timer_full_high; // коэф. заполнения
TIM2->CCMR1 &= ~(TIM_CCMR1_OC2M); //сбрасываем все биты OCxM
TIM2->CCMR1 |= TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2PE; // настроить канал // PWM Mode 1
TIM2->CCER |= TIM_CCER_CC2E; // настроим на выход канал 2
TIM2->BDTR = TIM_BDTR_MOE; // разрешим использовать выводы таймера как выходы
TIM2->CR1 &= ~TIM_CR1_DIR; // считаем вверх
TIM2->CR1 &= ~TIM_CR1_CMS; // выравнивание по фронту, Fast PWM
TIM2->CR1 |= TIM_CR1_ARPE; //Регистры таймера с буферизацией
}
void f4initWS281X(void) {
f4setBrBytes(50);
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // GPIO port A
f4initPA1pwmT2_CH2_AF01();
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; // DMA1
DMA1_Stream6->CR &= ~DMA_SxCR_EN; // отключаем DMA
DMA1_Stream6->CR &= ~DMA_SxCR_CHSEL; // 000: channel 0 selected
DMA1_Stream6->CR |= DMA_SxCR_CHSEL_0; // 011: channel 3 selected // T2_CH2
DMA1_Stream6->CR |= DMA_SxCR_CHSEL_1; // 011: channel 3 selected // T2_CH2
f4initDMA1CH3S6_T2_CH2();
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // clock Timer2 ON
f4initTIM2pwmCH2();
}
unsigned char f303getBrBytes(void) {
return ws281Ybrval;
}
bool f303busyWS281X(void) {
return ws281Xmode;
}
void f4setBrBytes(unsigned char inVal) {
if (inVal>ws281y_max_bright_percent) ws281Ybrval = ws281y_max_bright_percent;
else if (inVal==0) ws281Ybrval = 1;
else ws281Ybrval = inVal;
}
/*void f4tsartManualPWMtim2Ch2PA1(void) {
TIM2->CCR2 = ws281x_ccr_th0_timer;
TIM2->CR1 |= TIM_CR1_CEN; //Запускаем таймер
}*/
extern "C" void TIM2_IRQHandler(void) { //прерывание от таймера //Сюда попадаем после завершения формирования //сигнала RET шины ws2812b
TIM2->SR &= ~TIM_SR_UIF; //сбрасываем флаг прерывания
TIM2->CR1 &= ~TIM_CR1_CEN; //останавливаем таймер
TIM2->DIER &= ~TIM_DIER_UIE; //прерывание не вызывать
TIM2->CNT = 0; //Очищаем счетный регистр
TIM2->ARR = ws281x_arr_timer_125_usec; // значение перезагрузки // 1.25 usec
TIM2->CCR2 = 0; // коэф. заполнения // WS2812 -> reset bus // PA1 LOW
//f4tsartManualPWMtim2Ch2PA1();
DMA1_Stream6->CR &= ~DMA_SxCR_EN; // отключаем DMA
while(DMA1_Stream6->CR & DMA_SxCR_EN); // ждем как DMA отключится
DMA1->LIFCR |= DMA_LIFCR_CTCIF2; // сброс флага DAMA Transfer Complete
DMA1_Stream6->NDTR = ws281XrealSizeDMAbuf; // передаем нужное количество
TIM2->DIER |= TIM_DIER_CC2DE; //Разрешить запрос DMA
TIM2->CR1 |= TIM_CR1_CEN; //Запускаем таймер
DMA1_Stream6->CR |= DMA_SxCR_EN; // запускаем DMA
}
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 long 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 f4sendWS281X(unsigned char * inData, unsigned short dataCount) {
ws281Xmode = true; // заняты передачей
DMA1_Stream6->CR &= ~DMA_SxCR_EN; // отключаем DMA
TIM2->CR1 &= ~(TIM_CR1_CEN); //останавливаем таймер
prepareDMAbuf(inData, dataCount);
TIM2->DIER &= ~TIM_DIER_CC2DE; // запретить запрос DMA
TIM2->CNT = 0; //Очищаем счетный регистр
TIM2->ARR = ws281x_arr_timer_reset_bus; // значение перезагрузки // 50+ usec // (50/1,25+1)*125 = 5125
TIM2->CCR2 = 0; // коэф. заполнения // WS2812 -> reset bus // PA1 LOW
TIM2->SR &= ~(TIM_SR_UIF); //сбрасываем флаг прерывания
TIM2->DIER |= TIM_DIER_UIE; //прерывание по обновлению
NVIC_EnableIRQ(TIM2_IRQn); // включим прерывание таймера
NVIC_SetPriority(TIM2_IRQn,13);
TIM2->CR1 |= TIM_CR1_CEN; // Поехали считать!
}
если я убираю работу с DMA, комментирую строки с 138-144, и раскомментирую 137 - то вижу полноценный ШИМ и светодиоды прекрасно загораются.
Перевел на другой таймер TIM5 Ch2 - абсолютно аналогичный результат ![]()
Уже начинаю на чип грешить, если не взлетит - переводить на SPI+DMA.
Единственный таймер на котором это запустится - таймер1
Интересно - в чем его отличие? (Не стёб, действительно интересно отличие от остальных).
Он называется продвинутым. У него есть дополнительные режимы и схема тактирования отлична от других.
Это особенность всей F4 серии?






