Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_hc_sr04.c
1 #include <bc_hc_sr04.h>
2 #include <bc_scheduler.h>
3 #include <bc_system.h>
4 #include <stm32l0xx.h>
5 #include <bc_gpio.h>
6 #include <bc_timer.h>
7 
8 // Timer resolution in microseconds
9 #define _BC_HC_SR04_RESOLUTION 5
10 
11 static struct
12 {
13  bc_scheduler_task_id_t task_id_interval;
14  bc_scheduler_task_id_t task_id_notify;
15  void (*event_handler)(bc_hc_sr04_event_t, void *);
16  void *event_param;
17  bc_tick_t update_interval;
18  bool measurement_active;
19  bool measurement_valid;
20  uint16_t echo_duration;
21 
22 } _bc_hc_sr04;
23 
24 static void _bc_hc_sr04_task_interval(void *param);
25 
26 static void _bc_hc_sr04_task_notify(void *param);
27 
28 static void _bc_hc_sr04_iqr_handler(void *param);
29 
30 void bc_hc_sr04_init(void)
31 {
32  memset(&_bc_hc_sr04, 0, sizeof(_bc_hc_sr04));
33 
34  _bc_hc_sr04.task_id_interval = bc_scheduler_register(_bc_hc_sr04_task_interval, NULL, BC_TICK_INFINITY);
35 
36  // Pin Echo
38 
40 
41  // Pin Trig
43 
45 
47 
48  // Enable TIM3 clock
49  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
50 
51  // Errata workaround
52  RCC->APB1ENR;
53 
54  // Capture 4 and Capture 3 is connected to TI3
55  TIM3->CCMR2 |= TIM_CCMR2_CC4S_1 | TIM_CCMR2_CC3S_0;
56 
57  // Capture 4 is sensitive on falling edge
58  TIM3->CCER |= 1 << TIM_CCER_CC4P_Pos;
59 
60  // Set prescaler to 5 * 32 (5 microseconds resolution)
61  TIM3->PSC = _BC_HC_SR04_RESOLUTION * 32 - 1;
62 
63  bc_timer_set_irq_handler(TIM3, _bc_hc_sr04_iqr_handler, NULL);
64 
65  // Enable TIM3 interrupts
66  NVIC_EnableIRQ(TIM3_IRQn);
67 
68  bc_timer_init();
69 }
70 
71 void bc_hc_sr04_set_event_handler(void (*event_handler)(bc_hc_sr04_event_t, void *), void *event_param)
72 {
73  _bc_hc_sr04.event_handler = event_handler;
74  _bc_hc_sr04.event_param = event_param;
75 }
76 
78 {
79  _bc_hc_sr04.update_interval = interval;
80 
81  if (_bc_hc_sr04.update_interval == BC_TICK_INFINITY)
82  {
83  bc_scheduler_plan_absolute(_bc_hc_sr04.task_id_interval, BC_TICK_INFINITY);
84  }
85  else
86  {
87  bc_scheduler_plan_relative(_bc_hc_sr04.task_id_interval, _bc_hc_sr04.update_interval);
88 
90  }
91 }
92 
94 {
95  if (_bc_hc_sr04.measurement_active)
96  {
97  return false;
98  }
99 
100  // Enable PLL
101  bc_system_pll_enable();
102 
103  _bc_hc_sr04.measurement_active = true;
104 
105  _bc_hc_sr04.measurement_valid = false;
106 
107  _bc_hc_sr04.task_id_notify = bc_scheduler_register(_bc_hc_sr04_task_notify, NULL, BC_TICK_INFINITY);
108 
109  // Set timeout register (250 milliseconds)
110  TIM3->CCR1 = 250000 / _BC_HC_SR04_RESOLUTION;
111 
112  // Trigger update
113  TIM3->EGR = TIM_EGR_UG;
114 
115  // Clear Capture 4 / Capture 3 overcapture flags
116  TIM3->SR &= ~(TIM_SR_CC4OF | TIM_SR_CC3OF);
117 
118  // Clear Capture 4 / Capture 3 / Compare 1 interrupt flags
119  TIM3->SR &= ~(TIM_SR_CC4IF | TIM_SR_CC3IF | TIM_SR_CC1IF);
120 
121  // Enable timer counter
122  TIM3->CR1 |= TIM_CR1_CEN;
123 
124  // Enable Capture 4 / Capture 3
125  TIM3->CCER |= TIM_CCER_CC4E | TIM_CCER_CC3E;
126 
127  // Enable Capture 4 / Compare 1 interrupt
128  TIM3->DIER |= TIM_DIER_CC4IE | TIM_DIER_CC1IE;
129 
131 
132  bc_timer_start();
133 
134  bc_timer_delay(10);
135 
136  bc_timer_stop();
137 
139 
140  return true;
141 }
142 
143 bool bc_hc_sr04_get_distance_millimeter(float *millimeter)
144 {
145  if (!_bc_hc_sr04.measurement_valid)
146  {
147  return false;
148  }
149 
150  *millimeter = _BC_HC_SR04_RESOLUTION * (float) _bc_hc_sr04.echo_duration / 5.8;
151 
152  return true;
153 }
154 
155 static void _bc_hc_sr04_task_interval(void *param)
156 {
157  (void) param;
158 
160 
161  bc_scheduler_plan_current_relative(_bc_hc_sr04.update_interval);
162 }
163 
164 static void _bc_hc_sr04_task_notify(void *param)
165 {
166  (void) param;
167 
168  // Disable PLL
169  bc_system_pll_disable();
170 
171  _bc_hc_sr04.measurement_active = false;
172 
173  bc_scheduler_unregister(_bc_hc_sr04.task_id_notify);
174 
175  if (!_bc_hc_sr04.measurement_valid)
176  {
177  if (_bc_hc_sr04.event_handler != NULL)
178  {
179  _bc_hc_sr04.event_handler(BC_HC_SR04_EVENT_ERROR, _bc_hc_sr04.event_param);
180  }
181  }
182  else if (_bc_hc_sr04.event_handler != NULL)
183  {
184  _bc_hc_sr04.event_handler(BC_HC_SR04_EVENT_UPDATE, _bc_hc_sr04.event_param);
185  }
186 }
187 
188 static void _bc_hc_sr04_iqr_handler(void *param)
189 {
190  (void) param;
191 
192  // Disable Capture 4 / Compare 1 interrupt
193  TIM3->DIER &= ~(TIM_DIER_CC4IE | TIM_DIER_CC1IE);
194 
195  // Disable Capture 4 / Capture 3
196  TIM3->CCER &= ~(TIM_CCER_CC4E | TIM_CCER_CC3E);
197 
198  // Disable timer counter
199  TIM3->CR1 &= ~TIM_CR1_CEN;
200 
201  // If not Compare 1 interrupt... (timeout)
202  if ((TIM3->SR & TIM_SR_CC1IF) == 0)
203  {
204  // If not Capture 4 / Capture 3 overcapture...
205  if ((TIM3->SR & TIM_SR_CC4OF) == 0 && (TIM3->SR & TIM_SR_CC3OF) == 0)
206  {
207  // If Capture 4 interrupt... (falling edge)
208  if ((TIM3->SR & TIM_SR_CC4IF) != 0)
209  {
210  // Retrieve falling edge capture mark
211  uint16_t falling_capture = TIM3->CCR4;
212 
213  // If Capture 3 interrupt... (rising edge)
214  if ((TIM3->SR & TIM_SR_CC3IF) != 0)
215  {
216  // Retrieve rising edge capture mark
217  uint16_t rising_capture = TIM3->CCR3;
218 
219  // Calculate echo duration (distance between rising and falling edge)
220  _bc_hc_sr04.echo_duration = falling_capture - rising_capture;
221 
222  if (_bc_hc_sr04.echo_duration <= 30000 / _BC_HC_SR04_RESOLUTION)
223  {
224  // Indicate success
225  _bc_hc_sr04.measurement_valid = true;
226  }
227  }
228  }
229  }
230  }
231 
232  // Schedule task for immediate execution
233  bc_scheduler_plan_now(_bc_hc_sr04.task_id_notify);
234 }
235 
236 
uint64_t bc_tick_t
Timestamp data type.
Definition: bc_tick.h:16
void bc_scheduler_plan_absolute(bc_scheduler_task_id_t task_id, bc_tick_t tick)
Schedule specified task to absolute tick.
Definition: bc_scheduler.c:124
bool bc_hc_sr04_measure(void)
Start measurement manually.
Definition: bc_hc_sr04.c:93
void bc_timer_start(void)
Start timer.
Definition: bc_timer.c:24
bc_scheduler_task_id_t bc_scheduler_register(void(*task)(void *), void *param, bc_tick_t tick)
Register task in scheduler.
Definition: bc_scheduler.c:56
void bc_scheduler_plan_now(bc_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
Definition: bc_scheduler.c:119
void bc_timer_delay(uint16_t microseconds)
Relative delay.
Definition: bc_timer.c:40
bool bc_hc_sr04_get_distance_millimeter(float *millimeter)
Get measured distance in millimeters.
Definition: bc_hc_sr04.c:143
void bc_gpio_set_output(bc_gpio_channel_t channel, int state)
Set output state for GPIO channel.
Definition: bc_gpio.c:442
GPIO channel operates in alternate mode AF2.
Definition: bc_gpio.h:120
void bc_gpio_set_mode(bc_gpio_channel_t channel, bc_gpio_mode_t mode)
Set mode of operation for GPIO channel.
Definition: bc_gpio.c:340
void bc_scheduler_plan_current_relative(bc_tick_t tick)
Schedule current task to tick relative from current spin.
Definition: bc_scheduler.c:149
void bc_timer_stop(void)
Stop timer.
Definition: bc_timer.c:55
size_t bc_scheduler_task_id_t
Task ID assigned by scheduler.
Definition: bc_scheduler.h:18
GPIO channel P8.
Definition: bc_gpio.h:39
bc_hc_sr04_event_t
Definition: bc_hc_sr04.h:10
void bc_scheduler_plan_relative(bc_scheduler_task_id_t task_id, bc_tick_t tick)
Schedule specified task to tick relative from current spin.
Definition: bc_scheduler.c:129
Update event.
Definition: bc_hc_sr04.h:16
void bc_hc_sr04_set_event_handler(void(*event_handler)(bc_hc_sr04_event_t, void *), void *event_param)
Set callback function.
Definition: bc_hc_sr04.c:71
bool bc_timer_set_irq_handler(TIM_TypeDef *tim, void(*irq_handler)(void *), void *irq_param)
Register timer IRQ handler.
Definition: bc_timer.c:68
Error event.
Definition: bc_hc_sr04.h:13
GPIO channel P9.
Definition: bc_gpio.h:42
#define BC_TICK_INFINITY
Maximum timestamp value.
Definition: bc_tick.h:12
void bc_timer_init(void)
Initialize timer.
Definition: bc_timer.c:18
void bc_hc_sr04_set_update_interval(bc_tick_t interval)
Set measurement interval.
Definition: bc_hc_sr04.c:77
void bc_gpio_init(bc_gpio_channel_t channel)
Initialize GPIO channel.
Definition: bc_gpio.c:301
void bc_scheduler_unregister(bc_scheduler_task_id_t task_id)
Unregister specified task.
Definition: bc_scheduler.c:80
GPIO channel operates as output.
Definition: bc_gpio.h:102