add pwm
This commit is contained in:
parent
8af99b17d4
commit
2a28e9cfb6
12 changed files with 438 additions and 0 deletions
74
V203/common/baselayer.h
Normal file
74
V203/common/baselayer.h
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
#ifndef BASELAYER_H
|
||||||
|
#define BASELAYER_H
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __arm__
|
||||||
|
#define debug(...)
|
||||||
|
#else // ARCH_CM0
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define debug printf
|
||||||
|
#else // DEBUG
|
||||||
|
#define debug(...)
|
||||||
|
#endif // DEBUG
|
||||||
|
#endif // ARCH_CM0
|
||||||
|
/** @brief Bázová třída pro stack trochu obecnějšího komunikačního protokolu.
|
||||||
|
*
|
||||||
|
* @class BaseLayer
|
||||||
|
* @brief Od této třídy budeme dále odvozovat ostatní.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class BaseLayer {
|
||||||
|
public:
|
||||||
|
/** Konstruktor
|
||||||
|
*/
|
||||||
|
explicit constexpr BaseLayer () noexcept : pUp(nullptr), pDown(nullptr) {};
|
||||||
|
/** Virtuální metoda, přesouvající data směrem nahoru, pokud s nimi nechceme dělat něco jiného.
|
||||||
|
@param data ukazatel na pole dat
|
||||||
|
@param len delka dat v bytech
|
||||||
|
@return počet přenesených bytů
|
||||||
|
*/
|
||||||
|
virtual uint32_t Up (const char * data, const uint32_t len) {
|
||||||
|
if (pUp) return pUp->Up (data, len);
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
/** Virtuální metoda, přesouvající data směrem dolů, pokud s nimi nechceme dělat něco jiného.
|
||||||
|
@param data ukazatel na pole dat
|
||||||
|
@param len delka dat v bytech
|
||||||
|
@return počet přenesených bytů
|
||||||
|
*/
|
||||||
|
virtual uint32_t Down (const char * data, const uint32_t len) {
|
||||||
|
if (pDown) return pDown->Down (data, len);
|
||||||
|
return len;
|
||||||
|
};
|
||||||
|
/** @brief Zřetězení stacku.
|
||||||
|
* Tohle je vlastně to nejdůležitější. V čistém C by se musely
|
||||||
|
* nastavovat ukazatele na callback funkce, tady je to čitší - pro uživatele neviditelné,
|
||||||
|
* ale je to to samé.
|
||||||
|
@param bl Třída, ležící pod, spodní
|
||||||
|
@return Odkaz na tuto třídu (aby se to dalo řetězit)
|
||||||
|
*/
|
||||||
|
virtual BaseLayer & operator += (BaseLayer & bl) {
|
||||||
|
bl.setUp (this); // ta spodní bude volat při Up tuto třídu
|
||||||
|
setDown (& bl); // a tato třída bude volat při Down tu spodní
|
||||||
|
return * this;
|
||||||
|
};
|
||||||
|
/** Getter pro pDown
|
||||||
|
@return pDown
|
||||||
|
*/
|
||||||
|
BaseLayer * getDown (void) const { return pDown; };
|
||||||
|
protected:
|
||||||
|
/** Lokální setter pro pUp
|
||||||
|
@param p Co budeme do pUp dávat
|
||||||
|
*/
|
||||||
|
void setUp (BaseLayer * p) { pUp = p; };
|
||||||
|
/** Lokální setter pro pDown
|
||||||
|
@param p Co budeme do pDown dávat
|
||||||
|
*/
|
||||||
|
void setDown (BaseLayer * p) { pDown = p; };
|
||||||
|
private:
|
||||||
|
// Ono to je vlastně oboustranně vázaný spojový seznam.
|
||||||
|
BaseLayer * pUp; //!< Ukazatel na třídu, která bude dále volat Up
|
||||||
|
BaseLayer * pDown; //!< Ukazatel na třídu, která bude dále volat Down
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // BASELAYER_H
|
73
V203/common/fifo.h
Normal file
73
V203/common/fifo.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#ifndef FIFO_H
|
||||||
|
#define FIFO_H
|
||||||
|
/** Typ dbus_w_t je podobně definován jako sig_atomic_t v hlavičce signal.h.
|
||||||
|
* Je to prostě největší typ, ke kterému je "atomický" přístup. V GCC je definováno
|
||||||
|
* __SIG_ATOMIC_TYPE__, šlo by použít, ale je znaménkový.
|
||||||
|
* */
|
||||||
|
#ifdef __SIG_ATOMIC_TYPE__
|
||||||
|
typedef unsigned __SIG_ATOMIC_TYPE__ dbus_w_t;
|
||||||
|
#else
|
||||||
|
typedef unsigned int dbus_w_t; // pro AVR by to měl být uint8_t (šířka datové sběrnice)
|
||||||
|
#endif //__SIG_ATOMIC_TYPE__
|
||||||
|
/// Tahle podivná rekurzívní formule je použita pro validaci délky bufferu.
|
||||||
|
static constexpr bool isValidM (const int N, const dbus_w_t M) {
|
||||||
|
// constexpr má raději rekurzi než cyklus (c++11)
|
||||||
|
return (N > 12) ? false : (((1u << N) == M) ? true : isValidM (N+1, M));
|
||||||
|
}
|
||||||
|
/** @class FIFO
|
||||||
|
* @brief Jednoduchá fronta (kruhový buffer).
|
||||||
|
*
|
||||||
|
* V tomto přikladu je vidět, že synchronizace mezi přerušením a hlavní smyčkou programu
|
||||||
|
* může být tak jednoduchá, že je v podstatě neviditelná. Využívá se toho, že pokud
|
||||||
|
* do kruhového buferu zapisujeme jen z jednoho bodu a čteme také jen z jednoho bodu
|
||||||
|
* (vlákna), zápis probíhá nezávisle pomocí indexu m_head a čtení pomocí m_tail.
|
||||||
|
* Délka dat je dána rozdílem tt. indexů, pokud v průběhu výpočtu délky dojde k přerušení,
|
||||||
|
* v zásadě se nic špatného neděje, maximálně je délka určena špatně a to tak,
|
||||||
|
* že zápis nebo čtení je nutné opakovat. Důležité je, že po výpočtu se nová délka zapíše
|
||||||
|
* do paměti "atomicky". Takže např. pro 8-bit procesor musí být indexy jen 8-bitové.
|
||||||
|
* To není moc velké omezení, protože tyto procesory obvykle mají dost malou RAM, takže
|
||||||
|
* velikost bufferu stejně nebývá být větší než nějakých 64 položek.
|
||||||
|
* Opět nijak nevadí že přijde přerušení při zápisu nebo čtení položky - to se provádí
|
||||||
|
* dříve než změna indexu, zápis a čtení je vždy na jiném místě RAM. Celé je to uděláno
|
||||||
|
* jako šablona, takže je možné řadit do fronty i složitější věci než je pouhý byte.
|
||||||
|
* Druhým parametrem šablony je délka bufferu (aby to šlo konstruovat jako statický objekt),
|
||||||
|
* musí to být mocnina dvou v rozsahu 8 až 4096, default je 64. Mocnina 2 je zvolena proto,
|
||||||
|
* aby se místo zbytku po dělení mohl použít jen bitový and, což je rychlejší.
|
||||||
|
* */
|
||||||
|
template<typename T, const dbus_w_t M = 64> class FIFO {
|
||||||
|
T m_data [M];
|
||||||
|
volatile dbus_w_t m_head; //!< index pro zápis (hlava)
|
||||||
|
volatile dbus_w_t m_tail; //!< index pro čtení (ocas)
|
||||||
|
/// vrací skutečnou délku dostupných dat
|
||||||
|
constexpr dbus_w_t lenght () const { return (M + m_head - m_tail) & (M - 1); };
|
||||||
|
/// zvětší a saturuje index, takže se tento motá v kruhu @param n index
|
||||||
|
void sat_inc (volatile dbus_w_t & n) const { n = (n + 1) & (M - 1); };
|
||||||
|
public:
|
||||||
|
/// Konstruktor
|
||||||
|
explicit constexpr FIFO<T,M> () noexcept {
|
||||||
|
// pro 8-bit architekturu může být byte jako index poměrně malý
|
||||||
|
static_assert (1ul << (8 * sizeof(dbus_w_t) - 1) >= M, "atomic type too small");
|
||||||
|
// a omezíme pro jistotu i delku buferu na nějakou rozumnou delku
|
||||||
|
static_assert (isValidM (3, M), "M must be power of two in range <8,4096> or <8,128> for 8-bit data bus (AVR)");
|
||||||
|
m_head = 0;
|
||||||
|
m_tail = 0;
|
||||||
|
}
|
||||||
|
/// Čtení položky
|
||||||
|
/// @return true, pokud se úspěšně provede
|
||||||
|
const bool Read (T & c) {
|
||||||
|
if (lenght() == 0) return false;
|
||||||
|
c = m_data [m_tail];
|
||||||
|
sat_inc (m_tail);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// Zápis položky
|
||||||
|
/// @return true, pokud se úspěšně provede
|
||||||
|
const bool Write (const T & c) {
|
||||||
|
if (lenght() >= (M - 1)) return false;
|
||||||
|
m_data [m_head] = c;
|
||||||
|
sat_inc (m_head);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // FIFO_H
|
10
V203/common/oneway.h
Normal file
10
V203/common/oneway.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef ONEWAY_H
|
||||||
|
#define ONEWAY_H
|
||||||
|
#include <stdint.h>
|
||||||
|
/* C++ interface (jako callback v C) */
|
||||||
|
class OneWay {
|
||||||
|
public:
|
||||||
|
virtual unsigned Send (uint16_t * const ptr, const unsigned len) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ONEWAY_H
|
55
V203/pwm/Makefile
Normal file
55
V203/pwm/Makefile
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
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 pwmclass.o generator.o sin.o
|
||||||
|
#OBJS +=
|
||||||
|
|
||||||
|
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)
|
||||||
|
sin.c: sin.py
|
||||||
|
./sin.py
|
||||||
|
flash: $(PRJ).elf
|
||||||
|
minichlink -w $(PRJ).bin flash -b
|
||||||
|
# vycisti
|
||||||
|
clean:
|
||||||
|
$(DEL) $(BLD)* *.lst *.bin *.elf *.map sin.c *~
|
||||||
|
.PHONY: all clean flash run
|
1
V203/pwm/ch32v203
Symbolic link
1
V203/pwm/ch32v203
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../ch32v203/
|
1
V203/pwm/common
Symbolic link
1
V203/pwm/common
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../common/
|
13
V203/pwm/generator.cpp
Normal file
13
V203/pwm/generator.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include "generator.h"
|
||||||
|
|
||||||
|
extern "C" const int16_t sin_tab[0x100];
|
||||||
|
|
||||||
|
int16_t Generator::step() {
|
||||||
|
const int16_t v = sin_tab [base >> 24];
|
||||||
|
base += freq;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
unsigned int Generator::Send(uint16_t * const ptr, const unsigned int len) {
|
||||||
|
for (unsigned n=0u; n<len; n++) ptr [n] = 3000u + step();
|
||||||
|
return len;
|
||||||
|
}
|
14
V203/pwm/generator.h
Normal file
14
V203/pwm/generator.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef GENERATOR_H
|
||||||
|
#define GENERATOR_H
|
||||||
|
#include "oneway.h"
|
||||||
|
/* Něco jako DDS, přesná frekvence není řešena (závisí na TIM1). */
|
||||||
|
class Generator : public OneWay {
|
||||||
|
unsigned base, freq;
|
||||||
|
public:
|
||||||
|
explicit constexpr Generator (const unsigned f) noexcept : OneWay(), base(0u), freq (f) {};
|
||||||
|
unsigned Send (uint16_t * const ptr, const unsigned len) override;
|
||||||
|
protected:
|
||||||
|
int16_t step ();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GENERATOR_H
|
25
V203/pwm/main.cpp
Normal file
25
V203/pwm/main.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include "pwmclass.h"
|
||||||
|
#include "generator.h"
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
/* Demonstrace PWM s použitím DMA
|
||||||
|
* Generátor je sinus, data se tvoří
|
||||||
|
* v přerušení. Parametry se musí nastavit
|
||||||
|
* ve zdrojácích, je to jen DEMO.
|
||||||
|
*
|
||||||
|
* Proti V003 se to zase tak moc neliší.
|
||||||
|
* Pouze piny jsou jinak (PA7,PA8) a rozlišení
|
||||||
|
* PWM může být 3x lepší kvůli rychlejším hodinám.
|
||||||
|
* */
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
static constexpr double fs = SYSTEM_CORE_CLOCK / MAXPWM;
|
||||||
|
static constexpr unsigned long operator ""_Hz (const long double a) {
|
||||||
|
return a * double (1ull << 32) / fs; // fs je opět 24kHz
|
||||||
|
}
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
int main () {
|
||||||
|
PwmClass pwm; // takto na zásobníku
|
||||||
|
Generator gen (440.0_Hz); // to na V003 nefungovalo
|
||||||
|
pwm.attach(gen); // ale i tak nedoporučuji !!!
|
||||||
|
for (;;);
|
||||||
|
return 0;
|
||||||
|
}
|
113
V203/pwm/pwmclass.cpp
Normal file
113
V203/pwm/pwmclass.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include "pwmclass.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
|
||||||
|
static PwmClass * pInstance = nullptr;
|
||||||
|
extern "C" void DMA1_Channel5_IRQHandler( void ) __attribute__((interrupt));
|
||||||
|
void DMA1_Channel5_IRQHandler( void ) {
|
||||||
|
DMA1_Type::INTFR_DEF state (DMA1.INTFR);
|
||||||
|
DMA1.INTFCR.R = state.R; // clear all
|
||||||
|
if (!pInstance) return;
|
||||||
|
if (state.B.HTIF5 != RESET) pInstance->send(false);
|
||||||
|
else if (state.B.TCIF5 != RESET) pInstance->send(true);
|
||||||
|
|
||||||
|
pInstance->signalize(); // zbytečný efekt
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize TIM1 for PWM
|
||||||
|
*/
|
||||||
|
static inline void tim1pwm_init () noexcept {
|
||||||
|
// Enable GPIOA and TIM1
|
||||||
|
RCC.APB2PCENR.modify([] (RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||||
|
r.B.IOPAEN = SET;
|
||||||
|
r.B.TIM1EN = SET;
|
||||||
|
r.B.AFIOEN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
AFIO.PCFR.modify([](AFIO_Type::PCFR_DEF & r) -> auto {
|
||||||
|
r.B.TIM1RM = 1u;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// PA7 is T1CH1N, PA8 is T1CH1, 10MHz Output alt func, push-pull
|
||||||
|
GPIOA.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||||
|
r.B.CNF7 = 2u;
|
||||||
|
r.B.MODE7 = 1u;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
GPIOA.CFGHR.modify([](GPIOA_Type::CFGHR_DEF & r) -> auto {
|
||||||
|
r.B.CNF8 = 2u;
|
||||||
|
r.B.MODE8 = 1u;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// Reset TIM1 to init all regs
|
||||||
|
RCC.APB2PRSTR.B.TIM1RST = SET;
|
||||||
|
RCC.APB2PRSTR.B.TIM1RST = RESET;
|
||||||
|
// CTLR1: default is up, events generated, edge align
|
||||||
|
// SMCFGR: default clk input is CK_INT
|
||||||
|
// Prescaler
|
||||||
|
TIM1.PSC.R = 0u;
|
||||||
|
// Auto Reload - sets period
|
||||||
|
TIM1.ATRLR.R = MAXPWM - 1;
|
||||||
|
|
||||||
|
TIM1.CCER.modify([](TIM1_Type::CCER_DEF & r) -> auto {
|
||||||
|
// Enable CH1N, CH1 output, positive pol
|
||||||
|
r.B.CC1NE = SET;
|
||||||
|
r.B.CC1E = SET;
|
||||||
|
/*
|
||||||
|
r.B.CC1NP = SET; // active Low
|
||||||
|
r.B.CC1P = SET;
|
||||||
|
*/
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// CH1 Mode is output, PWM1 (CC1S = 00, OC1M = 110)
|
||||||
|
TIM1.CHCTLR1_Output.modify([](TIM1_Type::CHCTLR1_Output_DEF & r) -> auto {
|
||||||
|
r.B.OC1M = 0x6u;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// Enable TIM1 outputs
|
||||||
|
TIM1.BDTR.modify([](TIM1_Type::BDTR_DEF & r) -> auto {
|
||||||
|
r.B.MOE = SET;
|
||||||
|
r.B.DTG = 48u; // Dead time 1us
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reload immediately + Trigger DMA
|
||||||
|
TIM1.SWEVGR.B.UG = SET;
|
||||||
|
TIM1.DMAINTENR.B.UDE = SET;
|
||||||
|
// Enable TIM1
|
||||||
|
TIM1.CTLR1.B.CEN = SET;
|
||||||
|
}
|
||||||
|
typedef __SIZE_TYPE__ size_t;
|
||||||
|
static inline void dma1ch5_init (void * ptr) noexcept {
|
||||||
|
// Enable DMA
|
||||||
|
RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
|
||||||
|
r.B.SRAMEN = SET;
|
||||||
|
r.B.DMA1EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// DMA5 can be configured to attach to T1UP
|
||||||
|
// The system can only DMA out at ~2.2MSPS. 2MHz is stable.
|
||||||
|
DMA1.CNTR5 .R = FULL_LEN;
|
||||||
|
DMA1.MADDR5.R = reinterpret_cast<size_t>(ptr);
|
||||||
|
DMA1.PADDR5.R = reinterpret_cast<size_t>(& TIM1.CH1CVR);
|
||||||
|
NVIC.EnableIRQ (DMA1_Channel5_IRQn);
|
||||||
|
DMA1.CFGR5.modify([](DMA1_Type::CFGR5_DEF & r) -> auto {
|
||||||
|
r.B.DIR = SET; // MEM2PERIPHERAL
|
||||||
|
r.B.PL = 2u; // High priority.
|
||||||
|
r.B.PSIZE = 1u; // 16-bit peripheral
|
||||||
|
r.B.MSIZE = 1u; // 16-bit memory
|
||||||
|
r.B.MINC = SET; // Increase memory.
|
||||||
|
r.B.CIRC = SET; // Circular mode.
|
||||||
|
r.B.HTIE = SET; // Half-trigger
|
||||||
|
r.B.TCIE = SET; // Whole-trigger
|
||||||
|
// Enable DMA1 ch5
|
||||||
|
r.B.EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
PwmClass::PwmClass() noexcept : led(GPIOA, 0), count(0u), pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
|
||||||
|
pInstance = this;
|
||||||
|
tim1pwm_init ();
|
||||||
|
dma1ch5_init (buffer);
|
||||||
|
}
|
32
V203/pwm/pwmclass.h
Normal file
32
V203/pwm/pwmclass.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#ifndef PWMCLASS_H
|
||||||
|
#define PWMCLASS_H
|
||||||
|
#include "system.h"
|
||||||
|
#include "gpio.h"
|
||||||
|
#include "oneway.h"
|
||||||
|
static constexpr unsigned HALF_LEN = 160u;
|
||||||
|
static constexpr unsigned FULL_LEN = 2u * HALF_LEN;
|
||||||
|
static constexpr unsigned MAXPWM = 6000u;
|
||||||
|
/* Používá TIM1, PWM kanál 1, DMA1 kanál 5, přerušení DMA1_Channel5_IRQHandler */
|
||||||
|
class PwmClass {
|
||||||
|
GpioClass led;
|
||||||
|
unsigned count;
|
||||||
|
uint16_t * const pL;
|
||||||
|
uint16_t * const pH;
|
||||||
|
uint16_t buffer [FULL_LEN];
|
||||||
|
OneWay * src;
|
||||||
|
public:
|
||||||
|
explicit PwmClass () noexcept;
|
||||||
|
void attach (OneWay & s) { src = & s; }
|
||||||
|
void send (const bool b) {
|
||||||
|
if (!src) return;
|
||||||
|
if (b) src->Send (pH, HALF_LEN);
|
||||||
|
else src->Send (pL, HALF_LEN);
|
||||||
|
}
|
||||||
|
void signalize () {
|
||||||
|
const bool b = count & 8u;
|
||||||
|
led << b;
|
||||||
|
count += 1u;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PWMCLASS_H
|
27
V203/pwm/sin.py
Executable file
27
V203/pwm/sin.py
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
header = '''/* Generated file */
|
||||||
|
#include <stdint.h>
|
||||||
|
const int16_t sin_tab[] = {{{0:s}
|
||||||
|
}};
|
||||||
|
'''
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
s = ''
|
||||||
|
for n in range(0,256):
|
||||||
|
if (n % 16) == 0:
|
||||||
|
s += '\n '
|
||||||
|
a = float(n) * math.pi / 128.0
|
||||||
|
v = int (round (2900.0 * (math.sin (a))));
|
||||||
|
s += '{0:+6d},'.format(v)
|
||||||
|
return s
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
s = generate()
|
||||||
|
f = open ('sin.c','w')
|
||||||
|
f.write(header.format(s))
|
||||||
|
f.close()
|
||||||
|
|
Loading…
Reference in a new issue