1use pamoja_hal::sim::{I2cPart, Rules, REGISTERS};
19
20use super::{
21 mode1, mode2, register, ALLCALL_ADDR_RESET, CHANNELS, LED_OFF_H_RESET, MODE1_RESET,
22 MODE2_RESET, PRE_SCALE_MIN, PRE_SCALE_RESET, SUBADR1_RESET, SUBADR2_RESET, SUBADR3_RESET,
23};
24
25const LAST_CHANNEL_REGISTER: u8 = register::LED0_ON_L + 4 * CHANNELS - 1;
27
28pub const RULES: Rules = Rules { write, read, next };
30
31#[must_use]
71pub fn part(address: u8) -> I2cPart {
72 let mut part = I2cPart::new(address)
73 .holding(register::MODE1, &[MODE1_RESET, MODE2_RESET])
74 .holding(
75 register::SUBADR1,
76 &[
77 SUBADR1_RESET,
78 SUBADR2_RESET,
79 SUBADR3_RESET,
80 ALLCALL_ADDR_RESET,
81 ],
82 )
83 .holding(register::PRE_SCALE, &[PRE_SCALE_RESET]);
84 for channel in 0..CHANNELS {
85 part.load(register::LED0_ON_L + 4 * channel + 3, &[LED_OFF_H_RESET]);
86 }
87 part.following(RULES)
88}
89
90fn write(registers: &mut [u8; REGISTERS], register: u8, value: u8) {
91 match register {
92 register::MODE1 => write_mode1(registers, value),
93 register::MODE2 => registers[register::MODE2 as usize] = value & 0x1F,
94 register::SUBADR1..=register::ALLCALL_ADDR => registers[register as usize] = value & !1,
95 register::LED0_ON_L..=LAST_CHANNEL_REGISTER => {
96 registers[register as usize] = channel_byte(register - register::LED0_ON_L, value);
97 loaded(registers, register - register::LED0_ON_L);
98 }
99 register::ALL_LED_ON_L..=register::ALL_LED_OFF_H => {
100 let offset = register - register::ALL_LED_ON_L;
101 for channel in 0..CHANNELS {
102 let at = register::LED0_ON_L + 4 * channel + offset;
103 registers[at as usize] = channel_byte(offset, value);
104 }
105 loaded(registers, offset);
106 }
107 register::PRE_SCALE if registers[register::MODE1 as usize] & mode1::SLEEP != 0 => {
108 registers[register::PRE_SCALE as usize] = value.max(PRE_SCALE_MIN);
109 }
110 _ => {}
111 }
112}
113
114fn write_mode1(registers: &mut [u8; REGISTERS], value: u8) {
117 let held = registers[register::MODE1 as usize];
118 let falling_asleep = held & mode1::SLEEP == 0 && value & mode1::SLEEP != 0;
119 let restart = if value & mode1::RESTART != 0 {
120 0
121 } else if falling_asleep && running(registers) {
122 mode1::RESTART
123 } else {
124 held & mode1::RESTART
125 };
126 let extclk = (held | value) & mode1::EXTCLK;
127 registers[register::MODE1 as usize] =
128 (value & !(mode1::RESTART | mode1::EXTCLK)) | restart | extclk;
129}
130
131fn channel_byte(offset: u8, value: u8) -> u8 {
133 if offset % 2 == 1 {
134 value & 0x1F
135 } else {
136 value
137 }
138}
139
140fn loaded(registers: &mut [u8; REGISTERS], offset: u8) {
143 let on_ack = registers[register::MODE2 as usize] & mode2::OCH != 0;
144 if !on_ack || offset % 4 == 3 {
145 registers[register::MODE1 as usize] &= !mode1::RESTART;
146 }
147}
148
149fn running(registers: &[u8; REGISTERS]) -> bool {
150 (0..CHANNELS).any(|channel| {
151 let off_high = register::LED0_ON_L + 4 * channel + 3;
152 registers[off_high as usize] & 0x10 == 0
153 })
154}
155
156fn read(registers: &[u8; REGISTERS], register: u8) -> u8 {
157 match register {
158 register::ALL_LED_ON_L..=register::ALL_LED_OFF_H => 0,
159 _ => registers[register as usize],
160 }
161}
162
163fn next(registers: &[u8; REGISTERS], register: u8) -> u8 {
164 if registers[register::MODE1 as usize] & mode1::AUTO_INCREMENT == 0 {
165 return register;
166 }
167 match register {
168 LAST_CHANNEL_REGISTER | register::PRE_SCALE => register::MODE1,
169 _ => register.wrapping_add(1),
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use crate::pca9685::{channel_register, Pca9685, Pwm, DEFAULT_I2C_ADDRESS};
177 use pamoja_hal::i2c::I2c;
178 use pamoja_hal::script::DelayLog;
179
180 const PART: u8 = DEFAULT_I2C_ADDRESS;
181
182 #[test]
183 fn it_powers_up_with_the_datasheet_register_values() {
184 let part = part(PART);
185 assert_eq!(
186 part.register(register::MODE1),
187 0x11,
188 "Table 5: sleeping, All Call on"
189 );
190 assert_eq!(part.register(register::MODE2), 0x04, "Table 6: totem pole");
191 assert_eq!(part.register(register::SUBADR1), 0xE2, "Table 9");
192 assert_eq!(part.register(register::SUBADR2), 0xE4);
193 assert_eq!(part.register(register::SUBADR3), 0xE8);
194 assert_eq!(part.register(register::ALLCALL_ADDR), 0xE0, "Table 10");
195 assert_eq!(part.register(register::PRE_SCALE), 0x1E, "Table 8: 200 Hz");
196 for channel in 0..CHANNELS {
197 let first = channel_register(channel);
198 let bytes = [
199 part.register(first),
200 part.register(first + 1),
201 part.register(first + 2),
202 part.register(first + 3),
203 ];
204 assert_eq!(
205 bytes,
206 [0, 0, 0, 0x10],
207 "Table 7: channel {channel} full off"
208 );
209 assert_eq!(Pwm::from_bytes(&bytes), Pwm::full_off());
210 }
211 }
212
213 #[test]
214 fn prescale_takes_a_write_only_while_asleep_and_never_below_three() {
215 let mut part = part(PART);
216 part.write(PART, &[register::PRE_SCALE, 121]).unwrap();
217 assert_eq!(
218 part.register(register::PRE_SCALE),
219 121,
220 "asleep at power-up"
221 );
222 part.write(PART, &[register::PRE_SCALE, 1]).unwrap();
223 assert_eq!(
224 part.register(register::PRE_SCALE),
225 3,
226 "the hardware minimum"
227 );
228 part.write(PART, &[register::MODE1, 0x00]).unwrap();
229 part.write(PART, &[register::PRE_SCALE, 121]).unwrap();
230 assert_eq!(part.register(register::PRE_SCALE), 3, "blocked while awake");
231 }
232
233 #[test]
234 fn the_pointer_moves_on_only_with_auto_increment() {
235 let mut part = part(PART);
236 let first = channel_register(0);
237 part.write(PART, &[first, 1, 2, 3, 4]).unwrap();
238 assert_eq!(part.register(first), 4, "every byte lands on one register");
239 assert_eq!(part.register(first + 1), 0);
240
241 part.write(PART, &[register::MODE1, mode1::AUTO_INCREMENT])
242 .unwrap();
243 part.write(PART, &[first, 1, 2, 3, 4]).unwrap();
244 assert_eq!(
245 [
246 part.register(first),
247 part.register(first + 1),
248 part.register(first + 2),
249 part.register(first + 3)
250 ],
251 [1, 2, 3, 4]
252 );
253
254 part.write(PART, &[LAST_CHANNEL_REGISTER, 0x10, mode1::AUTO_INCREMENT])
255 .unwrap();
256 assert_eq!(
257 part.register(register::MODE1),
258 mode1::AUTO_INCREMENT,
259 "wrapped to MODE1"
260 );
261 let mut around = [0u8; 2];
262 part.write_read(PART, &[register::PRE_SCALE], &mut around)
263 .unwrap();
264 assert_eq!(
265 around,
266 [PRE_SCALE_RESET, mode1::AUTO_INCREMENT],
267 "PRE_SCALE rolls over to MODE1"
268 );
269 }
270
271 #[test]
272 fn all_led_loads_every_channel_and_reads_zero() {
273 let mut part = part(PART);
274 part.write(PART, &[register::MODE1, mode1::AUTO_INCREMENT])
275 .unwrap();
276 let [on_l, on_h, off_l, off_h] = Pwm::from_counts(410, 3686).bytes();
277 part.write(PART, &[register::ALL_LED_ON_L, on_l, on_h, off_l, off_h])
278 .unwrap();
279 for channel in 0..CHANNELS {
280 let first = channel_register(channel);
281 let bytes = [
282 part.register(first),
283 part.register(first + 1),
284 part.register(first + 2),
285 part.register(first + 3),
286 ];
287 assert_eq!(Pwm::from_bytes(&bytes), Pwm::from_counts(410, 3686));
288 }
289 let mut all = [0xFFu8; 4];
290 part.write_read(PART, &[register::ALL_LED_ON_L], &mut all)
291 .unwrap();
292 assert_eq!(all, [0; 4], "Table 4: the ALL_LED registers read zero");
293 }
294
295 #[test]
296 fn restart_follows_section_7_3_1_1() {
297 let mut part = part(PART);
298 let awake = mode1::AUTO_INCREMENT;
299 part.write(PART, &[register::MODE1, awake]).unwrap();
300 part.write(PART, &[channel_register(3), 0, 0, 0x00, 0x08])
301 .unwrap();
302
303 part.write(PART, &[register::MODE1, awake | mode1::SLEEP])
304 .unwrap();
305 assert_ne!(
306 part.register(register::MODE1) & mode1::RESTART,
307 0,
308 "asleep with a channel running"
309 );
310
311 part.write(PART, &[register::MODE1, awake]).unwrap();
312 assert_ne!(
313 part.register(register::MODE1) & mode1::RESTART,
314 0,
315 "a written 0 has no effect"
316 );
317 part.write(PART, &[register::MODE1, awake | mode1::RESTART])
318 .unwrap();
319 assert_eq!(
320 part.register(register::MODE1) & mode1::RESTART,
321 0,
322 "a written 1 clears it"
323 );
324
325 part.write(PART, &[register::MODE1, awake | mode1::SLEEP])
326 .unwrap();
327 part.write(PART, &[channel_register(5), 0, 0, 0, 0x08])
328 .unwrap();
329 assert_eq!(
330 part.register(register::MODE1) & mode1::RESTART,
331 0,
332 "loading a channel clears it"
333 );
334
335 let mut stopped = super::part(PART);
336 stopped.write(PART, &[register::MODE1, 0x00]).unwrap();
337 stopped
338 .write(PART, &[register::MODE1, mode1::SLEEP])
339 .unwrap();
340 assert_eq!(
341 stopped.register(register::MODE1) & mode1::RESTART,
342 0,
343 "every channel off"
344 );
345 }
346
347 #[test]
348 fn extclk_stays_set_and_reserved_bits_read_zero() {
349 let mut part = part(PART);
350 part.write(PART, &[register::MODE1, mode1::SLEEP | mode1::EXTCLK])
351 .unwrap();
352 part.write(PART, &[register::MODE1, mode1::SLEEP]).unwrap();
353 assert_ne!(part.register(register::MODE1) & mode1::EXTCLK, 0);
354 part.write(PART, &[register::MODE2, 0xFF]).unwrap();
355 assert_eq!(part.register(register::MODE2), 0x1F);
356 part.write(PART, &[register::SUBADR1, 0xFF]).unwrap();
357 assert_eq!(
358 part.register(register::SUBADR1),
359 0xFE,
360 "the LSB is read-only 0"
361 );
362 part.write(PART, &[0x50, 0xAA]).unwrap();
363 assert_eq!(part.register(0x50), 0, "a reserved register takes nothing");
364 }
365
366 #[test]
367 fn the_driver_leaves_the_part_as_its_datasheet_asks() {
368 let mut pwm = Pca9685::new(part(PART), PART, DelayLog::new()).with_frequency(50);
369 pwm.set_channel(0, Pwm::servo(1500, 50)).unwrap();
370 pwm.set_channel(15, Pwm::duty(1024)).unwrap();
371 let (part, delay) = pwm.release();
372 assert_eq!(
373 part.register(register::PRE_SCALE),
374 121,
375 "round(25 MHz / 4096 / 50) - 1"
376 );
377 assert_eq!(
378 part.register(register::MODE1),
379 mode1::AUTO_INCREMENT,
380 "awake, RESTART cleared"
381 );
382 assert_eq!(part.register(register::MODE2), MODE2_RESET);
383 let first = channel_register(0);
384 let servo = [
385 part.register(first),
386 part.register(first + 1),
387 part.register(first + 2),
388 part.register(first + 3),
389 ];
390 assert_eq!(Pwm::from_bytes(&servo), Pwm::servo(1500, 50));
391 assert_eq!(delay.total_micros(), 500, "the oscillator's start-up");
392 }
393
394 #[test]
395 fn a_channel_reads_back_whatever_mode1_holds() {
396 let mut fresh = Pca9685::new(part(PART), PART, DelayLog::new());
397 assert_eq!(
398 fresh.channel(7).unwrap(),
399 Pwm::full_off(),
400 "every channel powers up full off, and auto-increment is off until init"
401 );
402 let (part, _) = fresh.release();
403 assert_eq!(
404 part.register(register::MODE1),
405 MODE1_RESET,
406 "a read changes nothing"
407 );
408
409 let mut pwm = Pca9685::new(part, PART, DelayLog::new()).with_frequency(50);
410 pwm.set_channel(7, Pwm::duty(1024)).unwrap();
411 assert_eq!(pwm.channel(7).unwrap(), Pwm::duty(1024));
412 }
413
414 #[test]
415 fn a_driver_that_writes_the_prescale_awake_leaves_the_part_at_200_hz() {
416 let mut part = part(PART);
417 part.write(PART, &[register::MODE1, mode1::AUTO_INCREMENT])
418 .unwrap();
419 part.write(PART, &[register::PRE_SCALE, 121]).unwrap();
420 assert_eq!(part.register(register::PRE_SCALE), PRE_SCALE_RESET);
421 }
422}