add disco
This commit is contained in:
parent
0d77388545
commit
f00b7884f7
18 changed files with 943 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -29,6 +29,8 @@ V203/usb/scope/software/ui_mainwindow.h
|
||||||
V203/usb/spitest/*
|
V203/usb/spitest/*
|
||||||
V203F6P6/programmer/software/programmer
|
V203F6P6/programmer/software/programmer
|
||||||
minichlink/minichlink
|
minichlink/minichlink
|
||||||
|
V203F6P6/disco/fftpre.cxx
|
||||||
|
V203F6P6/disco/precomp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
60
V203F6P6/disco/Makefile
Normal file
60
V203F6P6/disco/Makefile
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
TARGET?= ch32v203
|
||||||
|
|
||||||
|
TOOL ?= gcc
|
||||||
|
#TOOL ?= clang
|
||||||
|
|
||||||
|
PRJ = example
|
||||||
|
|
||||||
|
VPATH = . ./$(TARGET)
|
||||||
|
BLD = ./build/
|
||||||
|
DFLAGS = -d
|
||||||
|
LFLAGS = -g
|
||||||
|
LDLIBS =
|
||||||
|
BFLAGS = --strip-unneeded
|
||||||
|
|
||||||
|
CFLAGS = -MMD -Wall -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||||
|
CFLAGS+= -I. -I./$(TARGET) -I./common
|
||||||
|
DEL = rm -f
|
||||||
|
|
||||||
|
# zdrojaky
|
||||||
|
OBJS = main.o hack.o fft.o
|
||||||
|
OBJS += adc.o spiclass.o
|
||||||
|
OBJS += ws2812b.o spectrum.o
|
||||||
|
|
||||||
|
include $(TARGET)/$(TOOL).mk
|
||||||
|
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||||
|
|
||||||
|
all: $(BLD) $(PRJ).elf
|
||||||
|
# ... atd.
|
||||||
|
-include $(BLD)*.d
|
||||||
|
# linker
|
||||||
|
$(PRJ).elf: $(BOBJS)
|
||||||
|
-@echo [LD $(TOOL),$(TARGET)] $@
|
||||||
|
@$(LD) $(LFLAGS) -o $(PRJ).elf $(BOBJS) $(LDLIBS)
|
||||||
|
-@echo "size:"
|
||||||
|
@$(SIZE) $(PRJ).elf
|
||||||
|
-@echo "listing:"
|
||||||
|
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
|
||||||
|
-@echo "OK."
|
||||||
|
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
|
||||||
|
# preloz co je potreba
|
||||||
|
$(BLD)%.o: %.c
|
||||||
|
-@echo [CC $(TOOL),$(TARGET)] $@
|
||||||
|
@$(CC) -std=gnu99 -c $(CFLAGS) $< -o $@
|
||||||
|
$(BLD)%.o: %.cpp
|
||||||
|
-@echo [CX $(TOOL),$(TARGET)] $@
|
||||||
|
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
||||||
|
$(BLD):
|
||||||
|
mkdir $(BLD)
|
||||||
|
flash: $(PRJ).elf
|
||||||
|
minichlink -w $(PRJ).bin flash -b
|
||||||
|
|
||||||
|
$(BLD)fft.o: fftpre.cxx fft.cpp fft.h config.h
|
||||||
|
./precomp: fft.cpp config.h
|
||||||
|
g++ -std=c++17 -Wall -Os fft.cpp -o precomp
|
||||||
|
fftpre.cxx: ./precomp
|
||||||
|
./precomp
|
||||||
|
# vycisti
|
||||||
|
clean:
|
||||||
|
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~ precomp fftpre.cxx
|
||||||
|
.PHONY: all clean flash
|
133
V203F6P6/disco/adc.cpp
Normal file
133
V203F6P6/disco/adc.cpp
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#include "system.h"
|
||||||
|
#include "oneway.h"
|
||||||
|
#include "adc.h"
|
||||||
|
|
||||||
|
static AdcDma * pInstance = nullptr;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
[[gnu::interrupt]] extern void DMA1_Channel1_IRQHandler();
|
||||||
|
}
|
||||||
|
void DMA1_Channel1_IRQHandler( void ) {
|
||||||
|
DMA1_Type::INTFR_DEF state (DMA1.INTFR);
|
||||||
|
if (state.B.GIF1 != RESET) { // Zřejmě zbytečné, ale pokud používám víc DMA
|
||||||
|
DMA1.INTFCR.B.CGIF1 = SET; // kanálů, pak to tak má být.
|
||||||
|
} else return; // Událost nevznikla pro kanál 1.
|
||||||
|
if (state.B.HTIF1 != RESET) {
|
||||||
|
DMA1.INTFCR.B.CHTIF1 = SET;
|
||||||
|
if (pInstance) pInstance->send (false);
|
||||||
|
}
|
||||||
|
if (state.B.TCIF1 != RESET) {
|
||||||
|
DMA1.INTFCR.B.CTCIF1 = SET;
|
||||||
|
if (pInstance) pInstance->send (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void EnableClock (void) noexcept {
|
||||||
|
// Enable DMA
|
||||||
|
RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
|
||||||
|
r.B.SRAMEN = SET;
|
||||||
|
r.B.DMA1EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// Enable ADC + GPIOA
|
||||||
|
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||||
|
r.B.ADC1EN = SET;
|
||||||
|
r.B.IOPAEN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
RCC.APB1PCENR.B.TIM3EN = SET; // Enable TIM3
|
||||||
|
RCC.CFGR0.B.ADCPRE = 3u; // PCLK2 divided by 8 as ADC clock (18 MHz, ! pretaktovano 14 MHz max).
|
||||||
|
// PIN PA0 / A0, PA1 / A1
|
||||||
|
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||||
|
r.B.MODE0 = 0u;
|
||||||
|
r.B.CNF0 = 0u;
|
||||||
|
/*r.B.MODE1 = 0u;
|
||||||
|
r.B.CNF1 = 0u;*/
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static inline void Timer3Init (uint32_t us) noexcept {
|
||||||
|
TIM3.PSC.R = 143u; // 1 MHz Fs
|
||||||
|
TIM3.ATRLR.R = us - 1u;
|
||||||
|
// TRGO update for ADC
|
||||||
|
TIM3.CTLR2.B.MMS = 2u;
|
||||||
|
}
|
||||||
|
static inline void AdcCalibrate (void) noexcept {
|
||||||
|
// RESET
|
||||||
|
RCC.APB2PRSTR.B.ADC1RST = SET;
|
||||||
|
RCC.APB2PRSTR.B.ADC1RST = RESET;
|
||||||
|
// set channels
|
||||||
|
ADC1.RSQR3__CHANNEL.modify([](ADC1_Type::RSQR3__CHANNEL_DEF & r) -> auto {
|
||||||
|
r.B.SQ1__CHSEL = 0u; // CH0
|
||||||
|
//r.B.SQ2 = 1u; // CH1
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
ADC1.RSQR1.B.L = 0u; // 1 regular conversion
|
||||||
|
ADC1.SAMPTR2_CHARGE2.modify([](ADC1_Type::SAMPTR2_CHARGE2_DEF & r) -> auto {
|
||||||
|
r.B.SMP0_TKCG0 = 7u;
|
||||||
|
r.B.SMP1_TKCG1 = 7u;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
ADC1.CTLR1.modify([](ADC1_Type::CTLR1_DEF & r) -> auto {
|
||||||
|
r.B.SCAN = SET;
|
||||||
|
//r.B.BUFEN = SET;
|
||||||
|
//r.B.PGA = 3u; // x64
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
|
||||||
|
ADC1.CTLR2.B.ADON = SET;
|
||||||
|
ADC1.CTLR2.B.RSTCAL = SET; // Launch the calibration by setting RSTCAL
|
||||||
|
while (ADC1.CTLR2.B.RSTCAL != RESET); // Wait until RSTCAL=0
|
||||||
|
ADC1.CTLR2.B.CAL = SET; // Launch the calibration by setting CAL
|
||||||
|
while (ADC1.CTLR2.B.CAL != RESET); // Wait until CAL=0
|
||||||
|
}
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
inline void AdcDma::Dma1Ch1Init () noexcept {
|
||||||
|
// Configure the peripheral data register address
|
||||||
|
DMA1.PADDR1.R = reinterpret_cast<size_t> (& ADC1.RDATAR_DR_ACT_DCG);
|
||||||
|
// Configure the memory address
|
||||||
|
DMA1.MADDR1.R = reinterpret_cast<size_t> (buffer);
|
||||||
|
// Configure the number of DMA tranfer to be performs on DMA channel 1
|
||||||
|
DMA1.CNTR1 .R = ADC_FULL_LEN;
|
||||||
|
// Configure increment, size, interrupts and circular mode
|
||||||
|
DMA1.CFGR1.modify([] (DMA1_Type::CFGR1_DEF & r) -> auto {
|
||||||
|
r.B.PL = 3u; // highest priority
|
||||||
|
r.B.MEM2MEM = RESET; // periferal -> memory
|
||||||
|
r.B.DIR = RESET;
|
||||||
|
r.B.MINC = SET; // memory increment
|
||||||
|
r.B.MSIZE = 1u; // 16-bit
|
||||||
|
r.B.PSIZE = 1u; // 16-bit
|
||||||
|
r.B.HTIE = SET; // INT Enable HALF
|
||||||
|
r.B.TCIE = SET; // INT Enable FULL
|
||||||
|
r.B.CIRC = SET; // Circular MODE
|
||||||
|
// Enable DMA Channel 1
|
||||||
|
r.B.EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
static inline void AdcPostInit (void) noexcept {
|
||||||
|
ADC1.CTLR2.modify([](ADC1_Type::CTLR2_DEF & r) -> auto {
|
||||||
|
r.B.DMA = SET;
|
||||||
|
r.B.EXTTRIG = SET;
|
||||||
|
r.B.EXTSEL = 4u; // TRGO event of timer 3
|
||||||
|
r.B.SWSTART = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
AdcDma::AdcDma() noexcept : pL (buffer), pH (buffer + ADC_HALF_LEN), dst (nullptr) {
|
||||||
|
pInstance = this;
|
||||||
|
EnableClock ();
|
||||||
|
Timer3Init (125u);
|
||||||
|
NVIC.EnableIRQ (DMA1_Channel1_IRQn);
|
||||||
|
AdcCalibrate();
|
||||||
|
Dma1Ch1Init ();
|
||||||
|
AdcPostInit ();
|
||||||
|
// start timer
|
||||||
|
TIM3.CTLR1.B.CEN = SET;
|
||||||
|
}
|
||||||
|
inline void AdcDma::send(const bool b) {
|
||||||
|
if (!dst) return;
|
||||||
|
if (b) dst->Send (pH, ADC_HALF_LEN);
|
||||||
|
else dst->Send (pL, ADC_HALF_LEN);
|
||||||
|
}
|
23
V203F6P6/disco/adc.h
Normal file
23
V203F6P6/disco/adc.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef ADCDMA_H
|
||||||
|
#define ADCDMA_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "oneway.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
class AdcDma {
|
||||||
|
static constexpr unsigned ADC_HALF_LEN = 1u << FFTORDER;
|
||||||
|
static constexpr unsigned ADC_FULL_LEN = ADC_HALF_LEN * 2u;
|
||||||
|
|
||||||
|
uint16_t * pL;
|
||||||
|
uint16_t * pH;
|
||||||
|
uint16_t buffer [ADC_FULL_LEN];
|
||||||
|
OneWay<uint16_t> * dst;
|
||||||
|
public:
|
||||||
|
explicit AdcDma () noexcept;
|
||||||
|
void attach (OneWay<uint16_t> & d) { dst = & d; }
|
||||||
|
void send (const bool b);
|
||||||
|
protected:
|
||||||
|
void Dma1Ch1Init ();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ADCDMA_H
|
1
V203F6P6/disco/ch32v203
Symbolic link
1
V203F6P6/disco/ch32v203
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../ch32v203/
|
1
V203F6P6/disco/common
Symbolic link
1
V203F6P6/disco/common
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../common/
|
101
V203F6P6/disco/complex.h
Normal file
101
V203F6P6/disco/complex.h
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
//
|
||||||
|
// C++ Interface: complex
|
||||||
|
//
|
||||||
|
// Description:
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Author: Mrazik <mraz@seznam.cz>, (C) 2009
|
||||||
|
//
|
||||||
|
// Copyright: See COPYING file that comes with this distribution
|
||||||
|
//
|
||||||
|
//
|
||||||
|
#ifndef COMPLEX_H
|
||||||
|
#define COMPLEX_H
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
typedef short real;
|
||||||
|
typedef int dreal; // 2. násobná šířka
|
||||||
|
|
||||||
|
static constexpr unsigned HSHIFT = 8u * sizeof(real) - 1u;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\class Complex
|
||||||
|
|
||||||
|
\author Mrazik <mrazik@volny.cz>
|
||||||
|
|
||||||
|
\brief Komplexní číslo a práce s ním
|
||||||
|
|
||||||
|
Zkrácená verze pracující s 16-bitovými integer. Pouze nutné metody pro FFT.
|
||||||
|
|
||||||
|
Je to základní datová položka filtru. Všechny metody jsou jednoduché, takže je možné použít inline funkce. */
|
||||||
|
class complex {
|
||||||
|
private:
|
||||||
|
real re; //!< reálná část
|
||||||
|
real im; //!< imaginární část
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Konstruktor
|
||||||
|
* @param x reálná část
|
||||||
|
* @param y imaginární část
|
||||||
|
*/
|
||||||
|
explicit constexpr complex (const real x, const real y) noexcept : re(x), im(y) {}
|
||||||
|
/**
|
||||||
|
* Default konstruktor (nastaví re=0.0,im=0.0)
|
||||||
|
*/
|
||||||
|
explicit constexpr complex () noexcept : re(0.0), im(0.0) {}
|
||||||
|
// constexpr complex (const complex & o) noexcept : re(o.re), im(o.im) {}
|
||||||
|
/**
|
||||||
|
* Sčítání
|
||||||
|
* @param a odkaz na sčítanec
|
||||||
|
* @param b odkaz na sčítanec
|
||||||
|
*/
|
||||||
|
void add (const complex & a, const complex & b) {
|
||||||
|
re = a.re + b.re;
|
||||||
|
im = a.im + b.im;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Odčítání
|
||||||
|
* @param a odkaz na číslo, od něhož se odčítá
|
||||||
|
* @param b odkaz na číslo, jež se odčítá
|
||||||
|
*/
|
||||||
|
void sub (const complex & a, const complex & b) {
|
||||||
|
re = a.re - b.re;
|
||||||
|
im = a.im - b.im;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Násobení - integer verze výsledek zmenší 2^16-krát. Čili 16.bit*16.bit=32.bit
|
||||||
|
* a vezme se jen horní 16.bitová část, zbytek se zahodí.
|
||||||
|
* @param a odkaz na násobenec
|
||||||
|
* @param b odkaz na násobitel
|
||||||
|
*/
|
||||||
|
void operator*= (const complex & a) {
|
||||||
|
real rt;
|
||||||
|
const dreal ar(a.re), br(re), ai(a.im), bi(im);
|
||||||
|
rt = (ar * br - ai * bi) >> HSHIFT;
|
||||||
|
im = (ar * bi + ai * br) >> HSHIFT;
|
||||||
|
re = rt;
|
||||||
|
}
|
||||||
|
void operator>>= (const real i) {
|
||||||
|
re >>= i;
|
||||||
|
im >>= i;
|
||||||
|
}
|
||||||
|
void setc (const real x, const real y) {
|
||||||
|
re = x;
|
||||||
|
im = y;
|
||||||
|
}
|
||||||
|
real getr (void) const {
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
real geti (void) const {
|
||||||
|
return im;
|
||||||
|
}
|
||||||
|
void conj (void) {
|
||||||
|
im = -im;
|
||||||
|
}
|
||||||
|
dreal norm () const {
|
||||||
|
const dreal a = re, b = im;
|
||||||
|
return a*a + b*b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
7
V203F6P6/disco/config.h
Normal file
7
V203F6P6/disco/config.h
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#ifndef _CONFIG_H
|
||||||
|
#define _CONFIG_H
|
||||||
|
|
||||||
|
static constexpr int FFTORDER = 8;
|
||||||
|
static constexpr unsigned FIFOLEN = 8u;
|
||||||
|
|
||||||
|
#endif // _CONFIG_H
|
197
V203F6P6/disco/fft.cpp
Normal file
197
V203F6P6/disco/fft.cpp
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
// fft.cpp - impelementation of class
|
||||||
|
// of fast Fourier transform - FFT
|
||||||
|
|
||||||
|
#include "fft.h"
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdio>
|
||||||
|
class ComplexDouble {
|
||||||
|
private:
|
||||||
|
double re;
|
||||||
|
double im;
|
||||||
|
public:
|
||||||
|
explicit constexpr ComplexDouble () noexcept : re(0.0), im(0.0) {} //!< konstruktor
|
||||||
|
explicit constexpr ComplexDouble (const double x, const double y) noexcept : re(x), im(y) {} //!< konstruktor
|
||||||
|
void setc (double x, double y) {re = x; im = y;}; //!< nastavení hodnoty
|
||||||
|
void operator&= (const ComplexDouble & x) { //!< přičti a normuj na jednotku
|
||||||
|
re += x.re;
|
||||||
|
im += x.im;
|
||||||
|
double a (re*re + im*im);
|
||||||
|
a = 1.0 / ::sqrt (a);
|
||||||
|
re *= a;
|
||||||
|
im *= a;
|
||||||
|
};
|
||||||
|
void operator*= (const ComplexDouble & x) { //!< znásob komplexním číslem
|
||||||
|
const double a(x.re*re - x.im*im);
|
||||||
|
im = x.re*im + x.im*re;
|
||||||
|
re = a;
|
||||||
|
};
|
||||||
|
double gre (void) const {return re;}; //!< návrat reálné části
|
||||||
|
double gim (void) const {return im;}; //!< návrat imaginární části
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static unsigned short Reverse [1 << FFTORDER];
|
||||||
|
static complex Ones [(1 << FFTORDER) >> 1];
|
||||||
|
// s tímhle nebude potřeba se zabývat - prosté přehození pořadí bitů
|
||||||
|
// Zde do pole pouze pro zrychlení (není pro to instrukce procesoru)
|
||||||
|
void IFFT::PrecomputeOrder (void) {
|
||||||
|
const unsigned int half = N / 2;
|
||||||
|
const unsigned int last = N - 1;
|
||||||
|
unsigned j = 0u;
|
||||||
|
for (unsigned i = 0; i < last; i++) {
|
||||||
|
Reverse [i] = j;
|
||||||
|
// printf ("i = %d, j = %d\n", i, j);
|
||||||
|
unsigned k = half;
|
||||||
|
while (k <= j) {
|
||||||
|
j -= k;
|
||||||
|
k /= 2;
|
||||||
|
}
|
||||||
|
j += k;
|
||||||
|
}
|
||||||
|
Reverse [last] = last;
|
||||||
|
}
|
||||||
|
void IFFT::PrecomputeOnes (void) {
|
||||||
|
static constexpr double coeff = double((1UL << (HSHIFT)) - 1);
|
||||||
|
const unsigned int half = N >> 1;
|
||||||
|
unsigned int i,j,k;
|
||||||
|
real reo, imo;
|
||||||
|
// Tady to spočteme trochu jinak, bez použití sin a cos (ale stejně je potřeba sqrt)
|
||||||
|
ComplexDouble reone (1.0, 0.0), xx; // reálná jednotka, pomocná proměnná
|
||||||
|
ComplexDouble * aa = new ComplexDouble [M]; // pole komplexních jednotek - základní rotace
|
||||||
|
k = M-1;
|
||||||
|
aa[0].setc (0.0, 1.0); // nultá rotace je o 90st. - komplexní jednotka
|
||||||
|
for (i=1; i<k; i++) { // další budou vždy polovinou předchozího úhlu
|
||||||
|
aa[i] = aa[i-1]; // tedy nastavíme předchozí úhel
|
||||||
|
aa[i] &= reone; // sečteme ho s reálnou jednotkou a normujeme
|
||||||
|
}
|
||||||
|
for (i=0; i<half; i++) {
|
||||||
|
xx.setc (1.0, 0.0); // nastavit základ, kterýn budeme rotovat = reálná jednotka
|
||||||
|
for (j=0; j<k; j++) { // procházíme jednotlivé bity indexu i
|
||||||
|
// zleva - pokud je nastaven násobíme příslušně otočenou jednotkou
|
||||||
|
if (i & (1 << (k-j-1))) xx *= aa[j];
|
||||||
|
}
|
||||||
|
// a sinus i kosinus je hotov
|
||||||
|
reo = (real) (coeff * xx.gre()); // znásobíme koeficientem
|
||||||
|
imo = (real) (coeff * xx.gim()); // a je to
|
||||||
|
|
||||||
|
Ones [i].setc (reo, imo);
|
||||||
|
}
|
||||||
|
delete [] aa;
|
||||||
|
}
|
||||||
|
void IFFT::Precompute() {
|
||||||
|
PrecomputeOrder();
|
||||||
|
PrecomputeOnes();
|
||||||
|
FILE * out = fopen("fftpre.cxx","w");
|
||||||
|
fprintf(out, "/* GENERATED FILE - DO NOT EDIT. */\n");
|
||||||
|
fprintf(out, "static const unsigned short Reverse [1 << FFTORDER] = {");
|
||||||
|
for (unsigned n=0u; n<N; n++) {
|
||||||
|
if ((n%8) == 0) fprintf(out, "\n");
|
||||||
|
fprintf(out, " 0x%04Xu,", Reverse[n]);
|
||||||
|
}
|
||||||
|
fprintf(out, "\n};\n");
|
||||||
|
/* Tímto debilním způsobem to dostanu do flash, nenašel jsem jak to udělat s complex */
|
||||||
|
fprintf(out, "struct point {short x,y;};\n");
|
||||||
|
fprintf(out, "static const point Ones [(1 << FFTORDER) >> 1] = {");
|
||||||
|
const unsigned H = N >> 1;
|
||||||
|
for (unsigned n=0u; n<H; n++) {
|
||||||
|
if ((n%4) == 0) fprintf(out, "\n");
|
||||||
|
complex c = Ones[n];
|
||||||
|
fprintf(out, " {%+6d,%+6d},", c.getr(), c.geti());
|
||||||
|
}
|
||||||
|
fprintf(out, "\n};\n");
|
||||||
|
fprintf(out, "static const unsigned short CosWindow [1 << FFTORDER] = {");
|
||||||
|
static constexpr double alpha = 2.0 * M_PI / double (1 << FFTORDER), ampl = double ((1l << 15) - 1);
|
||||||
|
for (unsigned n=0u; n<N; n++) {
|
||||||
|
if ((n%8) == 0) fprintf(out, "\n");
|
||||||
|
const double w = ampl * (1.0 - ::cos (double(n) * alpha));
|
||||||
|
fprintf(out, " 0x%04lXu,", ::lround(w));
|
||||||
|
}
|
||||||
|
fprintf(out, "\n};\n");
|
||||||
|
fclose(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main () {
|
||||||
|
IFFT ifft;
|
||||||
|
ifft.Precompute();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include "fftpre.cxx"
|
||||||
|
static_assert (sizeof(complex) == sizeof(point), "size failed");
|
||||||
|
void IFFT::Precompute () {}
|
||||||
|
void IFFT::PrecomputeOnes () {}
|
||||||
|
void IFFT::PrecomputeOrder () {}
|
||||||
|
|
||||||
|
void CopyToComplex (complex * dst, const unsigned short * src, const unsigned len) {
|
||||||
|
for (unsigned n=0u; n<len; n++) {
|
||||||
|
int k = src [n] - 0x800, m = CosWindow[n];
|
||||||
|
dst [n].setc ((k * m) >> 12, 0); // rozšířit z 12 na 16 bitů pro FFT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
IFFT::IFFT() noexcept : reverse(Reverse), ones(reinterpret_cast<const complex * const> (Ones)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// FORWARD FOURIER TRANSFORM, INPLACE VERSION
|
||||||
|
// Data - both input data and output
|
||||||
|
bool IFFT::Forward (complex * const Data) const {
|
||||||
|
if (!Data) return false;
|
||||||
|
Rearrange (Data);
|
||||||
|
Perform (Data, false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// INVERSE FOURIER TRANSFORM, INPLACE VERSION
|
||||||
|
// Data - both input data and output
|
||||||
|
bool IFFT::Inverse (complex * const Data) const {
|
||||||
|
if (!Data) return false;
|
||||||
|
Rearrange (Data);
|
||||||
|
Perform (Data, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Změna pořadí ve vstupním poli
|
||||||
|
void IFFT::Rearrange (complex * const Data) const {
|
||||||
|
unsigned int Target, Position;
|
||||||
|
complex Temp;
|
||||||
|
for (Position = 0; Position < N; Position++) {
|
||||||
|
Target = reverse [Position];
|
||||||
|
if (Target > Position) {
|
||||||
|
Temp = Data [Target];
|
||||||
|
Data [Target] = Data [Position];
|
||||||
|
Data [Position] = Temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FFT implementation
|
||||||
|
void IFFT::Perform (complex * const Data, const bool Inverse) const {
|
||||||
|
unsigned int Dest, Step, Jump, Group, Pair, Incr, Roti;
|
||||||
|
complex Factor, Produc;
|
||||||
|
static constexpr real Shift = 1; // Úprava velikosti při forwardu, jinak to přeteče
|
||||||
|
|
||||||
|
for (Step = 1; Step < N; Step <<= 1) { // 1,2,...N/2
|
||||||
|
// printf ("1.cyklus Step=%4d\n", Step);
|
||||||
|
Jump = Step << 1; // 2,4,...N
|
||||||
|
Incr = N / Jump; // N/2....1
|
||||||
|
Roti = 0;
|
||||||
|
for (Group = 0; Group < Step; Group++) {
|
||||||
|
// printf (" 2.cyklus Group=%4d, rotace=%4d\n", Group, Roti);
|
||||||
|
for (Pair = Group; Pair < N; Pair += Jump) {
|
||||||
|
Dest = Pair + Step;
|
||||||
|
Factor = ones [Roti];
|
||||||
|
Produc = Data [Pair];
|
||||||
|
if (!Inverse) {
|
||||||
|
Factor.conj();
|
||||||
|
Factor >>= Shift; // shifty s integer doprava o 1 bit
|
||||||
|
Produc >>= Shift; // Zároveň normalizace (není potřeba při reverzi).
|
||||||
|
}
|
||||||
|
Factor *= Data [Dest];
|
||||||
|
// printf (" 3.cyklus Pair=%4d, Dest =%4d\n", Pair, Dest);
|
||||||
|
Data [Pair].add (Produc, Factor);
|
||||||
|
Data [Dest].sub (Produc, Factor);
|
||||||
|
}
|
||||||
|
Roti += Incr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
V203F6P6/disco/fft.h
Normal file
29
V203F6P6/disco/fft.h
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// fft.h - declaration of class
|
||||||
|
// of fast Fourier transform - FFT
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef _FFT_H_
|
||||||
|
#define _FFT_H_
|
||||||
|
|
||||||
|
// Include Complex numbers header
|
||||||
|
#include "config.h"
|
||||||
|
#include "complex.h"
|
||||||
|
|
||||||
|
class IFFT {
|
||||||
|
static constexpr unsigned int M = FFTORDER, N = 1u << FFTORDER;
|
||||||
|
public:
|
||||||
|
explicit IFFT () noexcept;
|
||||||
|
bool Forward (complex * const Data) const;
|
||||||
|
bool Inverse (complex * const Data) const;
|
||||||
|
void Precompute ();
|
||||||
|
protected:
|
||||||
|
void Rearrange (complex * const Data) const;
|
||||||
|
void Perform (complex * const Data, const bool Inverse = false) const;
|
||||||
|
void PrecomputeOrder ();
|
||||||
|
void PrecomputeOnes ();
|
||||||
|
private:
|
||||||
|
const unsigned short * const reverse;
|
||||||
|
const complex * const ones;
|
||||||
|
};
|
||||||
|
extern void CopyToComplex (complex * dst, const unsigned short * src, const unsigned len);
|
||||||
|
#endif
|
21
V203F6P6/disco/hack.c
Normal file
21
V203F6P6/disco/hack.c
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
size_t strlen (const char *s) {
|
||||||
|
size_t l = 0;
|
||||||
|
while (*s++) l++;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
void *memcpy (void *dest, const void *src, size_t n) {
|
||||||
|
const char *s = (const char *) src;
|
||||||
|
char *d = (char *) dest;
|
||||||
|
int i;
|
||||||
|
for (i=0; i<n; i++) d[i] = s[i];
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
void *memset (void *s, int c, size_t n) {
|
||||||
|
char *p = (char *) s;
|
||||||
|
int i;
|
||||||
|
for (i=0; i<n; i++) p[i] = c;
|
||||||
|
return s;
|
||||||
|
}
|
52
V203F6P6/disco/main.cpp
Normal file
52
V203F6P6/disco/main.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#include "config.h"
|
||||||
|
#include "system.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "fifo.h"
|
||||||
|
#include "spectrum.h"
|
||||||
|
#include "ws2812b.h"
|
||||||
|
#include "adc.h"
|
||||||
|
#include "spiclass.h"
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
/* V mládí jsme stavěli různé "barevné hudby" což bylo pár analogových
|
||||||
|
* pásmových propustí a na nich pověšený usměrňovač plus budič (barevné)
|
||||||
|
* žárovky.
|
||||||
|
* Tady jsem to zkusil celé udělat digitálně. ADC procesoru analogový
|
||||||
|
* signál zdigitalizuje se vzorkovací frekvencí 8kHz, prožene se to rychlou
|
||||||
|
* Fourierovou transformací a ze vzniklého spektra se vyberou ty správné
|
||||||
|
* části, posčítají se amplitudy a to se předá do digitální LED.
|
||||||
|
* Asi by se s tím daly dělat zajímavé efekty, zde je to jen napodobenina
|
||||||
|
* toho analogového zažízení z mládí - 3 pásma pro basy, středy a výšky
|
||||||
|
* a ledky svítí jednou barvou - červená pro basy, zelená středy a modrá
|
||||||
|
* výšky. Míchat barvy do jedné ledky nevypadá dobře - je to poslední
|
||||||
|
* komplementání ledka v řetězci - svítí většinou bíle.
|
||||||
|
* Má to několik malých ďáblíků. Ledky se chovají jinak než žárovky.
|
||||||
|
* Žárovka potřebuje určitý malý výkon aby vůbec jen trochu svítila.
|
||||||
|
* Ledka svítí i při nepatrném proudu a působí to rušivě. Lze to odstranit
|
||||||
|
* zavedením malého ofsetu. Ledka také nemá setrvačnost, takže to může
|
||||||
|
* blikat velice divoce. Setrvačnost by šlo zavést programově, času je dost,
|
||||||
|
* ale nepovažoval jsem to za nutné.
|
||||||
|
* Rozdělení na pásma je proti analogu dost striktní. Na výšky je rezervováno
|
||||||
|
* celé pásmo od 2 do 4kHz, ukazuje se však, že tam toho signálu stejně moc
|
||||||
|
* není. Celé to nastavit je poměrně dost práce, zde to vyžaduje přibližně
|
||||||
|
* úroveň linkového signálu ze zvukovky. Původně jsem předpokládal, že
|
||||||
|
* bude nutné do ledek předávat něco jako logaritmus amplitudy signálu,
|
||||||
|
* běžně se to tak FFT dělá a je to docela logické. Jenže takhle to nefunguje.
|
||||||
|
* Lidský sluch i zrak mají citlivost přibližně logaritmickou a svit ledky
|
||||||
|
* by pak byl vlastně komprimován dvakrát, z čehož je zrak pak zmaten.
|
||||||
|
*/////////////////////////////////////////////////////////////////////////
|
||||||
|
static GpioClass led (GPIOB, 8); // indikace běhu FFT
|
||||||
|
static FIFO<uint32_t, FIFOLEN> ring; // výměna barvy led
|
||||||
|
static AdcDma adc; // zdroj signálu
|
||||||
|
static Spectrum spectrum (ring, led);// vlastní FFT + vyhodnocení
|
||||||
|
static ws2812b chain (ring); // řetězec led WS2812B
|
||||||
|
static SpiClass spi (chain); // ovládání tt. retězce
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
int main () {
|
||||||
|
led << true;
|
||||||
|
adc.attach(spectrum);
|
||||||
|
spi.Init ();
|
||||||
|
for (;;) {
|
||||||
|
/* do nothing */
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
46
V203F6P6/disco/spectrum.cpp
Normal file
46
V203F6P6/disco/spectrum.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#include "ws2812b.h"
|
||||||
|
#include "spectrum.h"
|
||||||
|
|
||||||
|
static uint8_t saturate (const unsigned k, const unsigned ofs = 0x100u) {
|
||||||
|
if (k < ofs) return 0u; // začátek je třeba trochu posunout
|
||||||
|
const unsigned x = k - ofs; // jinak ledky trochu svítí - žárovka tuto vlastnost nemá
|
||||||
|
if (x >= (1u << 16)) return 0xffu; // zasarutuj
|
||||||
|
unsigned w, y = 0xffu; // aproximaci začneme od 255
|
||||||
|
for (unsigned n=0u; n<6u; n++) { // končí cca za 3-4 iterace, max. 6
|
||||||
|
w = (y + x/y) >> 1; // Newtonova metoda sqrt
|
||||||
|
if (y == w) break; // konec iterace - lepší už to nebude
|
||||||
|
else y = w;
|
||||||
|
}
|
||||||
|
return y; // výsledek
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Spectrum::Send(uint16_t * const ptr, const unsigned int len) {
|
||||||
|
led << true;
|
||||||
|
// procesor je rychlý, zvládne FFT i s kosinovým oknem v přerušení
|
||||||
|
CopyToComplex (buffer, ptr, len);
|
||||||
|
ifft.Forward (buffer);
|
||||||
|
// vyhodnotí barvy pro basy, středy a výšky
|
||||||
|
unsigned qa = 0u;
|
||||||
|
for (unsigned n=1u; n<blow; n++) qa += buffer[n].norm();
|
||||||
|
cred = saturate (qa >> 0, 0x200u);
|
||||||
|
qa = 0u;
|
||||||
|
for (unsigned n=blow; n<bmiddle; n++) qa += buffer[n].norm();
|
||||||
|
cgreen = saturate (qa >> 4);
|
||||||
|
qa = 0u;
|
||||||
|
for (unsigned n=bmiddle; n<bhigh; n++) qa += buffer[n].norm();
|
||||||
|
cblue = saturate (qa >> 2);
|
||||||
|
// uloží barvy do ledek
|
||||||
|
Entry l0(0), l1(0), l2(0), l3(0);
|
||||||
|
l0.ws.order = 0; l0.ws.r = cred;
|
||||||
|
l1.ws.order = 1; l1.ws.g = cgreen;
|
||||||
|
l2.ws.order = 2; l2.ws.b = cblue;
|
||||||
|
l3.ws.order = 3; l3.ws.r = 0xffu - cred; l3.ws.g = 0xffu - cgreen; l3.ws.b = 0xffu - cblue;
|
||||||
|
// a frontou pošle na výstup
|
||||||
|
ring.Write (l0.number);
|
||||||
|
ring.Write (l1.number);
|
||||||
|
ring.Write (l2.number);
|
||||||
|
ring.Write (l3.number);
|
||||||
|
// změří čas výpočtu (pod 0.5ms, rámec je 32ms)
|
||||||
|
led << false;
|
||||||
|
return len;
|
||||||
|
}
|
24
V203F6P6/disco/spectrum.h
Normal file
24
V203F6P6/disco/spectrum.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef SPECTRUM_H
|
||||||
|
#define SPECTRUM_H
|
||||||
|
#include "oneway.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "fifo.h"
|
||||||
|
#include "fft.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
class Spectrum : public OneWay<uint16_t> {
|
||||||
|
static constexpr unsigned FFTSIZE = 1 << FFTORDER;
|
||||||
|
GpioClass & led;
|
||||||
|
FIFO<uint32_t, FIFOLEN> & ring;
|
||||||
|
const unsigned bhigh, bmiddle, blow;
|
||||||
|
uint8_t cred, cgreen, cblue;
|
||||||
|
const IFFT ifft;
|
||||||
|
complex buffer [FFTSIZE];
|
||||||
|
public:
|
||||||
|
explicit Spectrum (FIFO<uint32_t, FIFOLEN> & ff, GpioClass & io) noexcept : OneWay<uint16_t>(), led(io), ring(ff),
|
||||||
|
bhigh (FFTSIZE >> 1), bmiddle(bhigh >> 1), blow (bmiddle >> 4), cred(0u), cgreen(0u), cblue(0u), ifft() {}
|
||||||
|
unsigned int Send(uint16_t * const ptr, const unsigned int len) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPECTRUM_H
|
118
V203F6P6/disco/spiclass.cpp
Normal file
118
V203F6P6/disco/spiclass.cpp
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
#include "system.h"
|
||||||
|
#include "spiclass.h"
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
|
||||||
|
enum SPICLK : uint32_t {
|
||||||
|
FPCLK_2 = 0u, // 72 MHz
|
||||||
|
FPCLK_4, // 36 MHz
|
||||||
|
FPCLK_8, // 18 MHz
|
||||||
|
FPCLK_16, // 9 MHz
|
||||||
|
FPCLK_32, // 4.5 MHz
|
||||||
|
FPCLK_64, // 2.25 MHz
|
||||||
|
FPCLK_128, // 1.125 MHz
|
||||||
|
FPCLK_256, // 0.5625 MHz
|
||||||
|
};
|
||||||
|
|
||||||
|
static SpiClass * pSpiInstance = nullptr;
|
||||||
|
extern "C" {
|
||||||
|
//[[gnu::interrupt]] extern void DMA1_Channel2_IRQHandler();
|
||||||
|
[[gnu::interrupt]] extern void DMA1_Channel3_IRQHandler();
|
||||||
|
extern void * memcpy (void * dest, const void * src, size_t n);
|
||||||
|
};
|
||||||
|
void DMA1_Channel3_IRQHandler() { // transmit channel
|
||||||
|
if (pSpiInstance) pSpiInstance->drq();
|
||||||
|
}
|
||||||
|
static constexpr unsigned FM = 3u; // 50 MHz
|
||||||
|
static void InitPins () noexcept {
|
||||||
|
// PA4 - NSS, PA5 - SCK, PA6 - MISO, PA7 - MOSI
|
||||||
|
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> uint32_t {
|
||||||
|
/*r.B.MODE4 = FM;
|
||||||
|
r.B.CNF4 = 2u; // alt push - pull
|
||||||
|
r.B.MODE6 = 0u; // input mode
|
||||||
|
r.B.CNF6 = 1u; // floating */
|
||||||
|
r.B.MODE5 = FM;
|
||||||
|
r.B.CNF5 = 2u; // alt push - pull
|
||||||
|
r.B.MODE7 = FM;
|
||||||
|
r.B.CNF7 = 2u; // alt push - pull
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// AFIO - default
|
||||||
|
}
|
||||||
|
void SpiClass::drq() {
|
||||||
|
if (!driver) return;
|
||||||
|
DMA1_Type::INTFR_DEF state (DMA1.INTFR);
|
||||||
|
if (state.B.GIF3 != RESET) { // Zřejmě zbytečné, ale pokud používám víc DMA
|
||||||
|
DMA1.INTFCR.B.CGIF3 = SET; // kanálů, pak to tak má být.
|
||||||
|
} else return; // Událost nevznikla pro kanál 1.
|
||||||
|
/*if (state.B.HTIF3) {
|
||||||
|
DMA1.INTFCR.B.CHTIF3 = SET; // clear half
|
||||||
|
} */
|
||||||
|
if (state.B.TCIF3) {
|
||||||
|
DMA1.INTFCR.B.CTCIF3 = SET; // clear complete
|
||||||
|
driver->Send (ptrh, LEDS_LEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpiClass::SpiClass(OneWay<uint8_t> & base) noexcept : driver (& base), /*ptrl (buffer),*/ ptrh (buffer + PADDING) {
|
||||||
|
pSpiInstance = this;
|
||||||
|
for (unsigned n=0u; n<FULL_LEN; n++) buffer[n] = 0u;
|
||||||
|
Color * ptr = reinterpret_cast<Color*>(ptrh);
|
||||||
|
const OneColor oz(0x00);
|
||||||
|
for (unsigned n=0; n<NUMLEDS; n++) {
|
||||||
|
Color & c = ptr [n];
|
||||||
|
c.b = oz; c.g = oz; c.r = oz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void SpiClass::Init() {
|
||||||
|
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> uint32_t {
|
||||||
|
r.B.SPI1EN = SET;
|
||||||
|
r.B.IOPAEN = SET;
|
||||||
|
r.B.AFIOEN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
RCC.AHBPCENR.B.DMA1EN = SET;
|
||||||
|
InitPins();
|
||||||
|
// Configure the peripheral data register address
|
||||||
|
DMA1.PADDR3.R = reinterpret_cast<size_t> (& SPI1.DATAR);
|
||||||
|
// Configure the memory address
|
||||||
|
DMA1.MADDR3.R = reinterpret_cast<size_t> (buffer);
|
||||||
|
// Configure the number of DMA tranfer to be performs on DMA channel 3
|
||||||
|
DMA1.CNTR3 .R = FULL_LEN;
|
||||||
|
// Configure increment, size, interrupts and circular mode
|
||||||
|
DMA1.CFGR3.modify([] (DMA1_Type::CFGR3_DEF & r) -> uint32_t {
|
||||||
|
r.B.PL = 3u; // highest priority
|
||||||
|
r.B.DIR = SET; // memory -> periferal
|
||||||
|
r.B.MINC = SET; // memory increment
|
||||||
|
r.B.MSIZE = 0u; // 8-bit
|
||||||
|
r.B.PSIZE = 0u; // 8-bit
|
||||||
|
//r.B.HTIE = SET; // INT Enable HALF
|
||||||
|
r.B.TCIE = SET; // INT Enable FULL
|
||||||
|
r.B.CIRC = SET; // Circular MODE
|
||||||
|
// Enable DMA Channel 1
|
||||||
|
r.B.EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
SPI1.CTLR1.modify([](SPI1_Type::CTLR1_DEF & r) -> uint32_t {
|
||||||
|
r.B.CPHA = RESET;
|
||||||
|
r.B.CPOL = RESET;
|
||||||
|
r.B.MSTR = SET;
|
||||||
|
r.B.DFF = RESET; // 8 bit
|
||||||
|
r.B.SSM = SET;
|
||||||
|
r.B.SSI = SET;
|
||||||
|
r.B.LSBFIRST = SET;
|
||||||
|
/* 2.25 MHz - 1bit = 444 ns
|
||||||
|
* 1 LED => 9 x 8 x 0.444 = 32 us DMA celkem (10 + 4) x 32 = 0.448 ms
|
||||||
|
* */
|
||||||
|
r.B.BR = FPCLK_64;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
SPI1.CTLR2.modify([](SPI1_Type::CTLR2_DEF & r) -> uint32_t {
|
||||||
|
r.B.SSOE = SET;
|
||||||
|
//r.B.RXNEIE = SET;
|
||||||
|
//r.B.TXEIE = SET;
|
||||||
|
r.B.TXDMAEN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
NVIC.EnableIRQ(DMA1_Channel3_IRQn);
|
||||||
|
SPI1.CTLR1.B.SPE = SET;
|
||||||
|
}
|
23
V203F6P6/disco/spiclass.h
Normal file
23
V203F6P6/disco/spiclass.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef SPICLASS_H
|
||||||
|
#define SPICLASS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "ws2812b.h"
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
static constexpr unsigned PADDING = 10 * sizeof (Color);
|
||||||
|
static constexpr unsigned LEDS_LEN = NUMLEDS * sizeof (Color);
|
||||||
|
static constexpr unsigned FULL_LEN = PADDING + LEDS_LEN;
|
||||||
|
|
||||||
|
class SpiClass {
|
||||||
|
OneWay<uint8_t> * driver;
|
||||||
|
//uint8_t * const ptrl;
|
||||||
|
uint8_t * const ptrh;
|
||||||
|
uint8_t buffer [FULL_LEN];
|
||||||
|
public:
|
||||||
|
explicit SpiClass (OneWay<uint8_t> & base) noexcept;
|
||||||
|
void Init ();
|
||||||
|
void drq ();
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPICLASS_H
|
50
V203F6P6/disco/ws2812b.cpp
Normal file
50
V203F6P6/disco/ws2812b.cpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include "ws2812b.h"
|
||||||
|
|
||||||
|
enum WS2812B_SPI : uint32_t {
|
||||||
|
BIT_LOW = 1u, BIT_HIGH = 3u,
|
||||||
|
};
|
||||||
|
|
||||||
|
OneColor::OneColor(const uint8_t color) noexcept {
|
||||||
|
b0 = (color & 0x80u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b1 = (color & 0x40u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b2 = (color & 0x20u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b3 = (color & 0x10u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b4 = (color & 0x08u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b5 = (color & 0x04u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b6 = (color & 0x02u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
b7 = (color & 0x01u) ? BIT_HIGH : BIT_LOW;
|
||||||
|
}
|
||||||
|
unsigned OneColor::to_string(char * ptr, const int m) {
|
||||||
|
auto f = [=](const uint32_t x, char * buf) -> unsigned {
|
||||||
|
unsigned i = 0;
|
||||||
|
for (int k=0; k<m; k++) {
|
||||||
|
buf [i++] = x & (1u << k) ? '1' : '0';
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
};
|
||||||
|
unsigned n = 0;
|
||||||
|
n += f (b0, ptr + n);
|
||||||
|
n += f (b1, ptr + n);
|
||||||
|
n += f (b2, ptr + n);
|
||||||
|
n += f (b3, ptr + n);
|
||||||
|
n += f (b4, ptr + n);
|
||||||
|
n += f (b5, ptr + n);
|
||||||
|
n += f (b6, ptr + n);
|
||||||
|
n += f (b7, ptr + n);
|
||||||
|
ptr [n] = '\0';
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ws2812b::Send (uint8_t * const ptr, const unsigned int len) {
|
||||||
|
uint32_t cmd;
|
||||||
|
while (ring.Read(cmd)) {
|
||||||
|
const Entry ne (cmd);
|
||||||
|
if (ne.ws.order < NUMLEDS) {
|
||||||
|
Color * cptr = reinterpret_cast<Color*>(ptr);
|
||||||
|
Color & c = cptr [ne.ws.order];
|
||||||
|
const OneColor cb (ne.ws.b), cg (ne.ws.g), cr (ne.ws.r);
|
||||||
|
c.b = cb; c.g = cg; c.r = cr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
55
V203F6P6/disco/ws2812b.h
Normal file
55
V203F6P6/disco/ws2812b.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#ifndef WS2812B_H
|
||||||
|
#define WS2812B_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "oneway.h"
|
||||||
|
#include "fifo.h"
|
||||||
|
static constexpr int BWW = 3;
|
||||||
|
struct OneColor {
|
||||||
|
uint32_t b0 : BWW;
|
||||||
|
uint32_t b1 : BWW;
|
||||||
|
uint32_t b2 : BWW;
|
||||||
|
uint32_t b3 : BWW;
|
||||||
|
uint32_t b4 : BWW;
|
||||||
|
uint32_t b5 : BWW;
|
||||||
|
uint32_t b6 : BWW;
|
||||||
|
uint32_t b7 : BWW;
|
||||||
|
explicit OneColor (const uint8_t c) noexcept;
|
||||||
|
unsigned to_string (char * ptr, const int m = BWW);
|
||||||
|
}__attribute__((packed));
|
||||||
|
struct Color {
|
||||||
|
OneColor g,r,b;
|
||||||
|
}__attribute__((packed));
|
||||||
|
union Entry {
|
||||||
|
struct _ws {
|
||||||
|
uint8_t g,r,b;
|
||||||
|
uint8_t order; // určuje pořadí LED
|
||||||
|
} ws;
|
||||||
|
uint32_t number;
|
||||||
|
explicit Entry (const uint32_t e) noexcept { number = e; }
|
||||||
|
};
|
||||||
|
/*************************************************************************************/
|
||||||
|
static constexpr unsigned NUMLEDS = 4u;
|
||||||
|
/*************************************************************************************/
|
||||||
|
/** @class ws2812b
|
||||||
|
* @brief Driver pro WS2812B
|
||||||
|
* On ten driver je trochu divný. Běží nad SPI s použitím pinu MOSI, 1 bit barvy
|
||||||
|
* jsou 3 bity SPI. Funguje to tak, že SPI běží přes DMA v cirkulárním módu, tedy
|
||||||
|
* pořád, v 1. části dělá "reset", tedy časování rámce (vysílá nuly).
|
||||||
|
* V 2. části si vybere z fronty ring data a uloží je do buferu ve správném
|
||||||
|
* tvaru. Využívá se toho, že přerušení přijde až na konci a naplnění daty
|
||||||
|
* netrvá dlouho, takže se přepisuje jen část, která se právě nevysílá.
|
||||||
|
* Fronta byla použita (celkem zbytečně) protože zatím netuším jaká data posílat.
|
||||||
|
* NOTE
|
||||||
|
* Protože WS2812B je 5V záležitost a procesor je napájen jen 3.3V, funguje to
|
||||||
|
* na hraně a je dobré zapojit mezi MOSI a +5V rezistor 1k. Je to pak stabilnější.
|
||||||
|
* */
|
||||||
|
class ws2812b : public OneWay<uint8_t> {
|
||||||
|
FIFO<uint32_t,FIFOLEN> & ring;
|
||||||
|
public:
|
||||||
|
explicit ws2812b (FIFO<uint32_t,FIFOLEN> & r) noexcept : OneWay<uint8_t> (), ring(r) {}
|
||||||
|
unsigned int Send (uint8_t * const ptr, const unsigned int len) override;
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WS2812B_H
|
Loading…
Add table
Reference in a new issue