Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_module_relay.c
1 #include <bc_module_relay.h>
2 
3 static void _bc_module_relay_task(void *param);
4 
5 #define BC_MODULE_RELAY_POLARITY_F ((1 << 6) | (1 << 7))
6 #define BC_MODULE_RELAY_POLARITY_T ((1 << 4) | (1 << 5))
7 #define BC_MODULE_RELAY_POLARITY_NONE ((1 << 6) | (1 << 4))
8 
9 
10 static bool _bc_module_relay_hardware_init(bc_module_relay_t *self)
11 {
12  // Init i2C expander driver
13  if (!bc_tca9534a_init(&self->_tca9534a, BC_I2C_I2C0, self->_i2c_address))
14  {
15  return false;
16  }
17  // De-energize bistable relay coil - turn off
18  bc_tca9534a_write_port(&(self->_tca9534a), BC_MODULE_RELAY_POLARITY_NONE);
19  // Enable outputs
20  bc_tca9534a_set_port_direction(&self->_tca9534a, 0x00); // inverted: 0 = output
21  // Relay is bi-stable, in the begining we don't know the default state
22  self->_relay_state = BC_MODULE_RELAY_STATE_UNKNOWN;
23 
24  return true;
25 }
26 
27 bool bc_module_relay_init(bc_module_relay_t *self, uint8_t i2c_address)
28 {
29  // Init instance, set state machine initial state
30  memset(self, 0, sizeof(*self));
31  self->_i2c_address = i2c_address;
32  return _bc_module_relay_hardware_init(self);
33 }
34 
35 
36 static void bc_module_relay_scheduler_unregister(bc_module_relay_t *self)
37 {
38  // Check if there is already a task running
39  if (self->_task_is_active)
40  {
41  // Unregister running task
42  bc_scheduler_unregister(self->_task_id);
43  // Clear task id
44  self->_task_id = 0;
45  self->_task_is_active = false;
46  }
47 }
48 
49 static void bc_module_relay_scheduler_register(bc_module_relay_t *self)
50 {
51  // Exit if there's already registered task
52  if (self->_task_is_active)
53  {
54  return;
55  }
56 
57  // Register relay task
58  self->_task_id = bc_scheduler_register(_bc_module_relay_task, self, 0);
59  self->_task_is_active = true;
60 }
61 
62 static void _bc_module_relay_set_output(bc_module_relay_t *self, bc_module_relay_state_t state)
63 {
64  if (state == BC_MODULE_RELAY_STATE_TRUE)
65  {
66  if (!bc_tca9534a_write_port(&self->_tca9534a, BC_MODULE_RELAY_POLARITY_T)) // pol A
67  {
68  self->_error = true;
69  }
70  }
71  else
72  {
73  if (!bc_tca9534a_write_port(&self->_tca9534a, BC_MODULE_RELAY_POLARITY_F)) // pol B
74  {
75  self->_error = true;
76  }
77  }
78 }
79 
80 static void _bc_module_relay_set_output_disable(bc_module_relay_t *self)
81 {
82  if (!bc_tca9534a_write_port(&(self->_tca9534a), BC_MODULE_RELAY_POLARITY_NONE))
83  {
84  self->_error = true;
85  }
86 }
87 
88 static bc_tick_t bc_module_relay_state_machine(bc_module_relay_t *self, bc_tick_t tick_now)
89 {
90  while (true)
91  {
92  switch (self->_state)
93  {
94  case BC_MODULE_RELAY_TASK_STATE_IDLE:
95  // Handle Error
96  if (self->_error)
97  {
98  // Try to initialize relay module again
99  if (_bc_module_relay_hardware_init(self))
100  {
101  self->_error = false;
102  }
103  }
104 
105  // Handle commands
106  if (self->_command == BC_MODULE_RELAY_COMMAND_SET)
107  {
108  self->_command = BC_MODULE_RELAY_COMMAND_NONE;
109  self->_state = BC_MODULE_RELAY_TASK_STATE_SET;
110  continue;
111  }
112 
113  if (self->_command == BC_MODULE_RELAY_COMMAND_PULSE)
114  {
115  self->_command = BC_MODULE_RELAY_COMMAND_NONE;
116  self->_state = BC_MODULE_RELAY_TASK_STATE_PULSE;
117  continue;
118  }
119 
120  // Unregister task if no command is needed
121  bc_module_relay_scheduler_unregister(self);
122  return tick_now;
123 
124  //
125  // Relay set start state
126  //
127  case BC_MODULE_RELAY_TASK_STATE_SET:
128  // Set relay to the selected polarity
129  _bc_module_relay_set_output(self, self->_desired_state);
130  self->_relay_state = self->_desired_state;
131 
132  self->_state = BC_MODULE_RELAY_TASK_STATE_SET_DEMAGNETIZE;
133  return tick_now + 20;
134 
135  case BC_MODULE_RELAY_TASK_STATE_SET_DEMAGNETIZE:
136  // De-energize bistable relay coil - turn off
137  _bc_module_relay_set_output_disable(self);
138 
139  self->_state = BC_MODULE_RELAY_TASK_STATE_IDLE;
140  // Needs 100ms to let the capacitor on relay board to charge
141  return tick_now + 100;
142 
143  //
144  // Relay pulse start state
145  //
146  case BC_MODULE_RELAY_TASK_STATE_PULSE:
147  // Create pulse of the set polarity
148  _bc_module_relay_set_output(self, self->_desired_state);
149  self->_relay_state = self->_desired_state;
150 
151  self->_state = BC_MODULE_RELAY_TASK_STATE_PULSE_DEMAGNETIZE;
152  return tick_now + 20;
153 
154  case BC_MODULE_RELAY_TASK_STATE_PULSE_DEMAGNETIZE:
155  // De-energize bistable relay coil - turn off
156  _bc_module_relay_set_output_disable(self);
157 
158  self->_state = BC_MODULE_RELAY_TASK_STATE_PULSE_REVERSE;
159  return tick_now + self->_pulse_duration;
160 
161  case BC_MODULE_RELAY_TASK_STATE_PULSE_REVERSE:
162  // Change actual relay state to the oposite polarity
163  self->_relay_state = (self->_relay_state == BC_MODULE_RELAY_STATE_TRUE) ? BC_MODULE_RELAY_STATE_FALSE : BC_MODULE_RELAY_STATE_TRUE;
164  _bc_module_relay_set_output(self, self->_relay_state);
165 
166  self->_state = BC_MODULE_RELAY_TASK_STATE_PULSE_DEMAGNETIZE_2;
167  return tick_now + 20;
168 
169  case BC_MODULE_RELAY_TASK_STATE_PULSE_DEMAGNETIZE_2:
170  // De-energize bistable relay coil - turn off
171  _bc_module_relay_set_output_disable(self);
172 
173  self->_state = BC_MODULE_RELAY_TASK_STATE_IDLE;
174  // Needs 100ms to let the capacitor on relay board to charge
175  return tick_now + 100;
176 
177  default:
178  break;
179  }
180  }
181 }
182 
183 static void _bc_module_relay_task(void *param)
184 {
185  bc_module_relay_t *self = param;
186 
187  bc_scheduler_plan_current_absolute(bc_module_relay_state_machine(self, bc_scheduler_get_spin_tick()));
188 }
189 
191 {
192  // Save set command
193  self->_command = BC_MODULE_RELAY_COMMAND_SET;
194  self->_desired_state = (state) ? BC_MODULE_RELAY_STATE_TRUE : BC_MODULE_RELAY_STATE_FALSE;
195 
196  bc_module_relay_scheduler_register(self);
197 }
198 
200 {
201  if (self->_relay_state == BC_MODULE_RELAY_STATE_FALSE)
202  {
204  }
205  else if (self->_relay_state == BC_MODULE_RELAY_STATE_TRUE)
206  {
208  }
209 }
210 
211 void bc_module_relay_pulse(bc_module_relay_t *self, bool state, bc_tick_t duration)
212 {
213  // Save pulse duration
214  self->_command = BC_MODULE_RELAY_COMMAND_PULSE;
215  self->_pulse_duration = duration;
216  self->_desired_state = (state) ? BC_MODULE_RELAY_STATE_TRUE : BC_MODULE_RELAY_STATE_FALSE;
217 
218  bc_module_relay_scheduler_register(self);
219 }
220 
222 {
223  return self->_relay_state;
224 }
bc_module_relay_state_t
Reported relay states.
bool bc_module_relay_init(bc_module_relay_t *self, uint8_t i2c_address)
Initialize BigClown Relay Module.
uint64_t bc_tick_t
Timestamp data type.
Definition: bc_tick.h:16
bc_module_relay_state_t bc_module_relay_get_state(bc_module_relay_t *self)
Get current relay state.
bc_tick_t bc_scheduler_get_spin_tick(void)
Get current tick of spin in which task has been run.
Definition: bc_scheduler.c:104
bool bc_tca9534a_set_port_direction(bc_tca9534a_t *self, uint8_t direction)
Set direction of all pins.
Definition: bc_tca9534a.c:87
I2C channel I2C0.
Definition: bc_i2c.h:18
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
bool bc_tca9534a_write_port(bc_tca9534a_t *self, uint8_t state)
Write state to all pins.
Definition: bc_tca9534a.c:35
void bc_scheduler_plan_current_absolute(bc_tick_t tick)
Schedule current task to absolute tick.
Definition: bc_scheduler.c:144
void bc_module_relay_pulse(bc_module_relay_t *self, bool state, bc_tick_t duration)
Generate pulse to specified state for given duration.
bool bc_tca9534a_init(bc_tca9534a_t *self, bc_i2c_channel_t i2c_channel, uint8_t i2c_address)
Initialize TCA9534A.
Definition: bc_tca9534a.c:8
void bc_module_relay_set_state(bc_module_relay_t *self, bool state)
Set relay to specified state.
void bc_module_relay_toggle(bc_module_relay_t *self)
Toggle relay to opposite state.
struct bc_module_relay_t bc_module_relay_t
BigClown Relay Module instance.
void bc_scheduler_unregister(bc_scheduler_task_id_t task_id)
Unregister specified task.
Definition: bc_scheduler.c:80