inverse_thermal_camera/src/ov7670.c

369 lines
11 KiB
C
Raw Normal View History

#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
2018-08-23 12:49:29 +02:00
uint8_t ImageBuffer[CAMERA_IMAGE_WIDTH * CAMERA_IMAGE_HEIGHT / 8];
static volatile int CurrentLine = 0;
2018-08-23 13:27:08 +02:00
uint8_t LineBuffer[CAMERA_IMAGE_WIDTH + 40];
int LineCount = 0;
2018-08-23 12:30:47 +02:00
static int FrameCount = 0;
volatile int Camera_Captured = 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;
2018-08-23 13:27:08 +02:00
TIM3->CCMR1 = TIM_CCMR1_CC2S_0 | TIM_CCMR1_CC1S_0 | TIM_CCMR1_IC1PSC_1;
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
2018-08-23 12:30:47 +02:00
DMA1_Channel6->CPAR = (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);
2018-08-23 12:30:47 +02:00
// Set clock prescaler to 2
WriteRegister(REG_CLKRC, 0x4 | 1);
// Enable scaling
2018-08-23 12:30:47 +02:00
WriteRegister(REG_COM3, 0x08);
// Use QCIF output format
WriteRegister(REG_COM7, 0x08);
// Blank pixel clock during sync pulses
WriteRegister(REG_COM10, 0x20);
// Enable pixel clock scaling
2018-08-23 12:49:29 +02:00
WriteRegister(REG_COM14, 0x18 | 1);
WriteRegister(REG_SCALING_PCLK_DIV, 1);
}
void TIM1_CC_IRQHandler(void)
{
// VSYNC
// GPIOC->BRR = (1 << PIN_LED);
LineCount = CurrentLine;
CurrentLine = 0;
2018-08-23 12:30:47 +02:00
FrameCount++;
if(FrameCount == 10)
{
Camera_Captured = 1;
2018-08-23 13:56:51 +02:00
// Disable everything
TIM3->CR1 = 0;
TIM1->CR1 = 0;
2018-08-23 12:30:47 +02:00
}
// Dummy read
TIM1->CCR2;
TIM1->SR &= ~TIM_SR_CC2IF;
// GPIOC->BSRR = (1 << PIN_LED);
}
void TIM3_IRQHandler(void)
{
// HSYNC
TIM3->DIER &= ~TIM_DIER_CC1DE;
TIM3->SR &= ~TIM_SR_CC1IF;
DMA1_Channel6->CCR = 0;
2018-08-23 13:27:08 +02:00
DMA1_Channel6->CNDTR = sizeof(LineBuffer);
DMA1_Channel6->CMAR = (uint32_t)LineBuffer;
DMA1_Channel6->CCR = DMA_CCR_PL | DMA_CCR_MINC | DMA_CCR_EN;
TIM3->DIER |= TIM_DIER_CC1DE;
2018-08-23 13:56:51 +02:00
if(!Camera_Captured && (~CurrentLine & 1)
&& (CurrentLine / 2 < CAMERA_IMAGE_HEIGHT))
2018-08-23 12:30:47 +02:00
{
int error = 0;
2018-08-23 12:49:29 +02:00
for(int i = 0; i < CAMERA_IMAGE_WIDTH; i++)
2018-08-23 12:30:47 +02:00
{
2018-08-23 13:27:08 +02:00
int pixel = LineBuffer[i + 15] + error;
int line = CurrentLine / 2;
2018-08-23 12:30:47 +02:00
if(pixel < 127)
{
error = pixel;
2018-08-23 13:27:08 +02:00
ImageBuffer[(line * CAMERA_IMAGE_WIDTH + i) / 8] |=
2018-08-23 12:49:29 +02:00
0x80 >> (i % 8);
2018-08-23 12:30:47 +02:00
}
else
{
error = pixel - 255;
2018-08-23 13:27:08 +02:00
ImageBuffer[(line * CAMERA_IMAGE_WIDTH + i) / 8] &=
2018-08-23 12:49:29 +02:00
~(0x80 >> (i % 8));
2018-08-23 12:30:47 +02:00
}
}
}
CurrentLine++;
// Dummy read
TIM3->CCR2;
TIM3->SR &= ~TIM_SR_CC2IF;
}