add sound to hello
This commit is contained in:
parent
7c52a1ad9e
commit
de48701e8c
10 changed files with 238 additions and 41 deletions
|
@ -1,8 +1,6 @@
|
||||||
# ch32v003
|
# ch32v003
|
||||||
TARGET?= ch32v003
|
TARGET?= ch32v003
|
||||||
TOOL ?= gcc
|
TOOL ?= gcc
|
||||||
#TOOL ?= clang
|
|
||||||
|
|
||||||
|
|
||||||
PRJ = example
|
PRJ = example
|
||||||
|
|
||||||
|
@ -14,11 +12,11 @@ LDLIBS =
|
||||||
BFLAGS = --strip-unneeded
|
BFLAGS = --strip-unneeded
|
||||||
|
|
||||||
CFLAGS = -MMD -Wall -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
CFLAGS = -MMD -Wall -ggdb -fno-exceptions -ffunction-sections -fdata-sections
|
||||||
CFLAGS+= -I. -I./$(TARGET) -I/usr/include/newlib
|
CFLAGS+= -I. -I./$(TARGET) -I./common -I/usr/include/newlib
|
||||||
DEL = rm -f
|
DEL = rm -f
|
||||||
|
|
||||||
# zdrojaky
|
# zdrojaky
|
||||||
OBJS = main.o morse.o
|
OBJS = main.o morse.o generator.o pwmclass.o sin.o
|
||||||
|
|
||||||
include $(TARGET)/$(TOOL).mk
|
include $(TARGET)/$(TOOL).mk
|
||||||
BOBJS = $(addprefix $(BLD),$(OBJS))
|
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||||
|
@ -36,6 +34,7 @@ $(PRJ).elf: $(BOBJS)
|
||||||
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
|
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
|
||||||
-@echo "OK."
|
-@echo "OK."
|
||||||
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
|
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
|
||||||
|
$(COPY) $(BFLAGS) -O ihex $(PRJ).elf $(PRJ).hex
|
||||||
# preloz co je potreba
|
# preloz co je potreba
|
||||||
$(BLD)%.o: %.c
|
$(BLD)%.o: %.c
|
||||||
-@echo [CC $(TOOL),$(TARGET)] $@
|
-@echo [CC $(TOOL),$(TARGET)] $@
|
||||||
|
@ -45,9 +44,11 @@ $(BLD)%.o: %.cpp
|
||||||
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
|
||||||
$(BLD):
|
$(BLD):
|
||||||
mkdir $(BLD)
|
mkdir $(BLD)
|
||||||
|
sin.c: sin.py
|
||||||
|
./sin.py
|
||||||
flash: $(PRJ).elf
|
flash: $(PRJ).elf
|
||||||
minichlink -w $(PRJ).bin flash -b
|
minichlink -w $(PRJ).bin flash -b
|
||||||
# vycisti
|
# vycisti
|
||||||
clean:
|
clean:
|
||||||
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~
|
$(DEL) $(BLD)* *.lst *.bin *.hex *.elf *.map sin.c *~
|
||||||
.PHONY: all clean
|
.PHONY: all clean
|
||||||
|
|
1
hello/common
Symbolic link
1
hello/common
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../common/
|
14
hello/generator.cpp
Normal file
14
hello/generator.cpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#include "generator.h"
|
||||||
|
|
||||||
|
extern "C" const uint16_t sin_tab[0x100];
|
||||||
|
|
||||||
|
uint16_t Generator::step() {
|
||||||
|
const uint16_t v = sin_tab [base >> 24];
|
||||||
|
base += incr;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
unsigned int Generator::Send(uint16_t * const ptr, const unsigned int len) {
|
||||||
|
for (unsigned n=0u; n<len; n++) ptr [n] = step();
|
||||||
|
if (ms_count) ms_count -= 1u; // průchod zde je za 1 ms přesně
|
||||||
|
return len;
|
||||||
|
}
|
24
hello/generator.h
Normal file
24
hello/generator.h
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#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 {
|
||||||
|
const unsigned freq;
|
||||||
|
unsigned base, incr;
|
||||||
|
volatile unsigned ms_count;
|
||||||
|
public:
|
||||||
|
explicit Generator (const unsigned f) noexcept : OneWay(),
|
||||||
|
freq (f), base(0u), incr (0u), ms_count (0u) { }
|
||||||
|
unsigned Send (uint16_t * const ptr, const unsigned len) override;
|
||||||
|
|
||||||
|
void delay (const unsigned ms) {
|
||||||
|
ms_count = ms;
|
||||||
|
while (ms_count);// asm volatile ("wfi"); // ???
|
||||||
|
}
|
||||||
|
void on () volatile { incr = freq; }
|
||||||
|
void off () volatile { base = 0u; incr = 0u; }
|
||||||
|
protected:
|
||||||
|
uint16_t step ();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GENERATOR_H
|
|
@ -1,9 +1,13 @@
|
||||||
/* SIMPLE EXAMPLE: LED blinking */
|
/* SIMPLE EXAMPLE: LED blinking */
|
||||||
|
/* Když už mám PWM hotové, tak to může
|
||||||
|
* na pinech PD0 a PD2 pípat. Frekvence
|
||||||
|
* je 1kHz - čistý sinus.
|
||||||
|
* */
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
//////////////////////////////////////
|
//////////////////////////////////////
|
||||||
|
static GpioClass led (GPIOD, 4);
|
||||||
|
static Morse morse (led, 100u);
|
||||||
int main () {
|
int main () {
|
||||||
GpioClass led (GPIOD, 4);
|
|
||||||
Morse morse (led, 333);
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
morse << "hello world";
|
morse << "hello world";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,21 @@
|
||||||
#include "system.h"
|
|
||||||
#include "morse.h"
|
#include "morse.h"
|
||||||
static constexpr unsigned slen (const char * const str) {
|
// Spočteme číslo (pro 1kHz) jako (1000 << 32) / 24000 (24 kHz je samplerate).
|
||||||
unsigned n = 0;
|
static constexpr unsigned F0 = (1000ull << 32) / 24000u;
|
||||||
while (str[n]) n++;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
static const unsigned char compressed_table [] = {
|
static const unsigned char compressed_table [] = {
|
||||||
0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xed, 0x00, 0xaa, 0xf3, 0xe1, 0xea, 0xa9,
|
0x00, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, 0xde, 0xad, 0xed, 0x00, 0xaa, 0xf3, 0xe1, 0xea, 0xa9,
|
||||||
0xbf, 0xbe, 0xbc, 0xb8, 0xb0, 0xa0, 0xa1, 0xa3, 0xa7, 0xaf, 0xc7, 0xd5, 0x00, 0xb1, 0x00, 0xcc,
|
0xbf, 0xbe, 0xbc, 0xb8, 0xb0, 0xa0, 0xa1, 0xa3, 0xa7, 0xaf, 0xc7, 0xd5, 0x00, 0xb1, 0x00, 0xcc,
|
||||||
0xd6, 0x42, 0x81, 0x85, 0x61, 0x20, 0x84, 0x63, 0x80, 0x40, 0x8e, 0x65, 0x82, 0x43, 0x41, 0x67,
|
0xd6, 0x42, 0x81, 0x85, 0x61, 0x20, 0x84, 0x63, 0x80, 0x40, 0x8e, 0x65, 0x82, 0x43, 0x41, 0x67,
|
||||||
0x86, 0x8b, 0x62, 0x60, 0x21, 0x64, 0x88, 0x66, 0x89, 0x8d, 0x83, 0x00, 0x00, 0x00, 0x00, 0xec,
|
0x86, 0x8b, 0x62, 0x60, 0x21, 0x64, 0x88, 0x66, 0x89, 0x8d, 0x83, 0x00, 0x00, 0x00, 0x00, 0xec,
|
||||||
};
|
};
|
||||||
|
Morse::Morse(const GpioClass & pin, const unsigned int ms) noexcept : unit (ms), led (pin),
|
||||||
extern "C" void SysTick_Handler() __attribute__((interrupt));
|
gen (F0), pwm () {
|
||||||
static volatile unsigned count = 0u;
|
pwm.attach(gen);
|
||||||
void SysTick_Handler () {
|
|
||||||
SysTick.SR = 0u;
|
|
||||||
if (count) count -= 1;
|
|
||||||
}
|
}
|
||||||
static void delay (const unsigned ms) {
|
const Morse & Morse::operator<< (const char * text) {
|
||||||
count = ms;
|
for (unsigned n=0; ; n++) {
|
||||||
while (count);
|
|
||||||
}
|
|
||||||
Morse::Morse(const GpioClass & pin, const unsigned int ms) noexcept : unit (ms), led (pin) {
|
|
||||||
SysTick.Config (48000u);
|
|
||||||
}
|
|
||||||
const Morse & Morse::operator<< (const char * text) const {
|
|
||||||
const unsigned len = slen (text);
|
|
||||||
for (unsigned n=0; n<len; n++) {
|
|
||||||
const char c = text [n];
|
const char c = text [n];
|
||||||
|
if (c == '\0') break;
|
||||||
morse_byte mb;
|
morse_byte mb;
|
||||||
if (c < '\x20') {
|
if (c < '\x20') {
|
||||||
} else if (c < '`') {
|
} else if (c < '`') {
|
||||||
|
@ -42,7 +29,7 @@ const Morse & Morse::operator<< (const char * text) const {
|
||||||
}
|
}
|
||||||
out (mb);
|
out (mb);
|
||||||
}
|
}
|
||||||
delay (10 * unit);
|
gen.delay (10 * unit);
|
||||||
return * this;
|
return * this;
|
||||||
}
|
}
|
||||||
/* . => 1 x unit
|
/* . => 1 x unit
|
||||||
|
@ -51,7 +38,7 @@ const Morse & Morse::operator<< (const char * text) const {
|
||||||
* mezera mezi znaky => 3 x unit
|
* mezera mezi znaky => 3 x unit
|
||||||
* mezera mezi slovy => 7 x unit
|
* mezera mezi slovy => 7 x unit
|
||||||
* */
|
* */
|
||||||
void Morse::out (const morse_byte mb) const {
|
void Morse::out (const morse_byte mb) {
|
||||||
/* Finta je v tom, že i když se pole mlen a bits překrývají,
|
/* Finta je v tom, že i když se pole mlen a bits překrývají,
|
||||||
* nevadí to - maximální délka je 6, takže v nejnižším bitu
|
* nevadí to - maximální délka je 6, takže v nejnižším bitu
|
||||||
* mlen může být obsažen 1 bit 6.bitového znaku.
|
* mlen může být obsažen 1 bit 6.bitového znaku.
|
||||||
|
@ -59,15 +46,15 @@ void Morse::out (const morse_byte mb) const {
|
||||||
* to do bitových posunů nebudu, i když by to bylo čistší.
|
* to do bitových posunů nebudu, i když by to bylo čistší.
|
||||||
* */
|
* */
|
||||||
const unsigned len = mb.mlen > 6u ? 6u : mb.mlen;
|
const unsigned len = mb.mlen > 6u ? 6u : mb.mlen;
|
||||||
if (!len) { delay (4 * unit); return; }
|
if (!len) { gen.delay (4 * unit); return; }
|
||||||
for (unsigned n=0; n<len; n++) {
|
for (unsigned n=0; n<len; n++) {
|
||||||
led << false;
|
off ();
|
||||||
delay (unit);
|
gen.delay (unit);
|
||||||
const unsigned v = (1u << n) & mb.bits;
|
const unsigned v = (1u << n) & mb.bits;
|
||||||
const unsigned d = v ? 3u : 1u;
|
const unsigned d = v ? 3u : 1u;
|
||||||
led << true;
|
on ();
|
||||||
delay (d * unit);
|
gen.delay (d * unit);
|
||||||
led << false;
|
off ();
|
||||||
}
|
}
|
||||||
delay (3 * unit);
|
gen.delay (3 * unit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef MORSE_H
|
#ifndef MORSE_H
|
||||||
#define MORSE_H
|
#define MORSE_H
|
||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
|
#include "generator.h"
|
||||||
|
#include "pwmclass.h"
|
||||||
union morse_byte {
|
union morse_byte {
|
||||||
struct {
|
struct {
|
||||||
unsigned char bits : 5;
|
unsigned char bits : 5;
|
||||||
|
@ -13,12 +15,22 @@ union morse_byte {
|
||||||
class Morse {
|
class Morse {
|
||||||
const unsigned unit;
|
const unsigned unit;
|
||||||
const GpioClass & led;
|
const GpioClass & led;
|
||||||
|
Generator gen;
|
||||||
|
PwmClass pwm;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Morse (const GpioClass & pin, const unsigned ms = 100) noexcept;
|
explicit Morse (const GpioClass & pin, const unsigned ms = 100u) noexcept;
|
||||||
const Morse & operator<< (const char * text) const;
|
const Morse & operator<< (const char * text);
|
||||||
protected:
|
protected:
|
||||||
void out (const morse_byte mb) const;
|
void out (const morse_byte mb);
|
||||||
|
void on () {
|
||||||
|
led << false; // LED je připojena proti VCC, zde se tedy rozsvítí
|
||||||
|
gen.on ();
|
||||||
|
}
|
||||||
|
void off () {
|
||||||
|
led << true;
|
||||||
|
gen.off();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MORSE_H
|
#endif // MORSE_H
|
||||||
|
|
104
hello/pwmclass.cpp
Normal file
104
hello/pwmclass.cpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
#include "system.h"
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* initialize TIM1 for PWM
|
||||||
|
*/
|
||||||
|
static inline void tim1pwm_init () noexcept {
|
||||||
|
// Enable GPIOD and TIM1
|
||||||
|
RCC.APB2PCENR.modify([] (RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||||
|
r.B.IOPDEN = SET;
|
||||||
|
r.B.TIM1EN = SET;
|
||||||
|
return r.R;
|
||||||
|
});
|
||||||
|
// PD0 is T1CH1N, PD2 is T1CH1, 10MHz Output alt func, push-pull
|
||||||
|
GPIOD.CFGLR.modify([](GPIOA_Type::CFGLR_DEF & r) -> auto {
|
||||||
|
r.B.CNF0 = 2u;
|
||||||
|
r.B.MODE0 = 1u;
|
||||||
|
r.B.CNF2 = 2u;
|
||||||
|
r.B.MODE2 = 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 : pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
|
||||||
|
pInstance = this;
|
||||||
|
tim1pwm_init ();
|
||||||
|
dma1ch5_init (buffer);
|
||||||
|
}
|
23
hello/pwmclass.h
Normal file
23
hello/pwmclass.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef PWMCLASS_H
|
||||||
|
#define PWMCLASS_H
|
||||||
|
#include "oneway.h"
|
||||||
|
static constexpr unsigned HALF_LEN = 24u;
|
||||||
|
static constexpr unsigned FULL_LEN = 2u * HALF_LEN;
|
||||||
|
static constexpr unsigned MAXPWM = 2000u;
|
||||||
|
/* Používá TIM1, PWM kanál 1, DMA1 kanál 5, přerušení DMA1_Channel5_IRQHandler */
|
||||||
|
class PwmClass {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // PWMCLASS_H
|
27
hello/sin.py
Executable file
27
hello/sin.py
Executable file
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import math
|
||||||
|
|
||||||
|
header = '''/* Generated file */
|
||||||
|
#include <stdint.h>
|
||||||
|
const uint16_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 (1000.0 * (1.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