#include "stm32f30x.h"

void dac_init(void);
void init_tim7(void);

#define SAMPLES sizeof(wave)/sizeof(wave[0])
uint32_t wave[]={0xfff<<16 | 0, 0<<16 | 0xfff}; // "step" impulz (od nuly do maxima a zpět)
#define DAC_DATA_REG &(DAC1->DHR12RD)

int main(void){
	SystemCoreClockUpdate(); // 72 MHz
	dac_init();
	init_tim7(); // trigger pro DAC
	DMA_Cmd(DMA1_Channel4, ENABLE); // spustíme DMA
	DAC_DMACmd(DAC1,DAC_Channel_2,ENABLE); // povolíme DAC posílat requesty do DMA
	TIM_Cmd(TIM7, ENABLE); // spustit TIM7 - rozběh waveformy

	while(1){
		PWR_EnterSleepMode(PWR_SLEEPEntry_WFI); // tady už není co dělat, tak jdeme spát
	}
}

void init_tim7(void){
TIM_TimeBaseInitTypeDef tim;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
tim.TIM_Prescaler = 9; // 1MHz / 10 = 100kHz (T=10us)
tim.TIM_Period = 71; // 72MHz / 72 = 1MHz
tim.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM7, &tim);
// výstup timeru (TRGO) je update událost (přetečení), signál jde pak jako trigger do DAC
TIM_SelectOutputTrigger(TIM7,TIM_TRGOSource_Update);
}

void dac_init(void){
	GPIO_InitTypeDef gp;
	DAC_InitTypeDef dac;
	DMA_InitTypeDef dma;

	// zapnout GPIOA, DAC1, SYSCFG a DMA1
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_DMA1, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // kvůli remapu DMA requestů
// PA4,5,6 výstupy DAC1 a DAC2 (nepoužíváme všechny)
gp.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
gp.GPIO_Mode = GPIO_Mode_AN;
gp.GPIO_OType = GPIO_OType_PP;
gp.GPIO_PuPd = GPIO_PuPd_NOPULL;
gp.GPIO_Speed = GPIO_Speed_Level_1;
GPIO_Init(GPIOA, &gp);

/* Pozor ať vás nemate "buffer switch" - název implikuje že funkce zapíná nebo vypíná buffer, to ale není úplně pravda
 * pro DAC1_OUT1 (PA4) má buffer a switch_enable ho "vypíná" (matoucí že :D)
 * pro DAC2_OUT1 (PA6) tato funkce pouze připojuje/odpojuje výstup DAC na pin ... žádný buffer na něm není !
 * pro DAC1_OUT2 (PA5) tato funkce pouze připojuje/odpojuje výstup DAC na pin ... žádný buffer na něm není !
 *
 * pro DAC1_OUT1:
 * 	- "Switch_Disable" = buffer je zapnutý :D
 * 	- "Switch_Enable"  = buffer vypnutý :D
 * pro zbylé kanály (DAC1_OUT2 a DAC2_OUT1):
 *  - "Switch_Disable" = výstup DAC je odpojený od pinu (PAx)
 *  - "Switch_Enable"  = výstup DAC je připojený k pinu (PAx)
 */
dac.DAC_Buffer_Switch = DAC_BufferSwitch_Disable; // Zapnout (nevypnout) DAC1_OT1 buffer
dac.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
dac.DAC_Trigger = DAC_Trigger_T7_TRGO; // převod spouští TRGO signál z TIM7
dac.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_Init(DAC1,DAC_Channel_1,&dac); // nastavit konfiguraci DAC1_OUT1

dac.DAC_Buffer_Switch = DAC_BufferSwitch_Enable; // Zapnout na PA5 Výstup DAC1_OUT2 (buffer nemá)
dac.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bit0;
dac.DAC_Trigger = DAC_Trigger_T7_TRGO; // převod spouští TRGO signál z TIM7
dac.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_Init(DAC1,DAC_Channel_2,&dac); // nastavit konfiguraci DAC1_OUT2

// konfigurace DMA
dma.DMA_BufferSize = SAMPLES; // počet vzorků generovaného průběhu
dma.DMA_DIR = DMA_DIR_PeripheralDST; // data tečou do periferie
dma.DMA_M2M = DMA_M2M_Disable; // přesno paměť-paměť nechceme
dma.DMA_MemoryBaseAddr = (uint32_t)wave; // pole se hodnotami pro DAC
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // přenášíme 32bit (data do obou kanálů DAC)
dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma.DMA_Mode = DMA_Mode_Circular; // generovat stále dokola
dma.DMA_PeripheralBaseAddr = (uint32_t)DAC_DATA_REG; // do tohoto registru zapisovat data pro DAC
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // přenášíme 32bit (data do obou kanálů DAC)
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel4, &dma); // channel 4 jsou requesty od DAC1 channel 2

// hnusný podraz, DAC1 ch2 requesty je potřeba remapovat aby vedly do DMA1Ch4 (ačkoli jiné DMA nemáme :D )
SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_TIM7DAC1Ch2, ENABLE);
// spustit obě DAC
DAC_Cmd(DAC1,DAC_Channel_1,ENABLE);
DAC_Cmd(DAC1,DAC_Channel_2,ENABLE);
}