From 116ab5d23bcc114b69db9ed8315e8f195cfc2ef7 Mon Sep 17 00:00:00 2001 From: fruchti Date: Thu, 23 Aug 2018 11:17:49 +0200 Subject: [PATCH] Add (not really working) camera interface --- .vscode/settings.json | 3 +- build-number.txt | 2 +- src/main.c | 25 +-- src/main.h | 1 + src/ov7670.c | 347 ++++++++++++++++++++++++++++++++++++++++++ src/ov7670.h | 6 + src/pinning.h | 4 +- 7 files changed, 374 insertions(+), 14 deletions(-) create mode 100644 src/ov7670.c create mode 100644 src/ov7670.h diff --git a/.vscode/settings.json b/.vscode/settings.json index bcf3a3d..cb13591 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,7 +11,8 @@ "stm32f100xb.h": "c", "buildid.h": "c", "font_arpegius_32.h": "c", - "font_arpegius_16.h": "c" + "font_arpegius_16.h": "c", + "debug.h": "c" }, "C_Cpp.intelliSenseEngineFallback": "Enabled" } \ No newline at end of file diff --git a/build-number.txt b/build-number.txt index 3860ed9..ec8785e 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -323 +405 diff --git a/src/main.c b/src/main.c index 5d9f877..73897b3 100755 --- a/src/main.c +++ b/src/main.c @@ -2,9 +2,6 @@ int main(void) { - LTP1245_Init(); - - // LTP1245_FeedPaper(10); RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; GPIOC->CRH = (GPIOC->CRH @@ -12,16 +9,24 @@ int main(void) | (0x01 << (4 * PIN_LED - 32)) // Output, max. 10 MHz ; - char buff[30]; - itoa(BUILD_NUMBER, buff, 10); + Camera_Init(); + LTP1245_Init(); + + + char buff[30] = "Build No. "; + itoa(BUILD_NUMBER, buff + strlen(buff), 10); Print_Text(buff, &Messe_Duesseldorf_39); - // LTP1245_FeedPaper(10); - // Print_Text("Testy McTestFace", &Arpegius_32); - // Print_Text("123555bcD", &Messe_Duesseldorf_39); - Print_Text("This is a long line with very much text!", &Hannover_Messe_Serif_26); + LTP1245_FeedPaper(2); + + strcpy(buff, "Line count: "); + extern int LineCount; + itoa(LineCount, buff + strlen(buff), 10); + Print_Text(buff, &Hannover_Messe_Serif_26); + LTP1245_FeedPaper(10); - LTP1245_Cut(); + // LTP1245_FeedPaper(10); + // LTP1245_Cut(); for(;;) { diff --git a/src/main.h b/src/main.h index ecdbe2b..bace11f 100755 --- a/src/main.h +++ b/src/main.h @@ -8,6 +8,7 @@ #include "pinning.h" #include "buildid.h" #include "debug.h" +#include "ov7670.h" #include "ltp1245.h" #include "print.h" #include "font_hannover_messe_serif_26.h" diff --git a/src/ov7670.c b/src/ov7670.c new file mode 100644 index 0000000..cae9976 --- /dev/null +++ b/src/ov7670.c @@ -0,0 +1,347 @@ +#include "ov7670.h" +#include "pinning.h" +#include "debug.h" + +#define REG_GAIN 0x00 +#define REG_BLUE 0x01 +#define REG_RED 0x02 +#define REG_VREF 0x03 +#define REG_COM1 0x04 +#define REG_BAVE 0x05 +#define REG_GbAVE 0x06 +#define REG_AECHH 0x07 +#define REG_RAVE 0x08 +#define REV_COM2 0x09 +#define REG_PID 0x0a +#define REG_VER 0x0b +#define REG_COM3 0x0c +#define REG_COM4 0x0d +#define REG_COM5 0x0e +#define REG_COM6 0x0f +#define REG_AECH 0x10 +#define REG_CLKRC 0x11 +#define REG_COM7 0x12 +#define REG_COM8 0x13 +#define REG_COM9 0x14 +#define REG_COM10 0x15 +#define REG_HSTART 0x17 +#define REG_HSTOP 0x18 +#define REG_VSTRT 0x19 +#define REG_VSTOP 0x1a +#define REG_PSHIFT 0x1b +#define REG_MIDH 0x1c +#define REG_MIDL 0x1d +#define REG_MVFP 0x1e +#define REG_LAEC 0x1f +#define REG_ADCCTR0 0x20 +#define REG_ADCCTR1 0x21 +#define REG_ADCCTR2 0x22 +#define REG_ADCCTR3 0x23 +#define REG_AEW 0x24 +#define REG_AEB 0x25 +#define REG_VPT 0x26 +#define REG_BBIAS 0x27 +#define REG_GbBIAS 0x28 +#define REG_EXHCH 0x2a +#define REG_EXHCL 0x2b +#define REG_RBIAS 0x2c +#define REG_ADVFL 0x2d +#define REG_ADVFH 0x2e +#define REG_YAVE 0x2f +#define REG_HSYST 0x30 +#define REG_HSYEN 0x31 +#define REG_HREF 0x32 +#define REG_CHLF 0x33 +#define REG_ARBLM 0x34 +#define REG_ADC 0x37 +#define REG_ACOM 0x38 +#define REG_OFON 0x39 +#define REG_TSLB 0x3a +#define REG_COM11 0x3b +#define REG_COM12 0x3c +#define REG_COM13 0x3d +#define REG_COM14 0x3e +#define REG_EDGE 0x3f +#define REG_COM15 0x40 +#define REG_COM16 0x41 +#define REG_COM17 0x42 +#define REG_REG4B 0x4b +#define REG_DNSTH 0x4c +#define REG_MTX1 0x4f +#define REG_MTX2 0x50 +#define REG_MTX3 0x51 +#define REG_MTX4 0x52 +#define REG_MTX5 0x53 +#define REG_MTX6 0x54 +#define REG_BRIGHT 0x55 +#define REG_CONTRAS 0x56 +#define REG_CONTRAS_CENTER 0x57 +#define REG_MTXS 0x58 +#define REG_LCC1 0x62 +#define REG_LCC2 0x63 +#define REG_LCC3 0x64 +#define REG_LCC4 0x65 +#define REG_LCC5 0x66 +#define REG_MANU 0x67 +#define REG_MANV 0x68 +#define REG_GFIX 0x69 +#define REG_GGAIN 0x6a +#define REG_DBLV 0x6b +#define REG_AWBCTR3 0x6c +#define REG_AWBCTR2 0x6d +#define REG_AWBCTR1 0x6e +#define REG_AWBCTR0 0x6f +#define REG_SCALING_XSC 0x70 +#define REG_SCALING_YSC 0x71 +#define REG_SCALING_DCWCTR 0x72 +#define REG_SCALING_PCLK_DIV 0x73 +#define REG_REG74 0x74 +#define REG_REG75 0x75 +#define REG_REG76 0x76 +#define REG_REG77 0x77 +#define REG_SLOP 0x7a +#define REG_GAM1 0x7b +#define REG_GAM2 0x7c +#define REG_GAM3 0x7d +#define REG_GAM4 0x7e +#define REG_GAM5 0x7f +#define REG_GAM6 0x80 +#define REG_GAM7 0x81 +#define REG_GAM8 0x82 +#define REG_GAM9 0x83 +#define REG_GAM10 0x84 +#define REG_GAM11 0x85 +#define REG_GAM12 0x86 +#define REG_GAM13 0x87 +#define REG_GAM14 0x88 +#define REG_GAM15 0x89 +#define REG_RGB444 0x8c +#define REG_DM_LNL 0x92 +#define REG_DM_LNH 0x93 +#define REG_LCC6 0x94 +#define REG_LCC7 0x95 +#define REG_BD50ST 0x9d +#define REG_BD60ST 0x9e +#define REG_HAECC1 0x9f +#define REG_HAECC2 0xa0 +#define REG_SCALING_PCLK_DELAY 0xa2 +#define REG_NT_CTRL 0xa4 +#define REG_BD50MAX 0xa5 +#define REG_HAECC3 0xa6 +#define REG_HAECC4 0xa7 +#define REG_HAECC5 0xa8 +#define REG_HAECC6 0xa9 +#define REG_HAECC7 0xaa +#define REG_BD60MAX 0xab +#define REG_STR_OPT 0xac +#define REG_STR_R 0xad +#define REG_STR_G 0xae +#define REG_STR_B 0xaf +#define REG_ABLC1 0xb1 +#define REG_THL_ST 0xb3 +#define REG_THL_DLT 0xb5 +#define REG_AD_CHB 0xbe +#define REG_AD_CHR 0xbf +#define REG_AD_CHGb 0xc0 +#define REG_AD_CHGr 0xc1 +#define REG_SATCR 0xc9 + +#define I2C_ADDRESS 0x42 + +uint8_t ImageBuffer[176 * 144 / 8]; +static volatile int CurrentLine = 0; +uint8_t LineBuffer1[176]; +uint8_t LineBuffer2[176]; +int LineCount = 0; + +static uint8_t ReadRegister(uint8_t reg) +{ + while(I2C1->SR2 & I2C_SR2_BUSY); + I2C1->CR1 |= I2C_CR1_START; + while(~I2C1->SR1 & I2C_SR1_SB); + I2C1->DR = I2C_ADDRESS; + while(~I2C1->SR1 & I2C_SR1_ADDR); + I2C1->SR2; // Dummy read + I2C1->DR = reg; // Write the register number to be read + while(~I2C1->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)); + I2C1->CR1 |= I2C_CR1_STOP; + + // Read the register value + while(I2C1->SR2 & I2C_SR2_BUSY); + I2C1->CR1 |= I2C_CR1_START; + while(~I2C1->SR1 & I2C_SR1_SB); + I2C1->DR = I2C_ADDRESS | 1; + while(~I2C1->SR1 & I2C_SR1_ADDR); + I2C1->SR2; // Dummy read + I2C1->CR1 |= I2C_CR1_STOP; + while(~I2C1->SR1 & I2C_SR1_RXNE); + uint8_t data = I2C1->DR; + return data; +} + +static void WriteRegister(uint8_t reg, uint8_t value) +{ + while(I2C1->SR2 & I2C_SR2_BUSY); + I2C1->CR1 |= I2C_CR1_START; + while(~I2C1->SR1 & I2C_SR1_SB); + I2C1->DR = I2C_ADDRESS; + while(~I2C1->SR1 & I2C_SR1_ADDR); + I2C1->SR2; // Dummy read + I2C1->DR = reg; // Write the register number + while(~I2C1->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)); + I2C1->DR = value; // Write the register value + while(~I2C1->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)); + I2C1->CR1 |= I2C_CR1_STOP; +} + +void Camera_Init(void) +{ + RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; + RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; + RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; + RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; + RCC->AHBENR |= RCC_AHBENR_DMA1EN; + RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; + RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; + + // Reset pin + GPIOB->CRH = (GPIOB->CRH + & ~(0x0f << (4 * PIN_CAMERA_RESET - 32))) + | (0x01 << (4 * PIN_CAMERA_RESET - 32)) // Output, max. 10 MHz + ; + GPIOB->BRR = (1 << PIN_CAMERA_RESET); + GPIOB->BSRR = (1 << PIN_CAMERA_RESET); + + + // Enable MCO for camera main clock line (PLL / 2 -> 24 MHz) + RCC->CFGR |= RCC_CFGR_MCO; + GPIOA->CRH = (GPIOA->CRH + & ~(0x0f << (4 * PIN_CAMERA_MCLK - 32))) + | (0x0b << (4 * PIN_CAMERA_MCLK - 32)) // Output, max. 50 MHz + ; + + AFIO->MAPR |= AFIO_MAPR_I2C1_REMAP; + + // I2C interface for camera configuration + GPIOB->CRH = (GPIOB->CRH + & ~(0x0f << (4 * PIN_CAMERA_SCL - 32)) + & ~(0x0f << (4 * PIN_CAMERA_SDA - 32))) + | (0x0e << (4 * PIN_CAMERA_SCL - 32)) // AF OD output, 2 MHz + | (0x0e << (4 * PIN_CAMERA_SDA - 32)) // AF OD output, 2 MHz + ; + + I2C1->CR1 = I2C_CR1_SWRST; + I2C1->CR1 = 0; + I2C1->CR2 = (24 << I2C_CR2_FREQ_Pos); + I2C1->CCR = I2C_CCR_FS | I2C_CCR_DUTY | (1 << I2C_CCR_CCR_Pos) | 5; + I2C1->CR1 = I2C_CR1_PE; + + // Timer setup + AFIO->MAPR |= AFIO_MAPR_TIM3_REMAP_PARTIALREMAP; + GPIOB->CRL = (GPIOB->CRL + & ~(0x0f << (PIN_CAMERA_HSYNC * 4)) + & ~(0x0f << (PIN_CAMERA_PCLK * 4))) + | (0x04 << (PIN_CAMERA_HSYNC * 4)) // Floating input + | (0x04 << (PIN_CAMERA_PCLK * 4)) // Floating input + ; + GPIOA->CRH = (GPIOA->CRH + & ~(0x0f << (PIN_CAMERA_VSYNC * 4 - 32))) + | (0x04 << (PIN_CAMERA_VSYNC * 4 - 32)) // Floating input + ; + + // TIM1_CH2 is VSYNC + TIM1->PSC = 0; + TIM1->ARR = 65535; + TIM1->CCMR1 = TIM_CCMR1_CC2S_0; + TIM1->CCER = TIM_CCER_CC2E; + TIM1->DIER = TIM_DIER_CC2IE; + TIM1->CR1 = TIM_CR1_CEN; + NVIC_SetPriority(TIM1_CC_IRQn, 0); + NVIC_EnableIRQ(TIM1_CC_IRQn); + + // TIM3_CH2 is HSYNC and should trigger an interrupt, while TIM3_CH1 is the + // pixel clock and should trigger DMA transfers + TIM3->PSC = 0; + TIM3->ARR = 1; + TIM3->CCMR1 = TIM_CCMR1_CC2S_0 | TIM_CCMR1_CC1S_0 | TIM_CCMR1_IC1PSC_0; + TIM3->CCER = TIM_CCER_CC2P | TIM_CCER_CC2E | TIM_CCER_CC1E | TIM_CCER_CC1P; + TIM3->DIER = TIM_DIER_CC2IE; + TIM3->CR1 = TIM_CR1_CEN; + NVIC_SetPriority(TIM3_IRQn, 0); + NVIC_EnableIRQ(TIM3_IRQn); + + // Fetch GPIOA IDR lower byte + DMA1_Channel6->CMAR = (uint32_t)&(GPIOA->IDR); + + // Startup delay + for(volatile int i = 0; i < 1000; i++); + + // Camera configuration + ReadRegister(REG_PID); + + // Disable timing resets + WriteRegister(REG_COM6, 0x00); + + // Enable scaling + WriteRegister(REG_COM3, 0x08 | 0x04); + + // Use QCIF output format + WriteRegister(REG_COM7, 0x08); + + // Blank pixel clock during sync pulses + WriteRegister(REG_COM10, 0x20); + + // Set clock prescaler to 2 + WriteRegister(REG_CLKRC, 0x4 | 1); + + // Enable pixel clock scaling + WriteRegister(REG_COM14, 0x10); +} + +void TIM1_CC_IRQHandler(void) +{ + // VSYNC + + // GPIOC->BRR = (1 << PIN_LED); + + LineCount = CurrentLine; + CurrentLine = 0; + + // Dummy read + TIM1->CCR2; + TIM1->SR &= ~TIM_SR_CC2IF; + + // GPIOC->BSRR = (1 << PIN_LED); +} + +void TIM3_IRQHandler(void) +{ + // HSYNC + + GPIOC->BRR = (1 << PIN_LED); + TIM3->DIER &= ~TIM_DIER_CC1DE; + TIM3->SR &= ~TIM_SR_CC1IF; + + DMA1_Channel6->CCR = 0; + DMA1_Channel6->CNDTR = sizeof(LineBuffer1); + if(CurrentLine & 1) + { + DMA1_Channel6->CMAR = (uint32_t)LineBuffer1; + } + else + { + DMA1_Channel6->CMAR = (uint32_t)LineBuffer2; + } + DMA1_Channel6->CCR = DMA_CCR_PL | DMA_CCR_MINC | DMA_CCR_EN; + TIM3->DIER |= TIM_DIER_CC1DE; + + // ImageBuffer[0] = 0; + + CurrentLine++; + + // Dummy read + TIM3->CCR2; + TIM3->SR &= ~TIM_SR_CC2IF; + + GPIOC->BSRR = (1 << PIN_LED); +} \ No newline at end of file diff --git a/src/ov7670.h b/src/ov7670.h new file mode 100644 index 0000000..51a5114 --- /dev/null +++ b/src/ov7670.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include "stm32f1xx.h" + +void Camera_Init(void); \ No newline at end of file diff --git a/src/pinning.h b/src/pinning.h index b498218..7a1ad91 100755 --- a/src/pinning.h +++ b/src/pinning.h @@ -10,7 +10,7 @@ #define PIN_CAMERA_DB6 6 // PA6 - Camera data line 6 #define PIN_CAMERA_DB7 7 // PA7 - Camera data line 7 #define PIN_CAMERA_MCLK 8 // PA8 - Camera main clock (MCO/TIM1_CH1) -#define PIN_CAMERA_PCLK 9 // PA9 - Camera pixel clock (TIM1_CH2) +#define PIN_CAMERA_VSYNC 9 // PA9 - Camera VSYNC (TIM1_CH2) #define PIN_STEPPER_AP 10 // PA10 - Stepper phase A positive #define PIN_STEPPER_BP 11 // PA11 - Stepper phase B positive #define PIN_STEPPER_AM 12 // PA12 - Stepper phase A negative @@ -21,7 +21,7 @@ #define PIN_VBATT 1 // PB1 - Battery monitor (ADC12_IN9) #define PIN_SUPPLY 2 // PB2 - Voltage regulator enable #define PIN_PAPER 3 // PB3 - Paper detect -#define PIN_CAMERA_VSYNC 4 // PB4 - Camera VSYNC (TIM3_CH1) +#define PIN_CAMERA_PCLK 4 // PB4 - Camera pixel clock (TIM3_CH1) #define PIN_CAMERA_HSYNC 5 // PB5 - Camera VSYNC (TIM3_CH2) #define PIN_HEAD 6 // PB6 - Head up sensor #define PIN_PAPER_CUT 7 // PB7 - Paper cutter servo (TIM4_CH2)