MotorController
i2c_interface.c
1 #include "i2c_interface.h"
2 #include "i2c_lld.h"
3 #include "i2cslave.h"
4 #include "settings.h"
5 #include "control.h"
6 #include "orientation.h"
7 #include "position.h"
8 #include "coding_wheels.h"
9 #include "data_storage.h"
10 
11 #include <string.h> //for memcpy
12 
13 #include "RTT/SEGGER_RTT.h"
14 
15 #define I2C_TX_BUFFER_SIZE 2
16 #define I2C_RX_BUFFER_SIZE 5
17 #define NO_DATA 0xFF
18 
19 /*
20  * Buffer to receive the message sent by the I2C master.
21  */
22 static volatile uint8_t rx_buffer[I2C_RX_BUFFER_SIZE];
23 /*
24  * Buffer to prepate the response to send to the I2C master.
25  */
26 static volatile uint8_t tx_buffer[I2C_TX_BUFFER_SIZE];
27 
28 /*
29  * Indicates whether an I2C error occurred.
30  */
31 static volatile bool error = FALSE;
32 
33 static virtual_timer_t i2c_vt;
34 
35 /*
36  * Configuration of the I2C driver.
37  */
38 static I2CConfig i2c_slave_cfg = {
39  0x20420F13, /* I2C clock = 100kHz, see table 141 of the reference manual */
40  0, /* Nothing to do, the driver will set the PE bit */
41  0, /* Nothing to do, all fields controlled by the driver */
42  NULL/* Slave mode */
43 };
44 
45 static I2CSlaveMsgCB i2c_error, i2c_reply, i2c_address_match;
46 /*
47  * The i2c_request object is used whenever a message is received. The
48  * ```i2c_address_match``` handler is called just after the device address has
49  * been recognized as one of the device addresses. This handler simply starts a
50  * timer so that the callback associated with this timer is called after the end
51  * of the message. It would have been much better to have a handler called at
52  * the end of the message but this functionnality doesn't seem to work for the
53  * time being. This solution is thus a dirty work-around.
54  *
55  * The i2c_response object is used only when a "read" request is received. The
56  * content of the specified buffer is sent and then the ```i2c_reply``` handler
57  * is called (after the transmission thus).
58  *
59  * In the implementation proposed here, it's the i2c_request handler that fills
60  * the tx_buffer with the appropriate value. Indeed, this I2C slave follows the
61  * read-after-write scheme. A "read" request is thus preceded by a "write" request,
62  * with the address of the 'register' to read. When the read request is received,
63  * the ```i2c_address_match``` handler is called and the rx_buffer contains the
64  * address of the register to read. The handler can thus analyse this address and
65  * fill the tx_buffer with the appropriate value before the content of this buffer
66  * is sent to the I2C master. In that case, the handler also stops the timer that
67  * has been started when the "write" request with the register address has been
68  * received because it's in fact not a true "write" request.
69  *
70  * If a direct read request is sent by the master, the returned value will be the
71  * one set in the tx_buffer last time (by the last "read-after-write" request).
72  */
73 
74 /*
75  * Data structure used to handle incoming requests from an I2C master.
76  */
77 const I2CSlaveMsg i2c_request = {
78  I2C_RX_BUFFER_SIZE,
79  (uint8_t*)rx_buffer,
80  i2c_address_match,
81  NULL,
82  i2c_error
83 };
84 
85 /*
86  * Data structure used to send a response to an I2C master.
87  */
88 I2CSlaveMsg i2c_response = {
89  I2C_TX_BUFFER_SIZE,
90  (uint8_t*)tx_buffer,
91  NULL,
92  i2c_reply,
93  i2c_error
94 };
95 
96 /* casts a float to an int32_t which has exactly the same binary representation */
97 static int32_t float_to_int32(float x){
98  int32_t result;
99  memcpy(&result, &x, sizeof(result));
100  return result;
101 }
102 
103 static float int32_to_float(int32_t x){
104  float result;
105  memcpy(&result, &x, sizeof(result));
106  return result;
107 }
108 
109 
110 /*
111  * @brief Handler called when a "read" request has been served.
112  */
113 static void i2c_reply(I2CDriver* i2cp) {
114  (void)i2cp;
115 }
116 
117 /*
118  * @brief Handle an error in the I2C connection.
119  */
120 void i2c_error(I2CDriver* i2cp)
121 {
122  (void)i2cp;
123  error = TRUE;
124 }
125 
126 static void rx_special_cases(uint8_t addr) {
127  static int32_t tmp_right_wheel_dist = 0;
128  static int32_t tmp_left_wheel_dist = 0;
129  static int32_t tmp_goal_mean_dist = 0;
130  static int32_t tmp;
131  static int32_t tmp2;
132  static int32_t tmp_cur_x;
133  static int32_t tmp_cur_y;
134 
135  printf("rx_buffer = %x %d %d\n", rx_buffer[0], rx_buffer[1], rx_buffer[2]);
136  /* Process the write message received */
137  switch (addr) {
138  case STORE_DATA_IN_FLASH_ADDR:
140  break;
141  case RESET_ORIENTATION_ADDR:
142  chBSemSignal(&reset_orientation_sem);
143  break;
144  case CUR_POS_X_LOW_ADDR:
145  tmp_cur_x = (rx_buffer[2] << 8) | rx_buffer[1];
146  break;
147  case CUR_POS_X_HIGH_ADDR:
148  tmp_cur_x |= (rx_buffer[2] << 24) | (rx_buffer[1] << 16);
149  cur_pos.x = int32_to_float(tmp_cur_x);
150  break;
151  case CUR_POS_Y_LOW_ADDR:
152  tmp_cur_y = (rx_buffer[2] << 8) | rx_buffer[1];
153  break;
154  case CUR_POS_Y_HIGH_ADDR:
155  tmp_cur_y |= (rx_buffer[2] << 24) | (rx_buffer[1] << 16);
156  cur_pos.y = int32_to_float(tmp_cur_y);
157  break;
158  case CUR_RIGHT_WHEEL_DIST_LOW_ADDR:
159  tmp_right_wheel_dist = (rx_buffer[2] << 8) | rx_buffer[1];
160  break;
161  case CUR_RIGHT_WHEEL_DIST_HIGH_ADDR:
162  tmp_right_wheel_dist |= (rx_buffer[2] << 24) | (rx_buffer[1] << 16);
163  right_ticks = tmp_right_wheel_dist * settings.ticks_per_m / 100;
164  break;
165  case CUR_LEFT_WHEEL_DIST_LOW_ADDR:
166  tmp_left_wheel_dist = (rx_buffer[2] << 8) | rx_buffer[1];
167  break;
168  case CUR_LEFT_WHEEL_DIST_HIGH_ADDR:
169  tmp_left_wheel_dist |= (rx_buffer[2] << 24) | (rx_buffer[1] << 16);
170  left_ticks = tmp_left_wheel_dist * settings.ticks_per_m / 100;
171  break;
172  case CUR_HEADING_ADDR:
173  tmp = (rx_buffer[2] << 8) | rx_buffer[1];
174  tmp2 = orientation - heading_offset;
175  while (tmp2 < HEADING_MIN_VALUE) {
176  tmp2 += HEADING_RANGE;
177  }
178  while (tmp2 > HEADING_MAX_VALUE) {
179  tmp2 -= HEADING_RANGE;
180  }
181  heading_offset = tmp - tmp2;
182  goal.heading = tmp;
183  break;
184  case GOAL_MEAN_DIST_LOW_ADDR:
185  tmp_goal_mean_dist = (rx_buffer[2] << 8) | rx_buffer[1];
186  break;
187  case GOAL_MEAN_DIST_HIGH_ADDR:
188  tmp_goal_mean_dist |= (rx_buffer[2] << 24) | (rx_buffer[1] << 16);
189  goal.mean_dist = tmp_goal_mean_dist;
190  dist_command_received = TRUE;
191  break;
192  case GOAL_HEADING_ADDR:
193  /* Ignore if not valid */
194  if (((rx_buffer[2] << 8) | rx_buffer[1]) <= 360) {
195  /* IMU unit = 16 * degree */
196  goal.heading = ((rx_buffer[2] << 8) | rx_buffer[1]) * 16;
197  }
198  else {
199  printf("Invalid GOAL_HEADING received : rx_buffer = 0x%x; %d; %d\n",
200  rx_buffer[0], rx_buffer[1], rx_buffer[2]);
201  }
202  break;
203  default:
204  break;
205  }
206 }
207 
208 static void tx_special_cases(uint8_t addr, uint16_t *value) {
209  static int32_t saved_left_wheel_dist = 0;
210  static int32_t saved_right_wheel_dist = 0;
211  static int32_t saved_cur_x;
212  static int32_t saved_cur_y;
213 
214  switch (addr) {
215  case CUR_POS_X_LOW_ADDR:
216  saved_cur_x = float_to_int32(cur_pos.x);
217  *value = saved_cur_x & 0x0000FFFFU;
218  break;
219  case CUR_POS_X_HIGH_ADDR:
220  *value = (saved_cur_x & 0xFFFF0000U) >> 16U;
221  break;
222  case CUR_POS_Y_LOW_ADDR:
223  saved_cur_y = float_to_int32(cur_pos.y);
224  *value = saved_cur_y & 0x0000FFFFU;
225  break;
226  case CUR_POS_Y_HIGH_ADDR:
227  *value = (saved_cur_y & 0xFFFF0000) >> 16U;
228  break;
229  case CUR_RIGHT_WHEEL_DIST_LOW_ADDR:
230  saved_right_wheel_dist = 100 * right_ticks / settings.ticks_per_m;
231  *value = saved_right_wheel_dist & 0x0000FFFF;
232  break;
233  case CUR_RIGHT_WHEEL_DIST_HIGH_ADDR:
234  *value = (saved_right_wheel_dist & 0xFFFF0000) >> 16U;
235  break;
236  case CUR_LEFT_WHEEL_DIST_LOW_ADDR:
237  saved_left_wheel_dist = 100 * left_ticks / settings.ticks_per_m;
238  *value = saved_left_wheel_dist & 0x0000FFFF;
239  break;
240  case CUR_LEFT_WHEEL_DIST_HIGH_ADDR:
241  *value = (saved_left_wheel_dist & 0xFFFF0000) >> 16U;
242  break;
243  case CUR_HEADING_ADDR:
244  *value = orientation / 16;
245  break;
246  case GOAL_HEADING_ADDR:
247  *value = goal.heading / 16;
248  break;
249  default:
250  *value = NO_DATA;
251  break;
252  }
253 }
254 
255 /* Include the generated code */
256 #include "i2c_interface_gen.c"
257 
258 /*
259  * @brief Start the I2C driver.
260  */
261 extern void i2c_slave_init(I2CDriver* i2cp)
262 {
263  int i;
264  if (i2cp == NULL) {
265  return;
266  } else {
267  /* Create the timer */
268  chVTObjectInit(&i2c_vt);
269 
270  /* Initialise the buffers */
271  tx_buffer[0] = 42;
272  tx_buffer[1] = 37;
273 
274  for (i = 0; i < I2C_RX_BUFFER_SIZE; ++i) {
275  rx_buffer[i] = NO_DATA;
276  }
277 
278  /* Start the I2C driver */
279  ((I2CDriver*)i2cp)->slaveTimeout = MS2ST(100);
280  i2cStart(i2cp, &i2c_slave_cfg);
281  i2cSlaveConfigure(i2cp, &i2c_request, &i2c_response);
282  i2cMatchAddress(i2cp, I2C_SLAVE_ADDRESS);
283  }
284 }
position_t cur_pos
Definition: position.c:22
int16_t orientation
Definition: orientation.c:32
int32_t store_data_in_flash(void)
Save configuration data in flash.
Definition: data_storage.c:22
int16_t heading_offset
Definition: orientation.c:31
uint16_t ticks_per_m
Definition: settings.h:42
volatile robot_settings_t settings
Global variable used to store the configuration in use.
Definition: settings.c:9