ACwing Flow bridge firmware
STM32C011: PMW3901 optical flow over SPI, I2C target 0x3B
Loading...
Searching...
No Matches
main.c
Go to the documentation of this file.
1
28#include "stm32c0xx.h"
29#include "bridge.h"
30#include "pmw3901.h"
31#include "flow_regs.h"
32
33#define SYSCLK_HZ 48000000u
34#define LOOP_MS 10u
35#define REINIT_MS 1000u
36
37static volatile uint32_t s_ms;
38
39void SysTick_Handler(void) { s_ms++; }
40
41/* ------------------------------------------------------------------ board */
42static void iwdg_feed(void) { IWDG->KR = 0xAAAAu; }
43
44static void clock_init(void)
45{
46 /* 1 wait state above 24 MHz, then HSISYS = HSI48 / 1 */
47 FLASH->ACR = (FLASH->ACR & ~FLASH_ACR_LATENCY) | FLASH_ACR_LATENCY_0;
48 while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_0) { }
49 RCC->CR &= ~RCC_CR_HSIDIV;
50 SystemCoreClockUpdate();
51 SysTick_Config(SYSCLK_HZ / 1000u);
52}
53
54static void gpio_mode(GPIO_TypeDef *g, uint32_t pin, uint32_t mode, uint32_t af)
55{
56 g->MODER = (g->MODER & ~(3u << (2u * pin))) | (mode << (2u * pin));
57 g->OSPEEDR |= 3u << (2u * pin);
58 volatile uint32_t *afr = &g->AFR[pin >> 3];
59 *afr = (*afr & ~(0xFu << (4u * (pin & 7u)))) | (af << (4u * (pin & 7u)));
60}
61
62static void board_init(void)
63{
64 RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN;
65 RCC->APBENR1 |= RCC_APBENR1_I2C1EN;
66 RCC->APBENR2 |= RCC_APBENR2_SPI1EN | RCC_APBENR2_TIM14EN;
67 (void)RCC->APBENR2;
68
69 /* outputs first at their idle level: NCS high, NRESET high, LED off */
70 GPIOA->BSRR = (1u << 4) | (1u << 2) | (1u << 8);
71 gpio_mode(GPIOA, 2, 1u, 0); /* PMW_NRESET out */
72 gpio_mode(GPIOA, 4, 1u, 0); /* PMW_NCS out */
73 gpio_mode(GPIOA, 8, 1u, 0); /* LED out */
74 gpio_mode(GPIOA, 3, 0u, 0); /* MOTION in */
75 /* PA0 / PA1 (ToF) untouched: analog after reset = high impedance */
76 gpio_mode(GPIOA, 5, 2u, 0); /* SPI1 SCK AF0 */
77 gpio_mode(GPIOA, 6, 2u, 0); /* SPI1 MISO AF0 */
78 gpio_mode(GPIOA, 7, 2u, 0); /* SPI1 MOSI AF0 */
79 GPIOB->OTYPER |= (1u << 6) | (1u << 7); /* I2C open drain */
80 gpio_mode(GPIOB, 6, 2u, 6); /* I2C1 SCL AF6 */
81 gpio_mode(GPIOB, 7, 2u, 6); /* I2C1 SDA AF6 */
82
83 /* TIM14: free-running 1 MHz counter for the microsecond delays */
84 TIM14->PSC = (SYSCLK_HZ / 1000000u) - 1u;
85 TIM14->ARR = 0xFFFFu;
86 TIM14->EGR = TIM_EGR_UG;
87 TIM14->CR1 = TIM_CR1_CEN;
88
89 /* SPI1: master, mode 3 (CPOL = CPHA = 1), 48 MHz / 32 = 1.5 MHz (PMW3901 <= 2 MHz), 8 bit */
90 SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_CPOL | SPI_CR1_CPHA | SPI_CR1_SSM | SPI_CR1_SSI |
91 (4u << SPI_CR1_BR_Pos);
92 SPI1->CR2 = (7u << SPI_CR2_DS_Pos) | SPI_CR2_FRXTH;
93 SPI1->CR1 |= SPI_CR1_SPE;
94
95 /* I2C1 target at 0x3B, clock stretching allowed. TIMINGR for the target
96 * only needs the data setup/hold: PRESC 5 (125 ns), SCLDEL 3 (500 ns),
97 * SDADEL 2 (250 ns): standard and fast mode (400 kHz). */
98 I2C1->CR1 = 0;
99 I2C1->TIMINGR = (5u << I2C_TIMINGR_PRESC_Pos) | (3u << I2C_TIMINGR_SCLDEL_Pos) |
100 (2u << I2C_TIMINGR_SDADEL_Pos) | (3u << I2C_TIMINGR_SCLH_Pos) | 9u;
101 I2C1->OAR1 = 0;
102 I2C1->OAR1 = I2C_OAR1_OA1EN | ((uint32_t)FLOW_I2C_ADDR << 1);
103 I2C1->CR1 = I2C_CR1_ADDRIE | I2C_CR1_RXIE | I2C_CR1_TXIE | I2C_CR1_STOPIE |
104 I2C_CR1_NACKIE | I2C_CR1_ERRIE | I2C_CR1_PE;
105 NVIC_SetPriority(I2C1_IRQn, 0);
106 NVIC_EnableIRQ(I2C1_IRQn);
107}
108
109static void iwdg_start(void)
110{
111 DBG->APBFZ1 |= DBG_APB_FZ1_DBG_IWDG_STOP; /* debugger halt: no reset */
112 IWDG->KR = 0xCCCCu;
113 IWDG->KR = 0x5555u;
114 IWDG->PR = 3u; /* LSI 32 kHz / 32 = 1 kHz */
115 IWDG->RLR = 500u; /* 0.5 s */
116 while (IWDG->SR != 0u) { }
117 iwdg_feed();
118}
119
120/* ------------------------------------------------------------------ I2C target */
122{
123 const uint32_t isr = I2C1->ISR;
124 if (isr & I2C_ISR_ADDR) {
125 if (isr & I2C_ISR_DIR) { /* master reads */
126 I2C1->ISR |= I2C_ISR_TXE; /* flush a stale byte */
128 } else {
130 }
131 I2C1->ICR = I2C_ICR_ADDRCF;
132 }
133 if (isr & I2C_ISR_RXNE) bridge_i2c_rx((uint8_t)I2C1->RXDR);
134 if (isr & I2C_ISR_TXIS) I2C1->TXDR = bridge_i2c_tx();
135 if (isr & I2C_ISR_NACKF) I2C1->ICR = I2C_ICR_NACKCF;
136 if (isr & I2C_ISR_STOPF) {
137 I2C1->ICR = I2C_ICR_STOPCF;
138 I2C1->ISR |= I2C_ISR_TXE;
140 }
141 if (isr & (I2C_ISR_BERR | I2C_ISR_ARLO | I2C_ISR_OVR))
142 I2C1->ICR = I2C_ICR_BERRCF | I2C_ICR_ARLOCF | I2C_ICR_OVRCF;
143}
144
145/* ------------------------------------------------------------------ PMW3901 bus */
146static void delay_us(uint32_t us)
147{
148 while (us) {
149 const uint32_t step = (us > 10000u) ? 10000u : us; /* < 16-bit counter range */
150 const uint16_t t0 = (uint16_t)TIM14->CNT;
151 while ((uint16_t)((uint16_t)TIM14->CNT - t0) < step) { }
152 us -= step;
153 iwdg_feed(); /* long init delays (100 ms) */
154 }
155}
156
157static uint8_t spi_xfer(uint8_t b)
158{
159 while (!(SPI1->SR & SPI_SR_TXE)) { }
160 *(volatile uint8_t *)&SPI1->DR = b;
161 while (!(SPI1->SR & SPI_SR_RXNE)) { }
162 return *(volatile uint8_t *)&SPI1->DR;
163}
164
165static void pin_cs(uint8_t l) { GPIOA->BSRR = l ? (1u << 4) : (1u << (4 + 16)); }
166static void pin_reset(uint8_t l) { GPIOA->BSRR = l ? (1u << 2) : (1u << (2 + 16)); }
167static void led(uint8_t on) { GPIOA->BSRR = on ? (1u << (8 + 16)) : (1u << 8); }
168
170
171/* ------------------------------------------------------------------ main */
172int main(void)
173{
174 clock_init();
175 board_init();
176 bridge_init();
177 iwdg_start();
178
179 uint8_t ok = (uint8_t)(pmw_init(&k_bus) == 0);
181 uint32_t t_next = s_ms, t_try = s_ms, t_reads = s_ms;
182 uint16_t last_us = (uint16_t)TIM14->CNT;
183 uint8_t reads_seen = bridge_reads(), fc_active = 0, valid = 1;
184
185 for (;;) {
186 iwdg_feed();
187
188 const uint8_t c = bridge_take_ctrl();
189 if (c & FLOW_CTRL_RESET) NVIC_SystemReset();
190 if ((c & FLOW_CTRL_REINIT) || (!ok && (s_ms - t_try) >= REINIT_MS)) {
191 t_try = s_ms;
193 ok = (uint8_t)(pmw_init(&k_bus) == 0);
195 last_us = (uint16_t)TIM14->CNT;
196 }
197
198 if ((int32_t)(s_ms - t_next) >= 0) {
199 t_next += LOOP_MS;
200 if ((int32_t)(s_ms - t_next) > 0) t_next = s_ms + LOOP_MS; /* after a re-init */
201 if (ok) {
202 pmw_burst_t b;
203 pmw_burst(&b);
204 const uint16_t now_us = (uint16_t)TIM14->CNT;
205 const uint32_t dt = (uint16_t)(now_us - last_us); /* <= 65 ms: fine at 10 ms */
206 last_us = now_us;
207 valid = pmw_valid(&b);
208 __disable_irq();
209 bridge_add_sample(b.dx, b.dy, b.squal, b.shutter, valid, dt);
210 __enable_irq();
211 }
212 }
213
214 /* the FC is reading frames: any FRAME read in the last second */
215 if (bridge_reads() != reads_seen) { reads_seen = bridge_reads(); t_reads = s_ms; }
216 fc_active = (uint8_t)((s_ms - t_reads) < 1000u);
217
218 if (!ok) led((s_ms / 100u) & 1u); /* 5 Hz */
219 else if (!valid) led((s_ms % 2000u) < 50u); /* short flash */
220 else if (!fc_active) led((s_ms / 500u) & 1u); /* 1 Hz */
221 else led(1);
222 }
223}
Register file of the flow bridge and the I2C target protocol.
void bridge_add_sample(int16_t dx, int16_t dy, uint8_t squal, uint16_t shutter, uint8_t valid, uint32_t dt_us)
one motion burst: deltas since the previous burst, dt_us = time between the two bursts; valid = 0 for...
Definition bridge.c:32
void bridge_i2c_addr_read(void)
address matched, master reads: snapshot
Definition bridge.c:92
void bridge_i2c_addr_write(void)
address matched, master writes
Definition bridge.c:82
void bridge_i2c_rx(uint8_t b)
first byte = register pointer
Definition bridge.c:84
void bridge_set_sensor_ok(uint8_t ok)
Definition bridge.c:26
uint8_t bridge_reads(void)
FRAME reads since boot (LED activity), wraps.
Definition bridge.c:114
void bridge_init(void)
Definition bridge.c:19
uint8_t bridge_take_ctrl(void)
CTRL bits written by the master, cleared.
Definition bridge.c:107
uint8_t bridge_i2c_tx(void)
next byte to send
Definition bridge.c:98
void bridge_i2c_stop(void)
Definition bridge.c:105
I2C register map of the ACwing Flow bridge (STM32C011 at 0x3B).
#define FLOW_CTRL_RESET
Definition flow_regs.h:52
#define FLOW_I2C_ADDR
Definition flow_regs.h:30
#define FLOW_CTRL_REINIT
Definition flow_regs.h:53
static void iwdg_feed(void)
Definition main.c:42
static void board_init(void)
Definition main.c:62
static void pin_cs(uint8_t l)
Definition main.c:165
static void pin_reset(uint8_t l)
Definition main.c:166
#define REINIT_MS
Definition main.c:35
int main(void)
Definition main.c:172
static void led(uint8_t on)
Definition main.c:167
static volatile uint32_t s_ms
Definition main.c:37
#define SYSCLK_HZ
Definition main.c:33
static void clock_init(void)
Definition main.c:44
void SysTick_Handler(void)
Definition main.c:39
static const pmw_bus_t k_bus
Definition main.c:169
static uint8_t spi_xfer(uint8_t b)
Definition main.c:157
void I2C1_IRQHandler(void)
Definition main.c:121
static void iwdg_start(void)
Definition main.c:109
static void delay_us(uint32_t us)
Definition main.c:146
static void gpio_mode(GPIO_TypeDef *g, uint32_t pin, uint32_t mode, uint32_t af)
Definition main.c:54
#define LOOP_MS
Definition main.c:34
PixArt PMW3901MB optical flow sensor over SPI (mode 3, <= 2 MHz).
int pmw_init(const pmw_bus_t *bus)
0 = product id ok, configured
Definition pmw3901.c:59
uint8_t pmw_valid(const pmw_burst_t *b)
PixArt rule: SQUAL < 0x19 with the shutter at its maximum = no usable features.
Definition pmw3901.c:106
void pmw_burst(pmw_burst_t *b)
motion burst (register 0x16)
Definition pmw3901.c:85
uint16_t shutter
Definition pmw3901.h:24
int16_t dx
Definition pmw3901.h:22
uint8_t squal
Definition pmw3901.h:23
int16_t dy
Definition pmw3901.h:22