Demo of low res colour vga video for stm32f3 discovery board

Dependencies:   STM32F3-Discovery-minimal

Fork of Space_Invaders_Demo by Martin Johnson

video.c

Committer:
MartinJohnson
Date:
2018-05-16
Revision:
3:93e488fbb8a2
Parent:
1:1b37c4b989b4
Child:
4:de45d218ed3c

File content as of revision 3:93e488fbb8a2:

/***************************************************************************
 * STM32 VGA demo
 * Copyright (C) 2012 Artekit Italy
 * http://www.artekit.eu
 * Written by Ruben H. Meleca
 
### video.c
 
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

***************************************************************************/

// VGA connector
// ---------------------
// \  Ro Go Bo  o Xo   /	X=Gnd
//  \ o  o  o  o Xo   /		H=HSync=PA8
//   \ o  o Ho Vo  o /		V=Vsync=PA1
//    --------------		G=Green=PA7
//
// o G o o   o X X 
// o o o o o H V o
// 
#include "stm32f30x.h"
#include "video.h"
#define HTOTAL	(VID_HSIZE+3)					/* Total bytes to send */

__attribute__ ((aligned (4))) u8 volatile fba[VID_VSIZE*HTOTAL];	/* Frame buffer */

__attribute__ ((section ("ccmram"))) u8 volatile *fb[VID_VSIZE];

int fboffset=0;

static volatile u16 vline = 0;				/* The current line being drawn */
static volatile u32 vflag = 0;				/* When 1, the DMA request can draw on the screen */
static volatile u32 vdraw = 0;				/* Used to increment vline every 3 drawn lines */

#define GPIO_MODE_INPUT 0
#define GPIO_MODE_OUTPUT 1
#define GPIO_MODE_AF 2

#define GPIO_NO_PULL 0
#define GPIO_PULL_UP 1
#define GPIO_PULL_DOWN 2

#define GPIO_OUTPUT_PUSH_PULL 0


#define GPIO_SPEED_MEDIUM 1
#define GPIO_SPEED_LOW 0
#define GPIO_SPEED_HIGH 3


void gpio_set_mode(GPIO_TypeDef *g,int n,int mode) {
	n=n<<1;
	g->MODER = (g->MODER & ~(3<<n)) | (mode<<n);
}

void gpio_set_af(GPIO_TypeDef *g,int n,int af, int otype, int pupd, int speed) {
	int reg=n>>3;
	int pos=(n&7)*4;
	g->AFR[reg] = (g->AFR[reg] & ~(0xf<<pos)) | (af<<pos); // alt func
	pos=(n<<1);
	g->OSPEEDR = (g->OSPEEDR & ~(3<<pos)) | (speed<<pos);
	g->OTYPER = (g->OTYPER & ~(1<<n)) | (otype<<n);
	gpio_set_mode(g,n,GPIO_MODE_AF);
	g->PUPDR = (g->PUPDR & ~(3<<pos)) | (pupd<<pos);	
}

void TIMER_Configuration(void) {
	u32 TimerPeriod = 0;
	u16 Channel1Pulse = 0, Channel2Pulse = 0, Channel3Pulse = 0;
	
	gpio_set_af(GPIOA,1,1,GPIO_OUTPUT_PUSH_PULL, GPIO_NO_PULL, GPIO_SPEED_LOW);
	gpio_set_af(GPIOA,8,6,GPIO_OUTPUT_PUSH_PULL, GPIO_NO_PULL, GPIO_SPEED_LOW);

	/*
		SVGA 800x600 @ 56 Hz
		Vertical refresh	35.15625 kHz
		Pixel freq.			36.0 MHz
		
		1 system tick @ 72Mhz = 0,0138 us
	*/
	
	/*
		Horizontal timing
		-----------------
		
		Timer 1 period = 35156 Hz
		
		Timer 1 channel 1 generates a pulse for HSYNC each 28.4 us.
		28.4 us	= Visible area + Front porch + Sync pulse + Back porch.
		HSYNC is 2 us long, so the math to do is:
		2us / 0,0138us = 144 system ticks.
		
		Timer 1 channel 2 generates a pulse equal to HSYNC + back porch.
		This interrupt will fire the DMA request to draw on the screen if vflag == 1.
		Since firing the DMA takes more or less 800ns, we'll add some extra time.
		The math for HSYNC + back porch is:
		(2us + 3,55us - dma) / 0,0138us = +-350 system ticks
	
		Horizontal timing info
		----------------------

						Dots	us
		--------------------------------------------		
		Visible area	800		22.222222222222
		Front porch		24		0.66666666666667
		Sync pulse		72		2
		Back porch		128		3.5555555555556
		Whole line		1024	28.444444444444
	
	*/

	TimerPeriod = 2048;
	Channel1Pulse = 144;		/* HSYNC */
	Channel2Pulse = 364; 		/* HSYNC + BACK PORCH */
	
	TIM1->CR1 &= ~TIM_CR1_CEN;
	TIM1->PSC=0;
	TIM1->ARR=TimerPeriod;
	TIM1->CNT=0;
	
	// disable Capture and Compare 1 and 2
	TIM1->CCER &= ~(TIM_CCER_CC1E | TIM_CCER_CC2E);
	// set output compare 1 to PWM mode with preload
	TIM1->CCMR1 = (TIM1->CCMR1 & ~(TIM_CCMR1_OC1M | TIM_CCMR1_CC1S)) | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;
	TIM1->CCR1=Channel1Pulse;
	TIM1->CCR2=Channel2Pulse;
	TIM1->CCR2 |= TIM_CR2_OIS1; // output idle state set
	// enable Capture and Compare 1
	TIM1->CCER |=  TIM_CCER_CC1E |  TIM_CCER_CC2E | TIM_CCER_CC1P | TIM_CCER_CC2P; // output polarity low
	// main output enable
    TIM1->BDTR |= TIM_BDTR_MOE; 
	
	TIM1->SMCR |= TIM_SMCR_MSM; // master slave mode
	
	TIM1->CR2 = (TIM1->CR2 & ~TIM_CR2_MMS) | TIM_CR2_MMS_1;// TIM_TRGOSource_Update mode
	
	TIM8->CR1 &= ~TIM_CR1_CEN;
	TIM8->PSC=2;
	TIM8->ARR=2;
	TIM8->CNT=0;
	//TIM16->CR1 |= TIM_CR1_OPM; 
	
	TIM8->DIER |= TIM_DIER_UDE;
	TIM8->SMCR=TIM8->SMCR & ~(TIM_SMCR_SMS | TIM_SMCR_TS) | 4 ;
	//TIM16->SMCR |= TIM_SMCR_MSM;
	//TIM16->CR2 = (TIM16->CR2 & ~TIM_CR2_MMS) | TIM_CR2_MMS_1;// TIM_TRGOSource_Update mode
	
	/*
		Vertical timing
		---------------
		
		Polarity of vertical sync pulse is positive.

						Lines
		------------------------------
		Visible area	600
		Front porch		1
		Sync pulse		2
		Back porch		22
		Whole frame		625
		
	*/

	/* VSYNC (TIM2_CH2) and VSYNC_BACKPORCH (TIM2_CH3) */
	/* Channel 2 and 3 Configuration in PWM mode */
	TIM2->SMCR=TIM2->SMCR & ~(TIM_SMCR_SMS | TIM_SMCR_TS) | 5 ;// gated slave mode trigger source 0
	
	
	TimerPeriod = 625;		/* Vertical lines */
	Channel2Pulse = 2;		/* Sync pulse */
	Channel3Pulse = 24;		/* Sync pulse + Back porch */

	TIM2->CR1 &= ~TIM_CR1_CEN;
	TIM2->PSC=0;
	TIM2->ARR=TimerPeriod;
	TIM2->CNT=0;

	// disable Capture and Compare 2 and 3
	TIM2->CCER &= ~(TIM_CCER_CC2E | TIM_CCER_CC3E);
	// set output compare 1 to PWM mode with preload
	TIM2->CCMR1 = (TIM2->CCMR1 & ~(TIM_CCMR1_OC2M | TIM_CCMR1_CC2S)) | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1;
	TIM2->CCR2=Channel2Pulse;
	TIM2->CCR3=Channel3Pulse;
	
//	TIM2->CCR2 |= TIM_CR2_OIS1; // output idle state set
	// enable Capture and Compare 2 and 3
	TIM2->CCER |=  TIM_CCER_CC2E |  TIM_CCER_CC3E | TIM_CCER_CC1P | TIM_CCER_CC2P; // output polarity low
	
	// main output enable
    TIM2->BDTR |= TIM_BDTR_MOE; 

	NVIC->IP[TIM2_IRQn]=10; // Interrupt Priority, lower is higher priority
	NVIC->ISER[TIM2_IRQn >> 0x05] = 1 << (TIM2_IRQn & 0x1F); // Interrupt enable
	
	TIM2->DIER |= TIM_DIER_CC3IE;
	
	NVIC->IP[TIM1_CC_IRQn]=1; // Interrupt Priority, lower is higher priority
	NVIC->ISER[TIM1_CC_IRQn >> 0x05] = 1 << (TIM1_CC_IRQn & 0x1F); // Interrupt enable

	TIM1->DIER |= TIM_DIER_CC2IE;

	TIM2->CR1 |= TIM_CR1_CEN;
	TIM1->CR1 |= TIM_CR1_CEN;
}

void DMA_Configuration(void) {
	//gpio_set_af(GPIOA,7,5,GPIO_OUTPUT_PUSH_PULL, GPIO_NO_PULL, GPIO_SPEED_HIGH);
	
	GPIOD->PUPDR = (GPIOD->PUPDR & ~0xffff) | 0;//xaaaa; // pull down (1010)
	GPIOD->OSPEEDR = (GPIOD->OSPEEDR & ~(0xffff)) | 0xffff;
	
	
	RCC->AHBENR |= RCC_AHBENR_DMA2EN;
    // direction = peripheral dest, memory inc, peripheral size=halfword, memory size=byte, priority level=high, transmission complete interrupt enabled
    DMA2_Channel1->CCR = DMA_CCR_DIR | DMA_CCR_MINC  | DMA_CCR_PL_1 | DMA_CCR_PL_0 | DMA_CCR_TCIE;
//    DMA1_Channel3->CCR =  DMA_CCR_PINC  | DMA_CCR_PL_1 | DMA_CCR_PL_0 | DMA_CCR_TCIE | DMA_CCR_MEM2MEM;
    // bytes to transfer
    DMA2_Channel1->CNDTR = HTOTAL*2;
    // peripheral address
    DMA2_Channel1->CPAR =(uint32_t) &GPIOD->ODR;
    // memory address
    DMA2_Channel1->CMAR =(u32) fba+fboffset;
    // enable CC DMA for TIM16
   // TIM16->DIER |= TIM_DIER_CC1DE;
	// configure NVIC
    NVIC->IP[DMA2_Channel1_IRQn]=16; // Interrupt Priority, lower is higher priority
	NVIC->ISER[DMA2_Channel1_IRQn >> 0x05] = 1 << (DMA2_Channel1_IRQn & 0x1F); // Interrupt enable
	
}

//*****************************************************************************
//	This irq is generated at the end of the horizontal back porch.
//	Test if inside a valid vertical start frame (vflag variable), 
//	and start the DMA to output a single frame buffer line through the GPIO device.
//*****************************************************************************
//__attribute__((interrupt)) 

__attribute__ ((section ("ccmram"))) void TIM1_CC_IRQHandler(void) {
	if (vflag) {
		DMA2_Channel1->CCR = DMA_CCR_DIR | DMA_CCR_MINC  | DMA_CCR_PL_1 | DMA_CCR_PL_0 | DMA_CCR_TCIE | DMA_CCR_EN;;// 0x3093;
//		TIM8->CNT=0;//TIM8->ARR;
		TIM8->CR1 |= TIM_CR1_CEN;
	}
	TIM1->SR = 0xFFFB; //~TIM_IT_CC2;
}

//*****************************************************************************
//	This irq is generated at the end of the vertical back porch.
//	Sets the 'vflag' variable to 1 (valid vertical frame).
//*****************************************************************************
//__attribute__((interrupt)) 
__attribute__ ((section ("ccmram"))) void TIM2_IRQHandler(void) {
	vflag = 1;
	TIM2->SR = 0xFFF7; //~TIM_IT_CC3;
//		DMA2_Channel1->CCR = DMA_CCR_DIR | DMA_CCR_MINC  | DMA_CCR_PL_1 | DMA_CCR_PL_0 | DMA_CCR_TCIE | DMA_CCR_EN;;// 0x3093;
	//	TIM8->CNT=0;
		TIM8->CR1 |= TIM_CR1_CEN;

}

//*****************************************************************************
//	This interrupt is generated at the end of every line.
//	It will increment the line number and set the corresponding line pointer
//	in the DMA register.
//*****************************************************************************
//__attribute__((interrupt)) 
__attribute__ ((section ("ccmram"))) void DMA2_Channel1_IRQHandler(void) {	
	DMA2->IFCR = DMA_ISR_TCIF1;
	DMA2_Channel1->CCR = DMA_CCR_DIR | DMA_CCR_MINC  | DMA_CCR_PL_1 | DMA_CCR_PL_0 | DMA_CCR_TCIE;// 0x3093; 0x92;// | (1<<10) | (1<<8);
	DMA2_Channel1->CNDTR = HTOTAL;
	TIM8->CR1 &= ~TIM_CR1_CEN;
	
	vdraw++;
	if (vdraw == 4) {
		vdraw = 0;
		vline++;
		if (vline == VID_VSIZE) {
			vdraw = vline = vflag = 0;
			DMA2_Channel1->CMAR = (u32) fba+fboffset;
		} else {
			DMA2_Channel1->CMAR += HTOTAL;
		}
	}
}
__attribute__ ((section ("ccmram"))) void vidNextBuffer(void) {
	    char *fp=(unsigned *)fb[0];
	    for(int i=0;i<VID_VSIZE*HTOTAL;i++)
	    	*fp++=(*fp>>4);//&0xf0f0f0f;
}

__attribute__ ((section ("ccmram"))) void waitForRefresh(void) {
	while(vflag) __wfi();
}
void vidClearScreen(void) {
	u16 x, y;

	for (y = 0; y < VID_VSIZE; y++) 	{
		for (x = 0; x < HTOTAL; x++) {
			fba[y*HTOTAL+x] = 0;
		}
	}
}

void vidDot(int x, int y, int col) {
	fb[y][x]=col;
}

void vidLine(int x, int y, int col) {
	fb[y][x]=col;
}

void vidInit(void) {
	int i;
	DMA_Configuration();
	TIMER_Configuration();
	for(i=0;i<VID_VSIZE;i++)
		fb[i]=fba+i*HTOTAL;
	vidClearScreen();
}