// USART Advanced features - Příjem s DMA,  STM32F031K6 48MHz
#include "stm32f0xx.h"
#include "string.h" // memcpy, strncmp

void USART_puts(volatile char *s);
void USART1_Configuration(void);
void init_aux_gpio(void);

#define MAX_LEN 64 // maximální délka přijímaného řetězce

volatile uint8_t rx_buff[MAX_LEN]; // do tohoto pole se přijímá řetězec
volatile uint8_t prikaz[MAX_LEN]; // druhý buffer na řetězec
volatile uint16_t prijatych_znaku=0;
volatile uint8_t nova_zprava=0;

int main(void){
  SystemCoreClockUpdate();
	USART1_Configuration();
	init_aux_gpio(); // PA4 jako výstup (pro měření rychlosti reakce)
	DMA_Cmd(DMA1_Channel3, ENABLE); // spusť příjem skrze DMA

	while(1){
		if(nova_zprava){	// pokud přišel nový řetězec
			prikaz[prijatych_znaku]=0; // na konec řetězce připsat znak '0'
			// zpracujeme příchozí řetězec...
			if(strncmp(prikaz,"ahoj",prijatych_znaku)==0){
				USART_puts("nazdar\n\r"); // ... a slušně odpovíme
			}
			else{
				USART_puts("nerozumim\n\r");	// ... a slušně odpovíme
			}
			nova_zprava=0; // zprávu jsme zpracovali
		}
	}
}

/*
// převezmeme přijatý řetezec a připravíme se pro příjem dalšího
void USART1_IRQHandler(void){
	GPIOA->BSRR = GPIO_Pin_4; // jen k měření rychlosti reakce
 // máme sice jen jeden zdroj pro toto přerušení,
	// ale aby to bylo obecně tak si ho raději zjistím
 if(USART_GetITStatus(USART1,USART_IT_RTO)){
 	USART_ClearITPendingBit(USART1,USART_IT_RTO); // už jej obsluhujeme
 	// zjistíme si kolik jsme přijali znaků (včetně ukončovacího)
		prijatych_znaku=MAX_LEN-DMA_GetCurrDataCounter(DMA1_Channel3);
		// a zkopírujeme si je aby se uvolnil rx_buffer pro další příjem
		memcpy(prikaz,rx_buff,prijatych_znaku);
		// v několika krocích postupně resetujeme DMA kanál
		DMA_Cmd(DMA1_Channel3, DISABLE);
		// nezapomeneme znovu nastavit limit přijímaných znaků
		DMA_SetCurrDataCounter(DMA1_Channel3,MAX_LEN);
		// povolíme DMA
		DMA_Cmd(DMA1_Channel3, ENABLE);
		// od teď už může USART přijímat další řetězec
		nova_zprava=1; // dáme vědět hlavní smyčce že máme data
 }
 GPIOA->BRR = GPIO_Pin_4; // jen k měření rychlosti reakce
}*/


// optimalizovaná varianta rutiny přerušení
// převezmeme přijatý řetezec a připravíme se pro příjem dalšího
void USART1_IRQHandler(void){
	GPIOA->BSRR = GPIO_Pin_4; // jen k měření rychlosti reakce
 // je to přerušení od RTO (jiné jsem nezapnul, není třeba zjišťovat zdroj)
 	USART_ClearITPendingBit(USART1,USART_IT_RTO); // už ho obsluhujeme...
 	// zjistíme si kolik jsme přijali znaků (včetně ukončovacího)
 	prijatych_znaku=MAX_LEN-DMA1_Channel3->CNDTR;
 	// a zkopírujeme si je aby se uvolnil rx_buffer pro další příjem
		memcpy(prikaz,rx_buff,prijatych_znaku);
		// v několika krocích postupně resetujeme DMA kanál
		DMA1_Channel3->CCR &= (uint16_t)(~DMA_CCR_EN); // vypnout DMA
		// nezapomeneme znovu nastavit limit přijímaných znaků
		DMA1_Channel3->CNDTR = MAX_LEN;
		DMA1_Channel3->CCR |= DMA_CCR_EN; // zapnout DMA
		nova_zprava=1; // dáme vědět hlavní smyčce že máme data
 GPIOA->BRR = GPIO_Pin_4; // jen k měření rychlosti reakce
}


void USART1_Configuration(void){
USART_InitTypeDef usart_is;
GPIO_InitTypeDef gp;
NVIC_InitTypeDef nvic;
DMA_InitTypeDef dma;

// konfigurace pinů Rx (PA15),Tx (PA2)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
gp.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_15;
gp.GPIO_Mode = GPIO_Mode_AF;
gp.GPIO_OType = GPIO_OType_PP;
gp.GPIO_PuPd = GPIO_PuPd_NOPULL;
gp.GPIO_Speed = GPIO_Speed_Level_3;
GPIO_Init(GPIOA, &gp);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1); // AF USART1
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_1);

// USART1, 8b+1stop, 115200b/s, No FlowControl
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
usart_is.USART_BaudRate = 115200;
usart_is.USART_WordLength = USART_WordLength_8b;
usart_is.USART_StopBits = USART_StopBits_1;
usart_is.USART_Parity = USART_Parity_No ;
usart_is.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart_is.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &usart_is);


USART1->CR2 |= USART_CR2_RTOEN; // povolit "timeout" funkci
USART1->RTOR = 30; // timeout 260us (30/baudrate)
// povolit DMA pro příjem
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
USART_Cmd(USART1, ENABLE); // spustit USART

// povolit přerušení ot "timeout"
USART_ITConfig(USART1,USART_IT_RTO,ENABLE);

DMA_DeInit(DMA1_Channel3); // pro jistotu (nevím co bylo s DMA dříve)
// init DMA channel3 (kanál s requestem USART1 Rx)
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
dma.DMA_BufferSize = MAX_LEN; // maximální délka přijímaného řetězce
dma.DMA_DIR = DMA_DIR_PeripheralSRC; // směr periferie->paměť
dma.DMA_M2M = DMA_M2M_Disable; // režim M2M nechceme
dma.DMA_MemoryBaseAddr = (uint32_t)(rx_buff); // sem ukládej přenesená data
dma.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // ...jsou 8bit
dma.DMA_MemoryInc = DMA_MemoryInc_Enable; // adresu v paměti inkrementuj
dma.DMA_Mode = DMA_Mode_Normal; // nepoužíváme "kruhový" režim
dma.DMA_PeripheralBaseAddr = (uint32_t)(&USART1->RDR); // odtud data přenášej
dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // ...jsou 8bit
dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // vše posílat na jednu adresu
dma.DMA_Priority = DMA_Priority_Low; // priorita nízká (nízký baudrate, času je dost)
DMA_Init(DMA1_Channel3, &dma); // aplikovat nastavení na channel3

// konfigurace NVIC pro přerušení od USART1
nvic.NVIC_IRQChannel = USART1_IRQn;
nvic.NVIC_IRQChannelCmd = ENABLE;
nvic.NVIC_IRQChannelPriority = 2;
NVIC_Init(&nvic);
}

// primitivní odesílání řetězce
void USART_puts(volatile char *s){
 while(*s){
  while (!USART_GetFlagStatus(USART1,USART_FLAG_TXE)){}
  USART_SendData(USART1, *s);
  s++;
 }
}

// na PA4 je info pulz (měříme rychlost odezvy a zpracování)
void init_aux_gpio(void){
	GPIO_InitTypeDef gp;
	gp.GPIO_Pin = GPIO_Pin_4;
	gp.GPIO_Mode = GPIO_Mode_OUT;
	gp.GPIO_OType = GPIO_OType_PP;
	gp.GPIO_PuPd = GPIO_PuPd_NOPULL;
	gp.GPIO_Speed = GPIO_Speed_Level_3;
	GPIO_Init(GPIOA, &gp);
}
