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:
2016-03-01
Revision:
0:404dae88af71
Child:
1:1b37c4b989b4

File content as of revision 0:404dae88af71:

/***************************************************************************
 * 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
//
#include "stm32f30x.h"
#include "video.h"
//#include "stm32f3xx_hal_conf.h"
#define HTOTAL	(VID_HSIZE+2)					/* Total bytes to send through SPI */

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

u8 volatile *fb[VID_VSIZE*2];

int fboffset=0;

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

void TIMER_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef nvic;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
	TIM_OCInitTypeDef TIM_OCInitStructure;
	u32 TimerPeriod = 0;
	u16 Channel1Pulse = 0, Channel2Pulse = 0, Channel3Pulse = 0;
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_6); // TIM1_CH1
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_1); // TIM2_CH2
 
	

	

	
	/*
		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 = 368; 		/* HSYNC + BACK PORCH */
	
	TIM_TimeBaseStructure.TIM_Prescaler = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;

	TIM_OC1Init(TIM1, &TIM_OCInitStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive;
	TIM_OCInitStructure.TIM_Pulse = Channel2Pulse;
	TIM_OC2Init(TIM1, &TIM_OCInitStructure);

	/* TIM1 counter enable and output enable */
	TIM_CtrlPWMOutputs(TIM1, ENABLE);

	/* Select TIM1 as Master */
	TIM_SelectMasterSlaveMode(TIM1, TIM_MasterSlaveMode_Enable);
	TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);
	
	/*
		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 */
	TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Gated);
	TIM_SelectInputTrigger(TIM2, TIM_TS_ITR0);
	
	TimerPeriod = 625;		/* Vertical lines */
	Channel2Pulse = 2;		/* Sync pulse */
	Channel3Pulse = 24;		/* Sync pulse + Back porch */
	TIM_TimeBaseStructure.TIM_Prescaler = 0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
	TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = Channel2Pulse;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Set;
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Inactive;
	TIM_OCInitStructure.TIM_Pulse = Channel3Pulse;
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);

	/*	TIM2 counter enable and output enable */
	TIM_CtrlPWMOutputs(TIM2, ENABLE);

	/* Interrupt TIM2 */
	nvic.NVIC_IRQChannel = TIM2_IRQn;
    	nvic.NVIC_IRQChannelPreemptionPriority = 1;
    	nvic.NVIC_IRQChannelSubPriority = 0;
    	nvic.NVIC_IRQChannelCmd = ENABLE;

	NVIC_Init(&nvic);
	TIM_ITConfig(TIM2, TIM_IT_CC3, ENABLE);

	/* Interrupt TIM1 */
	nvic.NVIC_IRQChannel = TIM1_CC_IRQn;
    	nvic.NVIC_IRQChannelPreemptionPriority = 1;
    	nvic.NVIC_IRQChannelSubPriority = 0;
    	nvic.NVIC_IRQChannelCmd = ENABLE;

	NVIC_Init(&nvic);
	TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE);
	
//	GPIOE->ODR=0;
	TIM_Cmd(TIM2, ENABLE);
	TIM_Cmd(TIM1, ENABLE);
}

void SPI_Configuration(void)
{
	NVIC_InitTypeDef nvic;
	SPI_InitTypeDef SPI_InitStructure;
	DMA_InitTypeDef	DMA_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5); // SPI1_MOSI

	SPI_Cmd(SPI1, DISABLE);
	DMA_DeInit(DMA1_Channel3);

	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32) fba+fboffset;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_BufferSize = HTOTAL;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_Init(DMA1_Channel3, &DMA_InitStructure);

	SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI1, &SPI_InitStructure);
	
	SPI_CalculateCRC(SPI1, DISABLE);
	SPI_Cmd(SPI1, ENABLE);
	
	SPI1->CR2 |= SPI_I2S_DMAReq_Tx;
	
	nvic.NVIC_IRQChannel = DMA1_Channel3_IRQn;
	nvic.NVIC_IRQChannelPreemptionPriority = 0;
	nvic.NVIC_IRQChannelSubPriority = 0;
	nvic.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&nvic);
	
	DMA1_Channel3->CCR &= ~1;
	DMA1_Channel3->CNDTR = HTOTAL;
	DMA1_Channel3->CMAR = (u32) fba+fboffset;
	
	DMA_ITConfig(DMA1_Channel3, DMA_IT_TC, 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 SPI device.
//*****************************************************************************
//__attribute__((interrupt)) 

__attribute__ ((section ("ccmram"))) void TIM1_CC_IRQHandler(void)
{
	if (vflag)
	{
		DMA1_Channel3->CCR = 0x3093;
	}
	TIM1->SR = 0xFFFB; //~TIM_IT_CC2;
//	GPIOE->ODR ^= 0x2000;
}

//*****************************************************************************
//	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)
{
//	GPIOE->ODR = 0x100;
	vflag = 1;
	TIM2->SR = 0xFFF7; //~TIM_IT_CC3;
//	GPIOE->ODR ^= 0x04000;
}

//*****************************************************************************
//	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 DMA1_Channel3_IRQHandler(void)
{	
//	GPIOE->ODR = 0x200;
	DMA1->IFCR = DMA1_IT_TC3;
	DMA1_Channel3->CCR = 0x92;// | (1<<10) | (1<<8);
	DMA1_Channel3->CNDTR = HTOTAL;
	
	vdraw++;
	
	if (vdraw == 2)
	{
		vdraw = 0;

		vline++;
		
		if (vline == VID_VSIZE)
		{
			vdraw = vline = vflag = 0;
			DMA1_Channel3->CMAR = (u32) fba+fboffset;
		} else {
			DMA1_Channel3->CMAR += HTOTAL;
		}
	}
//	GPIOE->ODR ^= 0x8000;
}

void vidClearScreen(void)
{
	u16 x, y;

	for (y = 0; y < VID_VSIZE*2; y++)
	{
//		GPIOE->ODR=(y<<8);
//		for(x=0;x<1000;x++);
		for (x = 0; x < HTOTAL; x++)
		{
			fba[y*HTOTAL+x] = 0;
		}
	}
}

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