Skip to content

Commit 79fd3a1

Browse files
committed
Initial HM01B0 PIO implementation
Works, but having some sync issues
1 parent 8b98132 commit 79fd3a1

File tree

1 file changed

+314
-0
lines changed

1 file changed

+314
-0
lines changed

drivers/camera/hm01b0_pio.py

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
import rp2
2+
from machine import Pin, I2C
3+
from ulab import numpy as np
4+
from time import sleep_us
5+
import time
6+
7+
# Derived from:
8+
# https://github.com/openmv/openmv/blob/5acf5baf92b4314a549bdd068138e5df6cc0bac7/drivers/sensors/hm01b0.c
9+
class HM01B0_PIO():
10+
# Derived from:
11+
# https://github.com/openmv/openmv/blob/5acf5baf92b4314a549bdd068138e5df6cc0bac7/drivers/sensors/hm01b0_regs.h
12+
13+
# Read only registers
14+
MODEL_ID_H = 0x0000
15+
MODEL_ID_L = 0x0001
16+
FRAME_COUNT = 0x0005
17+
PIXEL_ORDER = 0x0006
18+
# Sensor mode control
19+
MODE_SELECT = 0x0100
20+
IMG_ORIENTATION = 0x0101
21+
SW_RESET = 0x0103
22+
GRP_PARAM_HOLD = 0x0104
23+
# Sensor exposure gain control
24+
INTEGRATION_H = 0x0202
25+
INTEGRATION_L = 0x0203
26+
ANALOG_GAIN = 0x0205
27+
DIGITAL_GAIN_H = 0x020E
28+
DIGITAL_GAIN_L = 0x020F
29+
# Frame timing control
30+
FRAME_LEN_LINES_H = 0x0340
31+
FRAME_LEN_LINES_L = 0x0341
32+
LINE_LEN_PCK_H = 0x0342
33+
LINE_LEN_PCK_L = 0x0343
34+
# Binning mode control
35+
READOUT_X = 0x0383
36+
READOUT_Y = 0x0387
37+
BINNING_MODE = 0x0390
38+
# Test pattern control
39+
TEST_PATTERN_MODE = 0x0601
40+
# Black level control
41+
BLC_CFG = 0x1000
42+
BLC_TGT = 0x1003
43+
BLI_EN = 0x1006
44+
BLC2_TGT = 0x1007
45+
# Sensor reserved
46+
DPC_CTRL = 0x1008
47+
SINGLE_THR_HOT = 0x100B
48+
SINGLE_THR_COLD = 0x100C
49+
# VSYNC,HSYNC and pixel shift register
50+
VSYNC_HSYNC_PIXEL_SHIFT_EN = 0x1012
51+
# Automatic exposure gain control
52+
AE_CTRL = 0x2100
53+
AE_TARGET_MEAN = 0x2101
54+
AE_MIN_MEAN = 0x2102
55+
CONVERGE_IN_TH = 0x2103
56+
CONVERGE_OUT_TH = 0x2104
57+
MAX_INTG_H = 0x2105
58+
MAX_INTG_L = 0x2106
59+
MIN_INTG = 0x2107
60+
MAX_AGAIN_FULL = 0x2108
61+
MAX_AGAIN_BIN2 = 0x2109
62+
MIN_AGAIN = 0x210A
63+
MAX_DGAIN = 0x210B
64+
MIN_DGAIN = 0x210C
65+
DAMPING_FACTOR = 0x210D
66+
FS_CTRL = 0x210E
67+
FS_60HZ_H = 0x210F
68+
FS_60HZ_L = 0x2110
69+
FS_50HZ_H = 0x2111
70+
FS_50HZ_L = 0x2112
71+
FS_HYST_TH = 0x2113
72+
# Motion detection control
73+
MD_CTRL = 0x2150
74+
I2C_CLEAR = 0x2153
75+
WMEAN_DIFF_TH_H = 0x2155
76+
WMEAN_DIFF_TH_M = 0x2156
77+
WMEAN_DIFF_TH_L = 0x2157
78+
MD_THH = 0x2158
79+
MD_THM1 = 0x2159
80+
MD_THM2 = 0x215A
81+
MD_THL = 0x215B
82+
STATISTIC_CTRL = 0x2000
83+
MD_LROI_X_START_H = 0x2011
84+
MD_LROI_X_START_L = 0x2012
85+
MD_LROI_Y_START_H = 0x2013
86+
MD_LROI_Y_START_L = 0x2014
87+
MD_LROI_X_END_H = 0x2015
88+
MD_LROI_X_END_L = 0x2016
89+
MD_LROI_Y_END_H = 0x2017
90+
MD_LROI_Y_END_L = 0x2018
91+
MD_INTERRUPT = 0x2160
92+
# Sensor timing control
93+
QVGA_WIN_EN = 0x3010
94+
SIX_BIT_MODE_EN = 0x3011
95+
PMU_AUTOSLEEP_FRAMECNT = 0x3020
96+
ADVANCE_VSYNC = 0x3022
97+
ADVANCE_HSYNC = 0x3023
98+
EARLY_GAIN = 0x3035
99+
# IO and clock control
100+
BIT_CONTROL = 0x3059
101+
OSC_CLK_DIV = 0x3060
102+
ANA_Register_11 = 0x3061
103+
IO_DRIVE_STR = 0x3062
104+
IO_DRIVE_STR2 = 0x3063
105+
ANA_Register_14 = 0x3064
106+
OUTPUT_PIN_STATUS_CONTROL = 0x3065
107+
ANA_Register_17 = 0x3067
108+
PCLK_POLARITY = 0x3068
109+
110+
# Useful values of Himax registers
111+
HIMAX_RESET = 0x01
112+
HIMAX_MODE_STANDBY = 0x00
113+
HIMAX_MODE_STREAMING = 0x01 # I2C triggered streaming enable
114+
HIMAX_MODE_STREAMING_NFRAMES = 0x03 # Output N frames
115+
HIMAX_MODE_STREAMING_TRIG = 0x05 # Hardware Trigger
116+
# HIMAX_SET_HMIRROR (r, x) ((r & 0xFE) | ((x & 1) << 0))
117+
# HIMAX_SET_VMIRROR (r, x) ((r & 0xFD) | ((x & 1) << 1))
118+
119+
PCLK_RISING_EDGE = 0x00
120+
PCLK_FALLING_EDGE = 0x01
121+
AE_CTRL_ENABLE = 0x00
122+
AE_CTRL_DISABLE = 0x01
123+
124+
HIMAX_BOOT_RETRY = 10
125+
HIMAX_LINE_LEN_PCK_FULL = 0x178
126+
HIMAX_FRAME_LENGTH_FULL = 0x109
127+
128+
HIMAX_LINE_LEN_PCK_QVGA = 0x178
129+
HIMAX_FRAME_LENGTH_QVGA = 0x104
130+
131+
HIMAX_LINE_LEN_PCK_QQVGA = 0x178
132+
HIMAX_FRAME_LENGTH_QQVGA = 0x084
133+
134+
INIT_COMMANDS = (
135+
(0x3044, 0x0A), # Increase CDS time for settling
136+
(0x3045, 0x00), # Make symmetric for cds_tg and rst_tg
137+
(0x3047, 0x0A), # Increase CDS time for settling
138+
(0x3050, 0xC0), # Make negative offset up to 4x
139+
(0x3051, 0x42),
140+
(0x3052, 0x50),
141+
(0x3053, 0x00),
142+
(0x3054, 0x03), # tuning sf sig clamping as lowest
143+
(0x3055, 0xF7), # tuning dsun
144+
(0x3056, 0xF8), # increase adc nonoverlap clk
145+
(0x3057, 0x29), # increase adc pwr for missing code
146+
(0x3058, 0x1F), # turn on dsun
147+
(0x3059, 0x1E),
148+
(0x3064, 0x00),
149+
(0x3065, 0x04), # pad pull 0
150+
(ANA_Register_17, 0x00), # Disable internal oscillator
151+
152+
(0x1012, 0x00), # Sync. shift disable
153+
154+
(AE_CTRL, 0x01), #Automatic Exposure
155+
(AE_TARGET_MEAN, 0x80), #AE target mean [Def: 0x3C]
156+
(AE_MIN_MEAN, 0x0A), #AE min target mean [Def: 0x0A]
157+
(CONVERGE_IN_TH, 0x03), #Converge in threshold [Def: 0x03]
158+
(CONVERGE_OUT_TH, 0x05), #Converge out threshold [Def: 0x05]
159+
(MAX_INTG_H, (HIMAX_FRAME_LENGTH_QVGA - 2) >> 8), #Maximum INTG High Byte [Def: 0x01]
160+
(MAX_INTG_L, (HIMAX_FRAME_LENGTH_QVGA - 2) & 0xFF), #Maximum INTG Low Byte [Def: 0x54]
161+
(MAX_AGAIN_FULL, 0x04), #Maximum Analog gain in full frame mode [Def: 0x03]
162+
(MAX_AGAIN_BIN2, 0x04), #Maximum Analog gain in bin2 mode [Def: 0x04]
163+
(MAX_DGAIN, 0xC0),
164+
165+
(INTEGRATION_H, 0x01), #Integration H [Def: 0x01]
166+
(INTEGRATION_L, 0x08), #Integration L [Def: 0x08]
167+
(ANALOG_GAIN, 0x00), #Analog Global Gain [Def: 0x00]
168+
(DAMPING_FACTOR, 0x20), #Damping Factor [Def: 0x20]
169+
(DIGITAL_GAIN_H, 0x01), #Digital Gain High [Def: 0x01]
170+
(DIGITAL_GAIN_L, 0x00), #Digital Gain Low [Def: 0x00]
171+
172+
(MD_CTRL, 0x00),
173+
(FRAME_LEN_LINES_H, HIMAX_FRAME_LENGTH_QVGA >> 8),
174+
(FRAME_LEN_LINES_L, HIMAX_FRAME_LENGTH_QVGA & 0xFF),
175+
(LINE_LEN_PCK_H, HIMAX_LINE_LEN_PCK_QVGA >> 8),
176+
(LINE_LEN_PCK_L, HIMAX_LINE_LEN_PCK_QVGA & 0xFF),
177+
(QVGA_WIN_EN, 0x01), # Enable QVGA window readout
178+
(0x3059, 0x22), # 1-bit mode
179+
(OSC_CLK_DIV, 0x14),
180+
(IMG_ORIENTATION, 0x00), # change the orientation
181+
(0x0104, 0x01),
182+
(MODE_SELECT, 0x01), # Streaming mode
183+
)
184+
185+
def __init__(
186+
self,
187+
i2c,
188+
pin_d0,
189+
pin_vsync,
190+
pin_hsync,
191+
pin_pclk,
192+
sm_id = 0,
193+
i2c_address = 0x24,
194+
):
195+
self.i2c = i2c
196+
self.pin_d0 = pin_d0
197+
self.pin_vsync = pin_vsync
198+
self.pin_hsync = pin_hsync
199+
self.pin_pclk = pin_pclk
200+
self.sm_id = sm_id
201+
self.i2c_address = i2c_address
202+
self.buffer = np.zeros((244, 324), dtype=np.uint8)
203+
# self.buffer = bytearray(244 * 324)
204+
205+
Pin(pin_d0, Pin.IN)
206+
Pin(pin_vsync, Pin.IN)
207+
Pin(pin_hsync, Pin.IN)
208+
Pin(pin_pclk, Pin.IN)
209+
210+
self.soft_reset()
211+
self.send_init()
212+
self.start_pio_dma()
213+
214+
def is_connected(self):
215+
try:
216+
# Try to read the chip ID
217+
# If it throws an I/O error - the device isn't connected
218+
id = self.getChipID()
219+
220+
# Confirm the chip ID is correct
221+
if id == 0x01B0:
222+
return True
223+
else:
224+
return False
225+
except:
226+
return False
227+
228+
def getChipID(self):
229+
"""
230+
Reads the chip ID from the HM01B0 sensor.
231+
Returns:
232+
int: The chip ID as a 16-bit integer.
233+
"""
234+
data = self.readRegister(self.MODEL_ID_H, 2)
235+
return (data[0] << 8) | data[1]
236+
237+
def soft_reset(self):
238+
"""
239+
Performs a software reset of the HM01B0 sensor.
240+
This resets the sensor to its default state.
241+
"""
242+
self.writeRegister(self.SW_RESET, self.HIMAX_RESET)
243+
244+
def send_init(self):
245+
"""
246+
Initializes the HM01B0 sensor with default settings.
247+
This includes setting up exposure, gain, and frame timing.
248+
"""
249+
for reg, value in self.INIT_COMMANDS:
250+
self.writeRegister(reg, value)
251+
sleep_us(1000)
252+
253+
# Ensure the sensor is in streaming mode
254+
# self.writeRegister(self.MODE_SELECT, self.HIMAX_MODE_STREAMING)
255+
256+
def readRegister(self, reg, nbytes=1):
257+
self.i2c.writeto(self.i2c_address, bytes([reg >> 8, reg & 0xFF]))
258+
return self.i2c.readfrom(self.i2c_address, nbytes)
259+
260+
def writeRegister(self, reg, data):
261+
if isinstance(data, int):
262+
data = bytes([data])
263+
elif isinstance(data, (list, tuple)):
264+
data = bytes(data)
265+
self.i2c.writeto(self.i2c_address, bytes([reg >> 8, reg & 0xFF]) + data)
266+
267+
def start_pio_dma(self):
268+
program = self._pio_read_dvp
269+
program[0][0] |= self.pin_hsync & 0x1F
270+
program[0][1] |= self.pin_pclk & 0x1F
271+
program[0][3] |= self.pin_pclk & 0x1F
272+
self.sm = rp2.StateMachine(
273+
self.sm_id,
274+
program,
275+
in_base = self.pin_d0
276+
)
277+
self.sm.active(1)
278+
279+
self.dma = rp2.DMA()
280+
req_num = ((self.sm_id // 4) << 3) + (self.sm_id % 4) + 4
281+
dma_ctrl = self.dma.pack_ctrl(
282+
size = 0, # 0 = 8-bit, 1 = 16-bit, 2 = 32-bit
283+
inc_read = False,
284+
treq_sel = req_num
285+
# irq_quiet = False
286+
)
287+
self.dma.config(
288+
read = self.sm,
289+
write = self.buffer,
290+
count = 244 * 324,
291+
ctrl = dma_ctrl
292+
)
293+
294+
Pin(self.pin_vsync).irq(
295+
trigger = Pin.IRQ_FALLING,
296+
handler = lambda pin: self._vsync_handler()
297+
)
298+
299+
def _vsync_handler(self):
300+
self.sm.restart()
301+
self.dma.write = self.buffer
302+
self.dma.active(True)
303+
# print("new frame:", time.ticks_ms())
304+
305+
@rp2.asm_pio(
306+
in_shiftdir = rp2.PIO.SHIFT_LEFT,
307+
push_thresh = 8,
308+
autopush = True
309+
)
310+
def _pio_read_dvp():
311+
wait(1, gpio, 0) # Mask in HSYNC pin
312+
wait(1, gpio, 0) # Mask in PCLK pin
313+
in_(pins, 1) # Mask in number of pins
314+
wait(0, gpio, 0) # Mask in PCLK pin

0 commit comments

Comments
 (0)