add sinus generator
This commit is contained in:
parent
e7f6d52a86
commit
910bec6b6c
9 changed files with 88 additions and 171 deletions
|
@ -18,7 +18,7 @@ CFLAGS+= -I. -I./$(TARGET) -I/usr/include/newlib
|
|||
DEL = rm -f
|
||||
|
||||
# zdrojaky
|
||||
OBJS = main.o pwmclass.o
|
||||
OBJS = main.o pwmclass.o generator.o sin.o
|
||||
|
||||
include $(TARGET)/$(TOOL).mk
|
||||
BOBJS = $(addprefix $(BLD),$(OBJS))
|
||||
|
@ -45,9 +45,11 @@ $(BLD)%.o: %.cpp
|
|||
@$(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 *~
|
||||
$(DEL) $(BLD)* *.lst *.bin *.elf *.map sin.c *~
|
||||
.PHONY: all clean
|
||||
|
|
150
pwm/dma_gpio.c
150
pwm/dma_gpio.c
|
@ -1,150 +0,0 @@
|
|||
// DMA GPIO Output Example - this example shows
|
||||
// how you can output 8 pins all simultaneously
|
||||
// with a planned bit pattern at 4MSamples/s.
|
||||
//
|
||||
// It outputs a pattern of repeating 010101 and
|
||||
// 00000 alternating "frames".
|
||||
//
|
||||
// The interrupt fires once at the beginning and
|
||||
// once at the end.
|
||||
//
|
||||
|
||||
#include "ch32v003fun.h"
|
||||
#include <stdio.h>
|
||||
|
||||
volatile uint32_t count;
|
||||
|
||||
#define MBSAMPS 1024
|
||||
uint8_t memory_buffer[1024];
|
||||
|
||||
void DMA1_Channel2_IRQHandler( void ) __attribute__((interrupt)) __attribute__((section(".srodata")));
|
||||
void DMA1_Channel2_IRQHandler( void )
|
||||
{
|
||||
int i;
|
||||
static int frameno;
|
||||
volatile int intfr = DMA1->INTFR;
|
||||
do
|
||||
{
|
||||
DMA1->INTFCR = DMA1_IT_GL2;
|
||||
|
||||
// Gets called at the end-of-a frame.
|
||||
if( intfr & DMA1_IT_TC2 )
|
||||
{
|
||||
uint32_t fv = (frameno&1)?0:0xaa55aa55;
|
||||
uint32_t * mbb = (uint32_t*)( memory_buffer + MBSAMPS/2 );
|
||||
for( i = 0; i < MBSAMPS/8; i++ )
|
||||
{
|
||||
mbb[i] = fv; // Fill in the frame data
|
||||
}
|
||||
frameno++;
|
||||
}
|
||||
|
||||
// Gets called halfway through the frame
|
||||
if( intfr & DMA1_IT_HT2 )
|
||||
{
|
||||
uint32_t fv = (frameno&1)?0:0xaa55aa55;
|
||||
uint32_t * mbb = (uint32_t*)( memory_buffer );
|
||||
for( i = 0; i < MBSAMPS/8; i++ )
|
||||
{
|
||||
mbb[i] = fv; // Fill in the frame data.
|
||||
}
|
||||
}
|
||||
intfr = DMA1->INTFR;
|
||||
} while( intfr );
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int i;
|
||||
|
||||
SystemInit();
|
||||
|
||||
// Reset all the peripherals we care about.
|
||||
RCC->APB2PRSTR = 0xffffffff;
|
||||
RCC->APB2PRSTR = 0;
|
||||
|
||||
// Enable DMA
|
||||
RCC->AHBPCENR = RCC_AHBPeriph_SRAM | RCC_AHBPeriph_DMA1;
|
||||
|
||||
// Enable GPIO and Timer 1
|
||||
RCC->APB2PCENR = RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA;
|
||||
|
||||
// GPIO D0/D4 Push-Pull (LED)
|
||||
GPIOD->CFGLR =
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*1) | // PD1 = SWIO (so we don't go off-bus)
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*2) | // PD2 = T1CH1
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4);
|
||||
|
||||
// GPIO C All output.
|
||||
GPIOC->CFGLR =
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*0) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*1) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*2) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*5) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*6) |
|
||||
(GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*7);
|
||||
|
||||
|
||||
// Fill in the plan of what we will be sending out.
|
||||
for( i = 0; i < sizeof(memory_buffer) / sizeof(memory_buffer[0]); i++ )
|
||||
{
|
||||
memory_buffer[i] = (i&1)?0xaa:0x55;
|
||||
}
|
||||
|
||||
// DMA2 can be configured to attach to T1CH1
|
||||
// The system can only DMA out at ~2.2MSPS. 2MHz is stable.
|
||||
DMA1_Channel2->CNTR = sizeof(memory_buffer) / sizeof(memory_buffer[0]);
|
||||
DMA1_Channel2->MADDR = (uint32_t)memory_buffer;
|
||||
DMA1_Channel2->PADDR = (uint32_t)&GPIOC->OUTDR;
|
||||
DMA1_Channel2->CFGR =
|
||||
DMA_CFGR1_DIR | // MEM2PERIPHERAL
|
||||
DMA_CFGR1_PL | // High priority.
|
||||
0 | // 8-bit memory
|
||||
0 | // 8-bit peripheral
|
||||
DMA_CFGR1_MINC | // Increase memory.
|
||||
DMA_CFGR1_CIRC | // Circular mode.
|
||||
DMA_CFGR1_HTIE | // Half-trigger
|
||||
DMA_CFGR1_TCIE | // Whole-trigger
|
||||
DMA_CFGR1_EN; // Enable
|
||||
|
||||
NVIC_EnableIRQ( DMA1_Channel2_IRQn );
|
||||
DMA1_Channel2->CFGR |= DMA_CFGR1_EN;
|
||||
|
||||
// NOTE: You can also hook up DMA1 Channel 3 to T1C2,
|
||||
// if you want to output to multiple IO ports at
|
||||
// at the same time. Just be aware you have to offset
|
||||
// the time they read at by at least 1/8Mth of a second.
|
||||
|
||||
// Setup Timer1.
|
||||
RCC->APB2PRSTR = RCC_APB2Periph_TIM1; // Reset Timer
|
||||
RCC->APB2PRSTR = 0;
|
||||
|
||||
// Timer 1 setup.
|
||||
TIM1->PSC = 0x0000; // Prescaler
|
||||
TIM1->ATRLR = 11; // Auto Reload - sets period (48MHz / (11+1) = 4MHz)
|
||||
TIM1->SWEVGR = TIM_UG | TIM_TG; // Reload immediately + Trigger DMA
|
||||
TIM1->CCER = TIM_CC1E | TIM_CC1P; // Enable CH1 output, positive pol
|
||||
TIM1->CHCTLR1 = TIM_OC1M_2 | TIM_OC1M_1; // CH1 Mode is output, PWM1 (CC1S = 00, OC1M = 110)
|
||||
TIM1->CH1CVR = 6; // Set the Capture Compare Register value to 50% initially
|
||||
TIM1->BDTR = TIM_MOE; // Enable TIM1 outputs
|
||||
TIM1->CTLR1 = TIM_CEN; // Enable TIM1
|
||||
TIM1->DMAINTENR = TIM_UDE | TIM_CC1DE; // Trigger DMA on TC match 1 (DMA Ch2) and TC match 2 (DMA Ch3)
|
||||
|
||||
// Just debug stuff.
|
||||
printf( "Setup OK\n" );
|
||||
|
||||
while(1)
|
||||
{
|
||||
GPIOD->BSHR = 1 | (1<<4); // Turn on GPIOs
|
||||
printf( "%lu\n", GPIOD->OUTDR );
|
||||
Delay_Ms( 250 );
|
||||
GPIOD->BSHR = (1<<16) | (1<<(16+4)); // Turn off GPIODs
|
||||
printf( "%lu\n", GPIOD->OUTDR );
|
||||
Delay_Ms( 250 );
|
||||
printf( "Step\n" );
|
||||
}
|
||||
}
|
||||
|
13
pwm/generator.cpp
Normal file
13
pwm/generator.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "generator.h"
|
||||
|
||||
extern "C" const uint16_t sin_tab[0x100];
|
||||
|
||||
uint16_t Generator::step() {
|
||||
const uint16_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] = step();
|
||||
return len;
|
||||
}
|
14
pwm/generator.h
Normal file
14
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 Generator (const unsigned f) noexcept : OneWay(), base(0u), freq (f) {};
|
||||
unsigned Send (uint16_t * const ptr, const unsigned len) override;
|
||||
protected:
|
||||
uint16_t step ();
|
||||
};
|
||||
|
||||
#endif // GENERATOR_H
|
|
@ -1,7 +1,16 @@
|
|||
#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.
|
||||
* */
|
||||
//////////////////////////////////////
|
||||
static PwmClass pwm;
|
||||
static Generator gen (1u << 16);
|
||||
int main () {
|
||||
pwm.attach(gen);
|
||||
for (;;);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#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;
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
#include "system.h"
|
||||
#include "pwmclass.h"
|
||||
#include "gpio.h"
|
||||
|
||||
static PwmClass * pInstance = nullptr;
|
||||
extern "C" void DMA1_Channel2_IRQHandler( void ) __attribute__((interrupt));
|
||||
void DMA1_Channel2_IRQHandler( void ) {
|
||||
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.HTIF2) pInstance->send(false);
|
||||
if (state.B.TCIF2) pInstance->send(true);
|
||||
if (state.B.HTIF5 != RESET) pInstance->send(false);
|
||||
else if (state.B.TCIF5 != RESET) pInstance->send(true);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize TIM1 for PWM
|
||||
*/
|
||||
static inline void t1pwm_init () noexcept {
|
||||
// Enable GPIOC, GPIOD and TIM1
|
||||
static inline void tim1pwm_init () noexcept {
|
||||
// Enable GPIOD and TIM1
|
||||
RCC.APB2PCENR.modify([] (RCC_Type::APB2PCENR_DEF & r) -> auto {
|
||||
r.B.IOPCEN = SET;
|
||||
r.B.IOPDEN = SET;
|
||||
r.B.TIM1EN = SET;
|
||||
return r.R;
|
||||
|
@ -35,12 +36,12 @@ static inline void t1pwm_init () noexcept {
|
|||
// CTLR1: default is up, events generated, edge align
|
||||
// SMCFGR: default clk input is CK_INT
|
||||
// Prescaler
|
||||
TIM1.PSC.R = 24000u;
|
||||
TIM1.PSC.R = 0u;
|
||||
// Auto Reload - sets period
|
||||
TIM1.ATRLR.R = 255u;
|
||||
TIM1.ATRLR.R = MAXPWM - 1;
|
||||
|
||||
TIM1.CCER.modify([](TIM1_Type::CCER_DEF & r) -> auto {
|
||||
// Enable CH1N output, positive pol
|
||||
// Enable CH1N, CH1 output, positive pol
|
||||
r.B.CC1NE = SET;
|
||||
r.B.CC1E = SET;
|
||||
/*
|
||||
|
@ -67,6 +68,7 @@ static inline void t1pwm_init () noexcept {
|
|||
// Enable TIM1
|
||||
TIM1.CTLR1.B.CEN = SET;
|
||||
}
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
static void dma1ch5_init (void * ptr) {
|
||||
// Enable DMA
|
||||
RCC.AHBPCENR.modify([](RCC_Type::AHBPCENR_DEF & r) -> auto {
|
||||
|
@ -77,8 +79,8 @@ static void dma1ch5_init (void * ptr) {
|
|||
// 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<uint32_t>(ptr);
|
||||
DMA1.PADDR5.R = reinterpret_cast<uint32_t>(& TIM1.CH1CVR);
|
||||
DMA1.MADDR5.R = reinterpret_cast<size_t>(ptr);
|
||||
DMA1.PADDR5.R = reinterpret_cast<size_t>(& TIM1.CH1CVR);
|
||||
DMA1.CFGR5.modify([](DMA1_Type::CFGR5_DEF & r) -> auto {
|
||||
r.B.DIR = SET; // MEM2PERIPHERAL
|
||||
r.B.PL = 2u; // High priority.
|
||||
|
@ -95,8 +97,7 @@ static void dma1ch5_init (void * ptr) {
|
|||
}
|
||||
|
||||
PwmClass::PwmClass() noexcept : pL(buffer), pH(buffer + HALF_LEN), src(nullptr) {
|
||||
for (unsigned n=0; n<FULL_LEN; n+=1) buffer [n] = n << 1;
|
||||
pInstance = this;
|
||||
t1pwm_init ();
|
||||
dma1ch5_init(buffer);
|
||||
tim1pwm_init ();
|
||||
dma1ch5_init (buffer);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
#define PWMCLASS_H
|
||||
#include "oneway.h"
|
||||
static constexpr unsigned HALF_LEN = 64u;
|
||||
static constexpr unsigned FULL_LEN = 2 * HALF_LEN;
|
||||
|
||||
static constexpr unsigned FULL_LEN = 2u * HALF_LEN;
|
||||
static constexpr unsigned MAXPWM = 2048u;
|
||||
/* 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;
|
||||
|
|
27
pwm/sin.py
Executable file
27
pwm/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 (1024.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