|
286 | 286 | # Bit[5:0]: QS
|
287 | 287 | _COMPRESSION_ISI_CTRL = const(0x4408)
|
288 | 288 | _COMPRESSION_CTRL09 = const(0x4409)
|
289 |
| -_COMPRESSION_CTRL0a = const(0x440A) |
290 |
| -_COMPRESSION_CTRL0b = const(0x440B) |
291 |
| -_COMPRESSION_CTRL0c = const(0x440C) |
292 |
| -_COMPRESSION_CTRL0d = const(0x440D) |
| 289 | +_COMPRESSION_CTRL0A = const(0x440A) |
| 290 | +_COMPRESSION_CTRL0B = const(0x440B) |
| 291 | +_COMPRESSION_CTRL0C = const(0x440C) |
| 292 | +_COMPRESSION_CTRL0D = const(0x440D) |
293 | 293 | _COMPRESSION_CTRL0E = const(0x440E)
|
294 | 294 |
|
295 | 295 | _TEST_COLOR_BAR = const(0xC0)
|
|
568 | 568 | OV5640_COLOR_JPEG: _sensor_format_jpeg,
|
569 | 569 | }
|
570 | 570 |
|
| 571 | +_contrast_settings = [ |
| 572 | + [0x20, 0x00], # 0 |
| 573 | + [0x24, 0x10], # +1 |
| 574 | + [0x1a, 0x13], # +2 |
| 575 | + [0x2c, 0x1c], # +3 |
| 576 | + [0x14, 0x14], # -3 |
| 577 | + [0x18, 0x18], # -2 |
| 578 | + [0x1c, 0x1c], # -1 |
| 579 | +] |
| 580 | + |
571 | 581 | _sensor_saturation_levels = [
|
572 | 582 | [0x1D, 0x60, 0x03, 0x0C, 0x78, 0x84, 0x7D, 0x6B, 0x12, 0x01, 0x98], # 0
|
573 | 583 | [0x1D, 0x60, 0x03, 0x0D, 0x84, 0x91, 0x8A, 0x76, 0x14, 0x01, 0x98], # +1
|
|
580 | 590 | [0x1D, 0x60, 0x03, 0x0B, 0x6C, 0x77, 0x70, 0x60, 0x10, 0x01, 0x98], # -1
|
581 | 591 | ]
|
582 | 592 |
|
| 593 | +_sensor_ev_levels = [ |
| 594 | + [0x38, 0x30, 0x61, 0x38, 0x30, 0x10], # 0 |
| 595 | + [0x40, 0x38, 0x71, 0x40, 0x38, 0x10], # +1 |
| 596 | + [0x50, 0x48, 0x90, 0x50, 0x48, 0x20], # +2 |
| 597 | + [0x60, 0x58, 0xa0, 0x60, 0x58, 0x20], # +3 |
| 598 | + [0x10, 0x08, 0x10, 0x08, 0x20, 0x10], # -3 |
| 599 | + [0x20, 0x18, 0x41, 0x20, 0x18, 0x10], # -2 |
| 600 | + [0x30, 0x28, 0x61, 0x30, 0x28, 0x10], # -1 |
| 601 | +] |
| 602 | + |
| 603 | +OV5640_WHITE_BALANCE_AUTO = 0 |
| 604 | +OV5640_WHITE_BALANCE_SUNNY = 1 |
| 605 | +OV5640_WHITE_BALANCE_FLUORESCENT = 2 |
| 606 | +OV5640_WHITE_BALANCE_CLOUDY = 3 |
| 607 | +OV5640_WHITE_BALANCE_INCANDESCENT = 4 |
| 608 | + |
| 609 | +_light_registers = [0x3406, 0x3400, 0x3401, 0x3402, 0x3403, 0x3404, 0x3405] |
| 610 | +_light_modes = [ |
| 611 | + [0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00], # auto |
| 612 | + [0x01, 0x06, 0x1c, 0x04, 0x00, 0x04, 0xf3], # sunny |
| 613 | + [0x01, 0x05, 0x48, 0x04, 0x00, 0x07, 0xcf], # office / fluorescent |
| 614 | + [0x01, 0x06, 0x48, 0x04, 0x00, 0x04, 0xd3], # cloudy |
| 615 | + [0x01, 0x04, 0x10, 0x04, 0x00, 0x08, 0x40], # home / incandescent |
| 616 | + |
| 617 | +] |
| 618 | + |
583 | 619 | OV5640_SPECIAL_EFFECT_NONE = 0
|
584 | 620 | OV5640_SPECIAL_EFFECT_NEGATIVE = 1
|
585 | 621 | OV5640_SPECIAL_EFFECT_GRAYSCALE = 2
|
@@ -849,6 +885,8 @@ def __init__(
|
849 | 885 | self._test_pattern = False
|
850 | 886 | self._binning = False
|
851 | 887 | self._scale = False
|
| 888 | + self._ev = 0 |
| 889 | + self._white_balance = 0 |
852 | 890 | self.size = size
|
853 | 891 |
|
854 | 892 | chip_id = _RegBits16(_CHIP_ID_HIGH, 0, 0xFFFF)
|
@@ -1100,6 +1138,10 @@ def saturation(self):
|
1100 | 1138 |
|
1101 | 1139 | @saturation.setter
|
1102 | 1140 | def saturation(self, value):
|
| 1141 | + if not -4 <= value <= 4: |
| 1142 | + raise ValueError( |
| 1143 | + "Invalid saturation {value}, use a value from -4..4 inclusive" |
| 1144 | + ) |
1103 | 1145 | for offset, reg_value in enumerate(_sensor_saturation_levels[value]):
|
1104 | 1146 | self._write_register(0x5381 + offset, reg_value)
|
1105 | 1147 | self._saturation = value
|
@@ -1129,3 +1171,87 @@ def quality(self, value: int):
|
1129 | 1171 | f"Invalid quality value {value}, use a value from 5..55 inclusive"
|
1130 | 1172 | )
|
1131 | 1173 | self._write_register(_COMPRESSION_CTRL07, value & 0x3F)
|
| 1174 | + |
| 1175 | + def _write_group_3_settings(self, settings): |
| 1176 | + self._write_register(0x3212, 0x3) # start group 3 |
| 1177 | + self._write_list(settings) |
| 1178 | + self._write_register(0x3212, 0x13) # end group 3 |
| 1179 | + self._write_register(0x3212, 0xA3) # launch group 3 |
| 1180 | + |
| 1181 | + @property |
| 1182 | + def brightness(self): |
| 1183 | + """Sensor brightness adjustment, from -4 to 4 inclusive""" |
| 1184 | + brightness_abs = self._read_register(0x5587) >> 4 |
| 1185 | + brightness_neg = self._read_register(0x5588) & 8 |
| 1186 | + if brightness_neg: |
| 1187 | + return -brightness_abs |
| 1188 | + return brightness_abs |
| 1189 | + |
| 1190 | + @brightness.setter |
| 1191 | + def brightness(self, value): |
| 1192 | + if not -4 <= value <= 4: |
| 1193 | + raise ValueError( |
| 1194 | + "Invalid brightness value {value}, use a value from -4..4 inclusive" |
| 1195 | + ) |
| 1196 | + self._write_group_3_settings( |
| 1197 | + [0x5587, abs(value) << 4, 0x9 if value < 0 else 0x1] |
| 1198 | + ) |
| 1199 | + |
| 1200 | + @property |
| 1201 | + def contrast(self): |
| 1202 | + """Sensor contrast adjustment, from -4 to 4 inclusive""" |
| 1203 | + contrast_abs = self._read_register(0x5587) >> 4 |
| 1204 | + contrast_neg = self._read_register(0x5588) & 8 |
| 1205 | + if contrast_neg: |
| 1206 | + return -contrast_abs |
| 1207 | + return contrast_abs |
| 1208 | + |
| 1209 | + @contrast.setter |
| 1210 | + def contrast(self, value): |
| 1211 | + if not -3 <= value <= 3: |
| 1212 | + raise ValueError( |
| 1213 | + "Invalid contrast value {value}, use a value from -3..3 inclusive" |
| 1214 | + ) |
| 1215 | + setting = _contrast_settings[value] |
| 1216 | + self._write_group_3_settings([0x5586, setting[0], 0x5585, setting[1]]) |
| 1217 | + |
| 1218 | + @property |
| 1219 | + def exposure_value(self): |
| 1220 | + """Sensor exposure (EV) adjustment, from -4 to 4 inclusive""" |
| 1221 | + return self._ev |
| 1222 | + |
| 1223 | + @exposure_value.setter |
| 1224 | + def exposure_value(self, value): |
| 1225 | + if not -3 <= value <= 3: |
| 1226 | + raise ValueError( |
| 1227 | + "Invalid exposure value (EV) {value}, use a value from -4..4 inclusive" |
| 1228 | + ) |
| 1229 | + for offset, reg_value in enumerate(_sensor_ev_levels[value]): |
| 1230 | + self._write_register(0x5381 + offset, reg_value) |
| 1231 | + |
| 1232 | + @property |
| 1233 | + def white_balance(self): |
| 1234 | + """The white balance setting, one of the ``OV5640_WHITE_BALANCE_*`` constants""" |
| 1235 | + return self._white_balance |
| 1236 | + |
| 1237 | + @white_balance.setter |
| 1238 | + def white_balance(self, value): |
| 1239 | + if not OV5640_WHITE_BALANCE_AUTO <= value <= OV5640_WHITE_BALANCE_INCANDESCENT: |
| 1240 | + raise ValueError( |
| 1241 | + "Invalid exposure value (EV) {value}, " |
| 1242 | + "use one of the OV5640_WHITE_BALANCE_* constants" |
| 1243 | + ) |
| 1244 | + self._write_register(0x3212, 0x3) # start group 3 |
| 1245 | + for reg_addr, reg_value in zip(_light_registers, _light_modes[value]): |
| 1246 | + self._write_register(reg_addr, reg_value) |
| 1247 | + self._write_register(0x3212, 0x13) # end group 3 |
| 1248 | + self._write_register(0x3212, 0xA3) # launch group 3 |
| 1249 | + |
| 1250 | + @property |
| 1251 | + def night_mode(self): |
| 1252 | + """Enable or disable the night mode setting of the sensor""" |
| 1253 | + return bool(self._read_register(0x3A00) & 0x04) |
| 1254 | + |
| 1255 | + @night_mode.setter |
| 1256 | + def night_mode(self, value): |
| 1257 | + self._write_reg_bits(0x3A00, 0x04, value) |
0 commit comments