Skip to content

Commit

Permalink
Merge pull request #28 from FoamyGuy/type_annotations
Browse files Browse the repository at this point in the history
adding type annotations
  • Loading branch information
FoamyGuy authored Dec 23, 2024
2 parents c157492 + 67117e8 commit fe63034
Showing 1 changed file with 65 additions and 50 deletions.
115 changes: 65 additions & 50 deletions adafruit_ov2640.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@

from micropython import const

try:
from typing import Optional, Union, Type, List
from busio import I2C
from microcontroller import Pin
from circuitpython_typing import WriteableBuffer
except ImportError:
pass

CTRLI = const(0x50)
_R_BYPASS = const(0x05)
_QS = const(0x44)
Expand Down Expand Up @@ -954,17 +962,17 @@


class _RegBits:
def __init__(self, bank, reg, shift, mask):
def __init__(self, bank: int, reg: int, shift: int, mask: int) -> None:
self.bank = bank
self.reg = reg
self.shift = shift
self.mask = mask

def __get__(self, obj, objtype=None):
def __get__(self, obj: "_SCCBCameraBase", objtype: Optional[Type] = None) -> int:
reg_value = obj._read_bank_register(self.bank, self.reg)
return (reg_value >> self.shift) & self.mask

def __set__(self, obj, value):
def __set__(self, obj: "_SCCBCameraBase", value: Union[bool, int]) -> None:
if value & ~self.mask:
raise ValueError(
f"Value 0x{value:02x} does not fit in mask 0x{self.mask:02x}"
Expand All @@ -976,38 +984,38 @@ def __set__(self, obj, value):


class _SCCBCameraBase: # pylint: disable=too-few-public-methods
def __init__(self, i2c_bus, i2c_address):
def __init__(self, i2c_bus: I2C, i2c_address: int) -> None:
self._i2c_device = I2CDevice(i2c_bus, i2c_address)
self._bank = None

def _get_reg_bits(self, bank, reg, shift, mask):
def _get_reg_bits(self, bank: int, reg: int, shift: int, mask: int) -> int:
return (self._read_bank_register(bank, reg) >> shift) & mask

def _set_reg_bits(
self, bank, reg, shift, mask, value
): # pylint: disable=too-many-arguments
def _set_reg_bits( # pylint: disable=too-many-arguments
self, bank: int, reg: int, shift: int, mask: int, value: int
) -> None:
reg_value = self._read_bank_register(bank, reg)
reg_value &= ~(mask << shift)
reg_value |= value << shift
self._write_register(reg, reg_value)

def _write_list(self, reg_list):
def _write_list(self, reg_list: List[int]) -> None:
for i in range(0, len(reg_list), 2):
self._write_register(reg_list[i], reg_list[i + 1])
time.sleep(0.001)

def _write_bank_register(self, bank, reg, value):
def _write_bank_register(self, bank: int, reg: int, value: int) -> None:
if self._bank != bank:
self._write_register(_BANK_SEL, bank)
self._write_register(reg, value)

def _read_bank_register(self, bank, reg):
def _read_bank_register(self, bank: int, reg: int) -> int:
if self._bank != bank:
self._write_register(_BANK_SEL, bank)
result = self._read_register(reg)
return result

def _write_register(self, reg, value):
def _write_register(self, reg: int, value: int) -> None:
if reg == _BANK_SEL:
if self._bank == value:
return
Expand All @@ -1019,7 +1027,7 @@ def _write_register(self, reg, value):
with self._i2c_device as i2c:
i2c.write(b)

def _read_register(self, reg):
def _read_register(self, reg: int) -> int:
b = bytearray(1)
b[0] = reg
with self._i2c_device as i2c:
Expand All @@ -1039,17 +1047,17 @@ class OV2640(_SCCBCameraBase): # pylint: disable=too-many-instance-attributes

def __init__(
self,
i2c_bus,
data_pins,
clock,
vsync,
href,
shutdown=None,
reset=None,
mclk=None,
mclk_frequency=20_000_000,
i2c_address=0x30,
size=OV2640_SIZE_QQVGA,
i2c_bus: I2C,
data_pins: Pin,
clock: Pin,
vsync: Pin,
href: Pin,
shutdown: Optional[Pin] = None,
reset: Optional[Pin] = None,
mclk: Optional[Pin] = None,
mclk_frequency: int = 20_000_000,
i2c_address: int = 0x30,
size: int = OV2640_SIZE_QQVGA,
): # pylint: disable=too-many-arguments
"""
Args:
Expand Down Expand Up @@ -1122,11 +1130,11 @@ def __init__(
data_pins=data_pins, clock=clock, vsync=vsync, href=href
)

def capture(self, buf):
def capture(self, buf: WriteableBuffer) -> Optional[memoryview]:
"""Capture an image into the buffer.
Args:
buf (Union[bytearray, memoryview]): A WritableBuffer to contain the \
buf (WriteableBuffer): A WritableBuffer to contain the \
captured image. Note that this can be a ulab array or a displayio Bitmap.
"""
self._imagecapture.capture(buf)
Expand All @@ -1138,40 +1146,40 @@ def capture(self, buf):
return None

@property
def capture_buffer_size(self):
def capture_buffer_size(self) -> int:
"""Return the size of capture buffer to use with current resolution & colorspace settings"""
if self.colorspace == OV2640_COLOR_JPEG:
return self.width * self.height // 5
return self.width * self.height * 2

@property
def mclk_frequency(self):
def mclk_frequency(self) -> Optional[int]:
"""Get the actual frequency the generated mclk, or None"""
return self._mclk_pwm.frequency if self._mclk_pwm else None

@property
def width(self):
def width(self) -> int:
"""Get the image width in pixels. A buffer of 2*width*height bytes \
stores a whole image."""
return self._w

@property
def height(self):
def height(self) -> int:
"""Get the image height in pixels. A buffer of 2*width*height bytes \
stores a whole image."""
return self._h

@property
def colorspace(self):
def colorspace(self) -> bytes:
"""Get or set the colorspace, one of the ``OV2640_COLOR_`` constants."""
return self._colorspace

@colorspace.setter
def colorspace(self, colorspace):
def colorspace(self, colorspace: bytes) -> None:
self._colorspace = colorspace
self._set_size_and_colorspace()

def _set_colorspace(self):
def _set_colorspace(self) -> None:
colorspace = self._colorspace
settings = _ov2640_color_settings[colorspace]

Expand All @@ -1180,7 +1188,7 @@ def _set_colorspace(self):
self._write_list(settings)
time.sleep(0.01)

def deinit(self):
def deinit(self) -> None:
"""Deinitialize the camera"""
self._imagecapture.deinit()
if self._mclk_pwm:
Expand All @@ -1191,11 +1199,11 @@ def deinit(self):
self._reset.deinit()

@property
def size(self):
def size(self) -> int:
"""Get or set the captured image size, one of the ``OV2640_SIZE_`` constants."""
return self._size

def _set_size_and_colorspace(self):
def _set_size_and_colorspace(self) -> None:
size = self._size
width, height, ratio = _resolution_info[size]
offset_x, offset_y, max_x, max_y = _ratio_table[ratio]
Expand All @@ -1218,11 +1226,11 @@ def _set_size_and_colorspace(self):
self._set_window(mode, offset_x, offset_y, max_x, max_y, width, height)

@size.setter
def size(self, size):
def size(self, size: int) -> None:
self._size = size
self._set_size_and_colorspace()

def _set_flip(self):
def _set_flip(self) -> None:
bits = 0
if self._flip_x:
bits |= _REG04_HFLIP_IMG
Expand All @@ -1231,38 +1239,45 @@ def _set_flip(self):
self._write_bank_register(_BANK_SENSOR, _REG04, _REG04_SET(bits))

@property
def flip_x(self):
def flip_x(self) -> bool:
"""Get or set the X-flip flag"""
return self._flip_x

@flip_x.setter
def flip_x(self, value):
def flip_x(self, value: bool) -> None:
self._flip_x = bool(value)
self._set_flip()

@property
def flip_y(self):
def flip_y(self) -> bool:
"""Get or set the Y-flip flag"""
return self._flip_y

@flip_y.setter
def flip_y(self, value):
def flip_y(self, value: bool) -> None:
self._flip_y = bool(value)
self._set_flip()

@property
def product_id(self):
def product_id(self) -> int:
"""Get the product id (PID) register. The expected value is 0x26."""
return self._read_bank_register(_BANK_SENSOR, _REG_PID)

@property
def product_version(self):
"""Get the version (VER) register. The expected value is 0x4x."""
def product_version(self) -> int:
"""Get the version (VER) register. The expected value is 0x41."""
return self._read_bank_register(_BANK_SENSOR, _REG_VER)

def _set_window(
self, mode, offset_x, offset_y, max_x, max_y, width, height
): # pylint: disable=too-many-arguments, too-many-locals
def _set_window( # pylint: disable=too-many-arguments, too-many-locals
self,
mode: int,
offset_x: int,
offset_y: int,
max_x: int,
max_y: int,
width: int,
height: int,
) -> None:
self._w = width
self._h = height

Expand Down Expand Up @@ -1335,7 +1350,7 @@ def _set_window(
self.test_pattern = self._test_pattern

@property
def exposure(self):
def exposure(self) -> int:
"""The exposure level of the sensor"""
aec_9_2 = self._get_reg_bits(_BANK_SENSOR, _AEC, 0, 0xFF)
aec_15_10 = self._get_reg_bits(_BANK_SENSOR, _REG45, 0, 0b111111)
Expand All @@ -1344,7 +1359,7 @@ def exposure(self):
return aec_1_0 | (aec_9_2 << 2) | (aec_15_10 << 10)

@exposure.setter
def exposure(self, exposure):
def exposure(self, exposure: int) -> None:
aec_1_0 = exposure & 0x11
aec_9_2 = (exposure >> 2) & 0b11111111
aec_15_10 = exposure >> 10
Expand Down

0 comments on commit fe63034

Please sign in to comment.