Skip to content

Commit 8de4b62

Browse files
committed
gc0308: add proper support for sensor image and effect settings
This includes contrast, brightness, saturation, sharpness, white-balance, gain & exposure levels and special effects.
1 parent b3c2aa2 commit 8de4b62

File tree

2 files changed

+395
-29
lines changed

2 files changed

+395
-29
lines changed

sensors/gc0308.c

Lines changed: 274 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -277,32 +277,131 @@ static int set_framesize(sensor_t *sensor, framesize_t framesize)
277277
return 0;
278278
}
279279

280-
static int set_contrast(sensor_t *sensor, int contrast)
280+
static int set_contrast(sensor_t *sensor, int level)
281281
{
282-
if (contrast > 0) {
283-
sensor->status.contrast = contrast;
284-
write_reg(sensor->slv_addr, 0xfe, 0x00);
285-
write_reg(sensor->slv_addr, 0xb3, contrast);
282+
int ret = 0;
283+
// GC0308 contrast range: -2 to +2 (mapped to register values)
284+
if (level < -2 || level > 2) {
285+
return -1;
286286
}
287-
return 0;
287+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
288+
// Adjust contrast (CONTRAST register)
289+
// Default value is 0x40, adjust based on level
290+
uint8_t contrast_val = 0x40 + (level * 0x10);
291+
ret |= write_reg(sensor->slv_addr, CONTRAST, contrast_val);
292+
if (ret == 0) {
293+
sensor->status.contrast = level;
294+
ESP_LOGD(TAG, "Set contrast to: %d", level);
295+
}
296+
return ret;
288297
}
289298

290-
static int set_global_gain(sensor_t *sensor, int gain_level)
299+
static int set_brightness(sensor_t *sensor, int level)
291300
{
292-
if (gain_level != 0) {
293-
write_reg(sensor->slv_addr, 0xfe, 0x00);
294-
write_reg(sensor->slv_addr, 0x50, gain_level);
301+
int ret = 0;
302+
// GC0308 brightness range: -2 to +2 (mapped to register values)
303+
if (level < -2 || level > 2) {
304+
return -1;
295305
}
296-
return 0;
306+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
307+
// Adjust brightness via LSC target Y base (LSC_RED_B2)
308+
// Default value is 0x48, adjust based on level
309+
uint8_t brightness_val = 0x48 + (level * 0x10);
310+
ret |= write_reg(sensor->slv_addr, LSC_RED_B2, brightness_val);
311+
if (ret == 0) {
312+
sensor->status.brightness = level;
313+
ESP_LOGD(TAG, "Set brightness to: %d", level);
314+
}
315+
return ret;
316+
}
317+
318+
static int set_saturation(sensor_t *sensor, int level)
319+
{
320+
int ret = 0;
321+
// GC0308 saturation range: -2 to +2
322+
if (level < -2 || level > 2) {
323+
return -1;
324+
}
325+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
326+
// Adjust saturation via Cb and Cr gain (SATURATION_CB1, SATURATION_CR1)
327+
// Default values: SATURATION_CB=0xcb, SATURATION_CB1=0x10, SATURATION_CR1=0x90
328+
uint8_t sat_base = 0x40 + (level * 0x10);
329+
ret |= write_reg(sensor->slv_addr, SATURATION_CB1, sat_base);
330+
ret |= write_reg(sensor->slv_addr, SATURATION_CR1, sat_base + 0x50);
331+
if (ret == 0) {
332+
sensor->status.saturation = level;
333+
ESP_LOGD(TAG, "Set saturation to: %d", level);
334+
}
335+
return ret;
336+
}
337+
338+
static int set_sharpness(sensor_t *sensor, int level)
339+
{
340+
int ret = 0;
341+
// GC0308 sharpness range: -2 to +2
342+
if (level < -2 || level > 2) {
343+
return -1;
344+
}
345+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
346+
// Adjust sharpness via edge enhancement (EDGE_DEC_SA1-SA3)
347+
// Lower values = less sharp, higher values = more sharp
348+
uint8_t sharp_val = 0x20 + (level * 0x08);
349+
ret |= write_reg(sensor->slv_addr, EDGE_DEC_SA1, sharp_val);
350+
ret |= write_reg(sensor->slv_addr, EDGE_DEC_SA2, sharp_val);
351+
ret |= write_reg(sensor->slv_addr, EDGE_DEC_SA3, sharp_val);
352+
if (ret == 0) {
353+
sensor->status.sharpness = level;
354+
ESP_LOGD(TAG, "Set sharpness to: %d", level);
355+
}
356+
return ret;
357+
}
358+
359+
static int set_whitebal(sensor_t *sensor, int enable)
360+
{
361+
int ret = 0;
362+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
363+
// AWB enable/disable via AEC_MODE1 register, AWB_ENABLE bit
364+
ret |= set_reg_bits(sensor->slv_addr, AEC_MODE1, 1, 0x01, enable != 0);
365+
if (ret == 0) {
366+
sensor->status.awb = enable;
367+
ESP_LOGD(TAG, "Set AWB to: %d", enable);
368+
}
369+
return ret;
370+
}
371+
372+
static int set_gain_ctrl(sensor_t *sensor, int enable)
373+
{
374+
int ret = 0;
375+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
376+
// AGC enable/disable via AEC_MODE1 register, AGC_ENABLE bit
377+
ret |= set_reg_bits(sensor->slv_addr, AEC_MODE1, 2, 0x01, enable != 0);
378+
if (ret == 0) {
379+
sensor->status.agc = enable;
380+
ESP_LOGD(TAG, "Set AGC to: %d", enable);
381+
}
382+
return ret;
383+
}
384+
385+
static int set_exposure_ctrl(sensor_t *sensor, int enable)
386+
{
387+
int ret = 0;
388+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
389+
// AEC enable/disable via AEC_MODE1 register, AEC_ENABLE bit
390+
ret |= set_reg_bits(sensor->slv_addr, AEC_MODE1, 0, 0x01, enable != 0);
391+
if (ret == 0) {
392+
sensor->status.aec = enable;
393+
ESP_LOGD(TAG, "Set AEC to: %d", enable);
394+
}
395+
return ret;
297396
}
298397

299398
static int set_hmirror(sensor_t *sensor, int enable)
300399
{
301400
int ret = 0;
302-
sensor->status.hmirror = enable;
303401
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
304-
ret |= set_reg_bits(sensor->slv_addr, 0x14, 0, 0x01, enable != 0);
402+
ret |= set_reg_bits(sensor->slv_addr, CISCTL_MODE1, 0, 0x01, enable != 0);
305403
if (ret == 0) {
404+
sensor->status.hmirror = enable;
306405
ESP_LOGD(TAG, "Set h-mirror to: %d", enable);
307406
}
308407
return ret;
@@ -311,27 +410,176 @@ static int set_hmirror(sensor_t *sensor, int enable)
311410
static int set_vflip(sensor_t *sensor, int enable)
312411
{
313412
int ret = 0;
314-
sensor->status.vflip = enable;
315413
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
316-
ret |= set_reg_bits(sensor->slv_addr, 0x14, 1, 0x01, enable != 0);
414+
ret |= set_reg_bits(sensor->slv_addr, CISCTL_MODE1, 1, 0x01, enable != 0);
317415
if (ret == 0) {
416+
sensor->status.vflip = enable;
318417
ESP_LOGD(TAG, "Set v-flip to: %d", enable);
319418
}
320419
return ret;
321420
}
322421

422+
423+
static int set_agc_gain(sensor_t *sensor, int gain)
424+
{
425+
int ret = 0;
426+
// GC0308 AGC gain range: 0-30 (standard sensor API range)
427+
// Maps to hardware register values 0x00-0x3F (6-bit effective range)
428+
// Hardware default is 0x14 (20)
429+
if (gain < 0) {
430+
gain = 0;
431+
} else if (gain > 30) {
432+
gain = 30;
433+
}
434+
// Map API range 0-30 to hardware range 0-63 (approximately 2x multiplier)
435+
uint8_t gain_value = (gain * 63) / 30;
436+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
437+
ret |= write_reg(sensor->slv_addr, GLOBAL_GAIN, gain_value);
438+
if (ret == 0) {
439+
sensor->status.agc_gain = gain;
440+
ESP_LOGD(TAG, "Set AGC gain to: %d (hw: 0x%02x)", gain, gain_value);
441+
}
442+
return ret;
443+
}
444+
323445
static int set_colorbar(sensor_t *sensor, int enable)
324446
{
325447
int ret = 0;
326448
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
327-
ret |= set_reg_bits(sensor->slv_addr, 0x2e, 0, 0x01, enable);
449+
ret |= set_reg_bits(sensor->slv_addr, OUT_CTRL, 0, 0x01, enable);
328450
if (ret == 0) {
329451
sensor->status.colorbar = enable;
330452
ESP_LOGD(TAG, "Set colorbar to: %d", enable);
331453
}
332454
return ret;
333455
}
334456

457+
static int set_special_effect(sensor_t *sensor, int effect)
458+
{
459+
int ret = 0;
460+
// Effect values: 0=Normal, 1=Negative, 2=Grayscale, 3=Red Tint, 4=Green Tint, 5=Blue Tint, 6=Sepia
461+
if (effect < 0 || effect > 6) {
462+
return -1;
463+
}
464+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
465+
switch (effect) {
466+
case 0: // Normal
467+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_NORMAL);
468+
ret |= write_reg(sensor->slv_addr, EFFECT_MODE, 0x0a);
469+
ret |= write_reg(sensor->slv_addr, FIXED_CB, 0x7f);
470+
ret |= write_reg(sensor->slv_addr, FIXED_CR, 0xfa);
471+
break;
472+
case 1: // Negative
473+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_NEGATIVE);
474+
break;
475+
case 2: // Grayscale (B&W)
476+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_GRAYSCALE);
477+
ret |= write_reg(sensor->slv_addr, EFFECT_MODE, 0x0a);
478+
ret |= write_reg(sensor->slv_addr, FIXED_CB, 0x7f);
479+
ret |= write_reg(sensor->slv_addr, FIXED_CR, 0xfa);
480+
break;
481+
case 3: // Red Tint
482+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_GRAYSCALE);
483+
ret |= write_reg(sensor->slv_addr, EFFECT_MODE, 0x0a);
484+
ret |= write_reg(sensor->slv_addr, FIXED_CB, 0x80);
485+
ret |= write_reg(sensor->slv_addr, FIXED_CR, 0xc0);
486+
break;
487+
case 4: // Green Tint
488+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_GRAYSCALE);
489+
ret |= write_reg(sensor->slv_addr, EFFECT_MODE, 0x0a);
490+
ret |= write_reg(sensor->slv_addr, FIXED_CB, 0x50);
491+
ret |= write_reg(sensor->slv_addr, FIXED_CR, 0x50);
492+
break;
493+
case 5: // Blue Tint
494+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_GRAYSCALE);
495+
ret |= write_reg(sensor->slv_addr, EFFECT_MODE, 0x0a);
496+
ret |= write_reg(sensor->slv_addr, FIXED_CB, 0xa0);
497+
ret |= write_reg(sensor->slv_addr, FIXED_CR, 0x80);
498+
break;
499+
case 6: // Sepia
500+
ret |= set_reg_bits(sensor->slv_addr, SPECIAL_EFFECT, 0, 0x03, EFFECT_GRAYSCALE);
501+
ret |= write_reg(sensor->slv_addr, EFFECT_MODE, 0x0a);
502+
ret |= write_reg(sensor->slv_addr, FIXED_CB, 0xd0);
503+
ret |= write_reg(sensor->slv_addr, FIXED_CR, 0x28);
504+
break;
505+
default:
506+
ret = -1;
507+
break;
508+
}
509+
if (ret == 0) {
510+
sensor->status.special_effect = effect;
511+
ESP_LOGD(TAG, "Set special effect to: %d", effect);
512+
}
513+
return ret;
514+
}
515+
516+
static int set_wb_mode(sensor_t *sensor, int mode)
517+
{
518+
int ret = 0;
519+
// WB modes: 0=Auto, 1=Sunny, 2=Cloudy, 3=Office, 4=Home
520+
if (mode < 0 || mode > 4) {
521+
return -1;
522+
}
523+
ret = write_reg(sensor->slv_addr, 0xfe, 0x00);
524+
if (mode == 0) {
525+
// Auto WB - enable AWB via AEC_MODE1
526+
ret |= set_reg_bits(sensor->slv_addr, AEC_MODE1, 1, 0x01, 1);
527+
} else {
528+
// Manual WB - disable AWB and set specific gains
529+
ret |= set_reg_bits(sensor->slv_addr, AEC_MODE1, 1, 0x01, 0);
530+
switch (mode) {
531+
case 1: // Sunny
532+
ret |= write_reg(sensor->slv_addr, AWB_R_GAIN, 0x74);
533+
ret |= write_reg(sensor->slv_addr, AWB_G_GAIN, 0x52);
534+
ret |= write_reg(sensor->slv_addr, AWB_B_GAIN, 0x40);
535+
break;
536+
case 2: // Cloudy
537+
ret |= write_reg(sensor->slv_addr, AWB_R_GAIN, 0x8c);
538+
ret |= write_reg(sensor->slv_addr, AWB_G_GAIN, 0x50);
539+
ret |= write_reg(sensor->slv_addr, AWB_B_GAIN, 0x40);
540+
break;
541+
case 3: // Office
542+
ret |= write_reg(sensor->slv_addr, AWB_R_GAIN, 0x48);
543+
ret |= write_reg(sensor->slv_addr, AWB_G_GAIN, 0x40);
544+
ret |= write_reg(sensor->slv_addr, AWB_B_GAIN, 0x5c);
545+
break;
546+
case 4: // Home
547+
ret |= write_reg(sensor->slv_addr, AWB_R_GAIN, 0x40);
548+
ret |= write_reg(sensor->slv_addr, AWB_G_GAIN, 0x54);
549+
ret |= write_reg(sensor->slv_addr, AWB_B_GAIN, 0x70);
550+
break;
551+
default:
552+
ret = -1;
553+
break;
554+
}
555+
}
556+
if (ret == 0) {
557+
sensor->status.wb_mode = mode;
558+
ESP_LOGD(TAG, "Set WB mode to: %d", mode);
559+
}
560+
return ret;
561+
}
562+
563+
static int set_ae_level(sensor_t *sensor, int level)
564+
{
565+
int ret = 0;
566+
// AE level range: -2 to +2
567+
if (level < -2 || level > 2) {
568+
return -1;
569+
}
570+
ret = write_reg(sensor->slv_addr, 0xfe, 0x01);
571+
// Adjust AE target via AEC_TARGET_Y register (Page 1)
572+
// Default is around 0x19, adjust based on level
573+
uint8_t ae_target = 0x19 + (level * 0x08);
574+
ret |= write_reg(sensor->slv_addr, AEC_TARGET_Y, ae_target);
575+
ret |= write_reg(sensor->slv_addr, 0xfe, 0x00);
576+
if (ret == 0) {
577+
sensor->status.ae_level = level;
578+
ESP_LOGD(TAG, "Set AE level to: %d", level);
579+
}
580+
return ret;
581+
}
582+
335583
static int get_reg(sensor_t *sensor, int reg, int mask)
336584
{
337585
int ret = 0;
@@ -433,27 +681,27 @@ int gc0308_init(sensor_t *sensor)
433681
sensor->set_pixformat = set_pixformat;
434682
sensor->set_framesize = set_framesize;
435683
sensor->set_contrast = set_contrast;
436-
sensor->set_brightness = set_dummy;
437-
sensor->set_saturation = set_dummy;
438-
sensor->set_sharpness = set_dummy;
684+
sensor->set_brightness = set_brightness;
685+
sensor->set_saturation = set_saturation;
686+
sensor->set_sharpness = set_sharpness;
439687
sensor->set_denoise = set_dummy;
440688
sensor->set_gainceiling = set_gainceiling_dummy;
441689
sensor->set_quality = set_dummy;
442690
sensor->set_colorbar = set_colorbar;
443-
sensor->set_whitebal = set_dummy;
444-
sensor->set_gain_ctrl = set_global_gain;
445-
sensor->set_exposure_ctrl = set_dummy;
691+
sensor->set_whitebal = set_whitebal;
692+
sensor->set_gain_ctrl = set_gain_ctrl;
693+
sensor->set_exposure_ctrl = set_exposure_ctrl;
446694
sensor->set_hmirror = set_hmirror;
447695
sensor->set_vflip = set_vflip;
448696

449697
sensor->set_aec2 = set_dummy;
450698
sensor->set_awb_gain = set_dummy;
451-
sensor->set_agc_gain = set_dummy;
699+
sensor->set_agc_gain = set_agc_gain;
452700
sensor->set_aec_value = set_dummy;
453701

454-
sensor->set_special_effect = set_dummy;
455-
sensor->set_wb_mode = set_dummy;
456-
sensor->set_ae_level = set_dummy;
702+
sensor->set_special_effect = set_special_effect;
703+
sensor->set_wb_mode = set_wb_mode;
704+
sensor->set_ae_level = set_ae_level;
457705

458706
sensor->set_dcw = set_dummy;
459707
sensor->set_bpc = set_dummy;

0 commit comments

Comments
 (0)