Demo of low res colour vga video for stm32f3 discovery board
Dependencies: STM32F3-Discovery-minimal
Fork of Space_Invaders_Demo by
video.c
- Committer:
- MartinJohnson
- Date:
- 2018-05-17
- Revision:
- 6:f16335989076
- Parent:
- 5:594c9712697c
- Child:
- 7:513afc954d6e
- Child:
- 12:f819427d0bec
File content as of revision 6:f16335989076:
/*************************************************************************** * 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 R=Red=PD0 // \ o o Ho Vo o / V=Vsync=PA1 G=Green=PD1 // -------------- B=Blue=PD2 // // R G B 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; __attribute__ ((section ("ccmram"))) static volatile u16 vline = 0; /* The current line being drawn */ __attribute__ ((section ("ccmram"))) static volatile u32 vflag = 0; /* When 1, the DMA request can draw on the screen */ __attribute__ ((section ("ccmram"))) 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 = 360; /* 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->CCMR1 = (TIM1->CCMR1 & ~(TIM_CCMR1_OC2M | TIM_CCMR1_CC2S)) | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2CE; TIM1->CCR1=Channel1Pulse; TIM1->CCR2=Channel2Pulse; // TIM1->CCR3=Channel2Pulse+100; //TIM1->CCR2 |= TIM_CR2_OIS1; // output idle state set // TIM1->CCR3 |= TIM_CR2_OIS1; // 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) | (3<<4); ///*TIM_CR2_MMS_2 |*/ TIM_CR2_MMS_1 /*| TIM_CR2_MMS_0*/;// TIM_TRGOSource_Update mode TIM8->CR1 &= ~TIM_CR1_CEN; TIM8->CR1 |= TIM_CR1_ARPE; TIM8->PSC=0; TIM8->ARR=8; 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 ;// triggered 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]=32; // 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]=0; // 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; //TIM8->CNT=7;//4-(TIM1->CNT&7); } 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; TIM8->CR1 &= ~TIM_CR1_CEN; TIM8->CNT=0; DMA2_Channel1->CCR = DMA_CCR_DIR | DMA_CCR_MINC | DMA_CCR_PL_1 | DMA_CCR_PL_0 | DMA_CCR_TCIE;// | DMA_CCR_EN;// 0x3093; 0x92;// | (1<<10) | (1<<8); DMA2_Channel1->CNDTR = HTOTAL; vdraw++; if (vdraw == 3) { vdraw = 0; vline++; if (vline == VID_VSIZE) { vdraw = vline = vflag = 0; DMA2_Channel1->CMAR = (u32) fba+fboffset; } else { DMA2_Channel1->CMAR += HTOTAL; } } // 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; } __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(); }