Demo of low res colour vga video for stm32f3 discovery board

Dependencies:   STM32F3-Discovery-minimal

Fork of Space_Invaders_Demo by Martin Johnson

Revision:
0:404dae88af71
Child:
1:1b37c4b989b4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/video.c	Tue Mar 01 02:40:19 2016 +0000
@@ -0,0 +1,359 @@
+/***************************************************************************
+ * 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();
+}
+