Compare commits
5 commits
Author | SHA1 | Date | |
---|---|---|---|
|
59b2022e48 | ||
|
dc61ac7f85 | ||
|
3f6016106b | ||
|
e43202a113 | ||
|
d6932ae70c |
|
@ -1 +1 @@
|
|||
562
|
||||
641
|
||||
|
|
BIN
photo.jpg
BIN
photo.jpg
Binary file not shown.
Before ![]() (image error) Size: 59 KiB After ![]() (image error) Size: 65 KiB ![]() ![]() |
20
src/bmp.c
20
src/bmp.c
|
@ -64,7 +64,7 @@ void BMP_ConstructFilename(int number, char *buffer, unsigned int size)
|
|||
memcpy(buffer + size - 5, ".BMP", 5);
|
||||
}
|
||||
|
||||
void BMP_Save(uint8_t *data, int width, int height)
|
||||
int BMP_Save(uint8_t *data, int width, int height)
|
||||
{
|
||||
FATFS fs;
|
||||
FIL fp;
|
||||
|
@ -74,19 +74,19 @@ void BMP_Save(uint8_t *data, int width, int height)
|
|||
rc = f_mount(&fs, "", 0);
|
||||
if(rc)
|
||||
{
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int file_number = BMP_PickFileNumber();
|
||||
if(file_number == -1)
|
||||
return;
|
||||
return -2;
|
||||
char filename[13];
|
||||
BMP_ConstructFilename(file_number, filename, sizeof(filename));
|
||||
|
||||
rc = f_open(&fp, filename, FA_WRITE | FA_CREATE_NEW);
|
||||
if(rc)
|
||||
{
|
||||
return;
|
||||
return -3;
|
||||
}
|
||||
|
||||
// Rows must be padded to a multiple of 4 bytes
|
||||
|
@ -121,24 +121,26 @@ void BMP_Save(uint8_t *data, int width, int height)
|
|||
};
|
||||
|
||||
if(f_write(&fp, &file_header, sizeof(file_header), &bw) != FR_OK)
|
||||
return;
|
||||
return -4;
|
||||
if(f_write(&fp, &core_header, sizeof(core_header), &bw) != FR_OK)
|
||||
return;
|
||||
return -4;
|
||||
if(f_write(&fp, &colour_table, sizeof(colour_table), &bw) != FR_OK)
|
||||
return;
|
||||
return -4;
|
||||
|
||||
// BMPs are stored with their rows from the bottom upwards
|
||||
for(int y = height - 1; y >= 0; y--)
|
||||
{
|
||||
uint8_t *row = data + y * (width / 8);
|
||||
if(f_write(&fp, row, width / 8, &bw) != FR_OK)
|
||||
return;
|
||||
return -4;
|
||||
if(padding_length != 0)
|
||||
{
|
||||
if(f_write(&fp, padding_bytes, padding_length, &bw) != FR_OK)
|
||||
return;
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
|
||||
f_close(&fp);
|
||||
|
||||
return file_number;
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void BMP_Save(uint8_t *data, int width, int height);
|
||||
int BMP_Save(uint8_t *data, int width, int height);
|
||||
|
|
24
src/config.h
Normal file
24
src/config.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
// Use 2D Floyd-Steinberg dithering. Uncomment to use simple line-wise
|
||||
// dithering.
|
||||
#define CONFIG_USE_2D_DITHERING
|
||||
|
||||
// Use a primitive autoexposure by shifting each frames luminosity based on the
|
||||
// ratio of white to black pixels in the previous frames
|
||||
// #define CONFIG_USE_EXPOSURE_CORRECTION
|
||||
|
||||
// Capture a frame when it is within this tolerance of 50% black pixels
|
||||
#define CONFIG_TOLERANCE 0.2
|
||||
|
||||
// Wait this number of frames before a frame is eligible for beaing captured
|
||||
#define CONFIG_MIN_FRAMES 2
|
||||
|
||||
// Maximum number of frames to wait. Capture even if it is not within tolerance.
|
||||
#define CONFIG_MAX_FRAMES 15
|
||||
|
||||
// Print number of black pixels per captured frame
|
||||
// #define CONFIG_PRINT_EXPOSURE_DEBUG_INFORMATION
|
||||
|
||||
// Print file number if a file was saved to SD card
|
||||
#define CONFIG_PRINT_FILE_NUMBER
|
38
src/main.c
38
src/main.c
|
@ -14,18 +14,48 @@ int main(void)
|
|||
Camera_Init();
|
||||
LTP1245_Init();
|
||||
|
||||
LTP1245_FeedPaper(100);
|
||||
LTP1245_FeedPaper(10);
|
||||
LTP1245_FeedPaper(50);
|
||||
|
||||
while(!Camera_Captured);
|
||||
|
||||
extern uint8_t ImageBuffer[CAMERA_IMAGE_WIDTH * CAMERA_IMAGE_HEIGHT / 8];
|
||||
Print_Image(ImageBuffer, CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT, 2);
|
||||
|
||||
LTP1245_FeedPaper(100);
|
||||
#ifdef CONFIG_PRINT_EXPOSURE_DEBUG_INFORMATION
|
||||
char buffer[32] = "Finished after lines: ";
|
||||
itoa(Camera_FinalFrameCount, buffer + strlen(buffer), 10);
|
||||
|
||||
BMP_Save(ImageBuffer, CAMERA_IMAGE_WIDTH, CAMERA_IMAGE_HEIGHT);
|
||||
Print_Text(buffer, &Hannover_Messe_Serif_26, Print_LeftAligned);
|
||||
Print_Text("Black pixel counts:", &Hannover_Messe_Serif_26,
|
||||
Print_LeftAligned);
|
||||
for(int i = 0; i < Camera_FinalFrameCount; i++)
|
||||
{
|
||||
if(i >= (int)(sizeof(Camera_BlackPixelCounts)
|
||||
/ sizeof(Camera_BlackPixelCounts[0])))
|
||||
{
|
||||
break;
|
||||
}
|
||||
itoa(Camera_BlackPixelCounts[i], buffer, 10);
|
||||
Print_Text(buffer, &Hannover_Messe_Serif_26, Print_LeftAligned);
|
||||
}
|
||||
#endif
|
||||
|
||||
int file_number = BMP_Save(ImageBuffer, CAMERA_IMAGE_WIDTH,
|
||||
CAMERA_IMAGE_HEIGHT);
|
||||
(void)file_number;
|
||||
|
||||
#ifdef CONFIG_PRINT_FILE_NUMBER
|
||||
LTP1245_FeedPaper(10);
|
||||
char buffer[32] = "- ";
|
||||
if(file_number >= 0)
|
||||
{
|
||||
itoa(file_number, buffer + strlen(buffer), 10);
|
||||
strcpy(buffer + strlen(buffer), " -");
|
||||
Print_Text(buffer, &Hannover_Messe_Serif_26, Print_Centred);
|
||||
}
|
||||
#endif
|
||||
|
||||
LTP1245_FeedPaper(80);
|
||||
LTP1245_FeedPaper(10);
|
||||
|
||||
GPIOC->BRR = (1 << PIN_SUPPLY);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "font_arpegius_16.h"
|
||||
#include "font_arpegius_32.h"
|
||||
#include "bmp.h"
|
||||
#include "config.h"
|
||||
|
||||
int main(void);
|
||||
|
||||
|
|
61
src/ov7670.c
61
src/ov7670.c
|
@ -126,12 +126,12 @@
|
|||
#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_AECGMAX 0xa5
|
||||
#define REG_LPH 0xa6
|
||||
#define REG_UPL 0xa7
|
||||
#define REG_TPL 0xa8
|
||||
#define REG_TPH 0xa9
|
||||
#define REG_NALG 0xaa
|
||||
#define REG_BD60MAX 0xab
|
||||
#define REG_STR_OPT 0xac
|
||||
#define REG_STR_R 0xad
|
||||
|
@ -156,10 +156,13 @@ static volatile int FrameCount = 0;
|
|||
volatile int Camera_Captured = 0;
|
||||
static unsigned int BlackPixels = 0;
|
||||
|
||||
#ifdef CAMERA_USE_EXPOSURE_CORRECTION
|
||||
#ifdef CONFIG_USE_EXPOSURE_CORRECTION
|
||||
static volatile int ExposureCorrection = 0;
|
||||
#endif
|
||||
|
||||
volatile unsigned int Camera_BlackPixelCounts[] = {0};
|
||||
volatile int Camera_FinalFrameCount = -1;
|
||||
|
||||
static uint8_t ReadRegister(uint8_t reg)
|
||||
{
|
||||
while(I2C1->SR2 & I2C_SR2_BUSY);
|
||||
|
@ -301,6 +304,24 @@ void Camera_Init(void)
|
|||
WriteRegister(REG_COM14, 0x18 | 1);
|
||||
WriteRegister(REG_SCALING_PCLK_DIV, 1);
|
||||
|
||||
// Set maximum gain ceiling
|
||||
WriteRegister(REG_COM9, 0xca);
|
||||
|
||||
// Disable auto-exposure, enable auto-gain
|
||||
WriteRegister(REG_COM8, 0xec);
|
||||
|
||||
// // Disable auto-gain, enable auto-exposure
|
||||
// WriteRegister(REG_COM8, 0xe9);
|
||||
|
||||
// Use histogram-based auto-exposure
|
||||
WriteRegister(REG_NALG, 0x94);
|
||||
|
||||
// Increase AEC speed:
|
||||
// ‘Decrease TPL but not less than UPL [default 0xc1] and increase TPH but
|
||||
// not bigger than LPH [default 0xf0]’
|
||||
WriteRegister(REG_TPL, 0xc1);
|
||||
WriteRegister(REG_TPH, 0xf0);
|
||||
|
||||
// Enable VSYNC interrupts. TIM3 is enabled at the first VSYNC.
|
||||
FrameCount = 0;
|
||||
Camera_Captured = 0;
|
||||
|
@ -327,20 +348,34 @@ void TIM1_CC_IRQHandler(void)
|
|||
CurrentLine = 0;
|
||||
FrameCount++;
|
||||
|
||||
#ifdef CAMERA_USE_EXPOSURE_CORRECTION
|
||||
#ifdef CONFIG_USE_EXPOSURE_CORRECTION
|
||||
// Correct exposure across frames
|
||||
ExposureCorrection += 32 * BlackPixels / CAMERA_PIXELS - 16;
|
||||
#endif
|
||||
|
||||
// Check if the last frame's exposure is reasonable
|
||||
if(FrameCount >= 5)
|
||||
if(!Camera_Captured && FrameCount >= CONFIG_MIN_FRAMES
|
||||
&& (
|
||||
(BlackPixels > (int)(CAMERA_PIXELS / 2
|
||||
- CAMERA_PIXELS * CONFIG_TOLERANCE)
|
||||
&& BlackPixels < (int)(CAMERA_PIXELS / 2
|
||||
+ CAMERA_PIXELS * CONFIG_TOLERANCE))
|
||||
|| FrameCount >= CONFIG_MAX_FRAMES
|
||||
))
|
||||
{
|
||||
Camera_Captured = 1;
|
||||
Camera_FinalFrameCount = FrameCount;
|
||||
// Disable everything
|
||||
TIM3->CR1 = 0;
|
||||
TIM1->CR1 = 0;
|
||||
}
|
||||
|
||||
if(FrameCount < (int)(sizeof(Camera_BlackPixelCounts)
|
||||
/ sizeof(Camera_BlackPixelCounts[0])))
|
||||
{
|
||||
Camera_BlackPixelCounts[FrameCount] = BlackPixels;
|
||||
}
|
||||
|
||||
// Reset black pixel counter
|
||||
BlackPixels = 0;
|
||||
|
||||
|
@ -361,7 +396,7 @@ void TIM3_IRQHandler(void)
|
|||
DMA1_Channel6->CCR = DMA_CCR_PL | DMA_CCR_MINC | DMA_CCR_EN;
|
||||
TIM3->DIER |= TIM_DIER_CC1DE;
|
||||
|
||||
#ifdef CAMERA_USE_2D_DITHERING
|
||||
#ifdef CONFIG_USE_2D_DITHERING
|
||||
static int8_t y_errors[CAMERA_IMAGE_WIDTH + 2] = {0};
|
||||
|
||||
if(CurrentLine == 0)
|
||||
|
@ -374,7 +409,7 @@ void TIM3_IRQHandler(void)
|
|||
&& (CurrentLine / 2 < CAMERA_IMAGE_HEIGHT))
|
||||
{
|
||||
|
||||
#ifdef CAMERA_USE_2D_DITHERING
|
||||
#ifdef CONFIG_USE_2D_DITHERING
|
||||
// Apply errors propagated from the previous line. Since y_errors is
|
||||
// overwritten during x error diffusion, this is done now.
|
||||
for(int i = 0; i < CAMERA_IMAGE_WIDTH; i++)
|
||||
|
@ -388,7 +423,7 @@ void TIM3_IRQHandler(void)
|
|||
for(int i = 0; i < CAMERA_IMAGE_WIDTH; i++)
|
||||
{
|
||||
int pixel = LineBuffer[i + 15] + x_error;
|
||||
#ifdef CAMERA_USE_EXPOSURE_CORRECTION
|
||||
#ifdef CONFIG_USE_EXPOSURE_CORRECTION
|
||||
if(ExposureCorrection < 0)
|
||||
{
|
||||
if(pixel < -ExposureCorrection)
|
||||
|
@ -421,7 +456,7 @@ void TIM3_IRQHandler(void)
|
|||
~(0x80 >> (i % 8));
|
||||
}
|
||||
|
||||
#ifdef CAMERA_USE_2D_DITHERING
|
||||
#ifdef CONFIG_USE_2D_DITHERING
|
||||
// Error propagated to the next pixel in the same line
|
||||
x_error = error * 7 / 16;
|
||||
|
||||
|
|
11
src/ov7670.h
11
src/ov7670.h
|
@ -2,19 +2,16 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include "stm32f1xx.h"
|
||||
#include "config.h"
|
||||
|
||||
#define CAMERA_IMAGE_WIDTH 160
|
||||
#define CAMERA_IMAGE_HEIGHT 144
|
||||
#define CAMERA_PIXELS (CAMERA_IMAGE_WIDTH * CAMERA_IMAGE_HEIGHT)
|
||||
|
||||
// Use 2D Floyd-Steinberg dithering. Uncomment to use simple line-wise
|
||||
// dithering.
|
||||
#define CAMERA_USE_2D_DITHERING
|
||||
|
||||
// Use a primitive autoexposure by shifting each frames luminosity based on the
|
||||
// ratio of white to black pixels in the previous frames
|
||||
// #define CAMERA_USE_EXPOSURE_CORRECTION
|
||||
|
||||
extern volatile int Camera_Captured;
|
||||
|
||||
extern volatile unsigned int Camera_BlackPixelCounts[CONFIG_MAX_FRAMES];
|
||||
extern volatile int Camera_FinalFrameCount;
|
||||
|
||||
void Camera_Init(void);
|
35
src/print.c
35
src/print.c
|
@ -3,12 +3,45 @@
|
|||
|
||||
static uint8_t Print_Buffer[PRINT_BUFFER_LINES * LTP1245_LINE_BYTES];
|
||||
|
||||
void Print_Text(const char *text, const Font_t *font)
|
||||
int Print_MeasureTextWidth(const char *text, const Font_t *font)
|
||||
{
|
||||
char c;
|
||||
int width = 0;
|
||||
while((c = *text++) != 0)
|
||||
{
|
||||
if(c > font->glyphcount + font->charoffset
|
||||
|| c < font->charoffset)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
width += font->glyphs[c - font->charoffset].width;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
void Print_Text(const char *text, const Font_t *font,
|
||||
Print_TextAlignment_t alignment)
|
||||
{
|
||||
int height = font->height;
|
||||
memset(Print_Buffer, 0, LTP1245_LINE_BYTES * height);
|
||||
char c;
|
||||
int xpos = 0;
|
||||
if(alignment != Print_LeftAligned)
|
||||
{
|
||||
int width = Print_MeasureTextWidth(text, font);
|
||||
if(width < LTP1245_LINEWIDTH)
|
||||
{
|
||||
if(alignment == Print_Centred)
|
||||
{
|
||||
xpos = LTP1245_LINEWIDTH / 2 - width / 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
xpos = LTP1245_LINEWIDTH - width;
|
||||
}
|
||||
}
|
||||
}
|
||||
while((c = *text++) != 0)
|
||||
{
|
||||
if(c > font->glyphcount + font->charoffset
|
||||
|
|
10
src/print.h
10
src/print.h
|
@ -7,5 +7,13 @@
|
|||
|
||||
#define PRINT_BUFFER_LINES 64
|
||||
|
||||
void Print_Text(const char *text, const Font_t *font);
|
||||
typedef enum
|
||||
{
|
||||
Print_LeftAligned,
|
||||
Print_Centred,
|
||||
Print_RightAligned,
|
||||
} Print_TextAlignment_t;
|
||||
|
||||
void Print_Text(const char *text, const Font_t *font,
|
||||
Print_TextAlignment_t alignment);
|
||||
void Print_Image(const uint8_t *data, int width, int height, int scale);
|
Loading…
Reference in a new issue