PX4 Firmware
PX4 Autopilot Software http://px4.io
atxxxx.cpp
Go to the documentation of this file.
1 /****************************************************************************
2  *
3  * Copyright (c) 2018 PX4 Development Team. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * 3. Neither the name PX4 nor the names of its contributors may be
16  * used to endorse or promote products derived from this software
17  * without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  *
32  ****************************************************************************/
33 
34 /**
35  * @file atxxxx.cpp
36  * @author Daniele Pettenuzzo
37  * @author Beat Küng <beat-kueng@gmx.net>
38  *
39  * Driver for the ATXXXX chip (e.g. MAX7456) on the omnibus f4 fcu connected via SPI.
40  */
41 
42 #include "atxxxx.h"
43 #include "symbols.h"
44 
45 using namespace time_literals;
46 
47 static constexpr uint32_t OSD_UPDATE_RATE{500_ms}; // 2 Hz
48 
50  SPI("OSD", nullptr, bus, PX4_MK_SPI_SEL(bus, OSD_SPIDEV), SPIDEV_MODE0, OSD_SPI_BUS_SPEED),
51  ModuleParams(nullptr),
52  ScheduledWorkItem(MODULE_NAME, px4::device_bus_to_wq(get_device_id()))
53 {
54 }
55 
57 {
58  ScheduleClear();
59 }
60 
61 int
62 OSDatxxxx::task_spawn(int argc, char *argv[])
63 {
64  int ch;
65  int myoptind = 1;
66  const char *myoptarg = nullptr;
67  int spi_bus = OSD_BUS;
68 
69  while ((ch = px4_getopt(argc, argv, "b:", &myoptind, &myoptarg)) != EOF) {
70  switch (ch) {
71  case 'b':
72  spi_bus = (uint8_t)atoi(myoptarg);
73  break;
74  }
75  }
76 
77  OSDatxxxx *osd = new OSDatxxxx(spi_bus);
78 
79  if (!osd) {
80  PX4_ERR("alloc failed");
81  return PX4_ERROR;
82  }
83 
84  if (osd->init() != PX4_OK) {
85  delete osd;
86  return PX4_ERROR;
87  }
88 
89  _object.store(osd);
90  _task_id = task_id_is_work_queue;
91 
92  osd->start();
93 
94  return PX4_OK;
95 }
96 
97 int
99 {
100  /* do SPI init (and probe) first */
101  int ret = SPI::init();
102 
103  if (ret != PX4_OK) {
104  return ret;
105  }
106 
107  ret = reset();
108 
109  if (ret != PX4_OK) {
110  return ret;
111  }
112 
113  ret = init_osd();
114 
115  if (ret != PX4_OK) {
116  return ret;
117  }
118 
119  // clear the screen
120  int num_rows = (_param_osd_atxxxx_cfg.get() == 1 ? OSD_NUM_ROWS_NTSC : OSD_NUM_ROWS_PAL);
121 
122  for (int i = 0; i < OSD_CHARS_PER_ROW; i++) {
123  for (int j = 0; j < num_rows; j++) {
124  add_character_to_screen(' ', i, j);
125  }
126  }
127 
128  return ret;
129 }
130 
131 int
133 {
134  ScheduleOnInterval(OSD_UPDATE_RATE, 10000);
135 
136  return PX4_OK;
137 }
138 
139 int
141 {
142  uint8_t data = 0;
143  int ret = PX4_OK;
144 
145  ret |= writeRegister(0x00, 0x01); //disable video output
146  ret |= readRegister(0x00, &data, 1);
147 
148  if (data != 1 || ret != PX4_OK) {
149  PX4_ERR("probe failed (%i %i)", ret, data);
150  }
151 
152  return ret;
153 }
154 
155 int
157 {
158  int ret = PX4_OK;
159  uint8_t data = OSD_ZERO_BYTE;
160 
161  if (_param_osd_atxxxx_cfg.get() == 2) {
162  data |= OSD_PAL_TX_MODE;
163  }
164 
165  ret |= writeRegister(0x00, data);
166  ret |= writeRegister(0x04, OSD_ZERO_BYTE);
167 
168  enable_screen();
169 
170  return ret;
171 }
172 
173 int
174 OSDatxxxx::readRegister(unsigned reg, uint8_t *data, unsigned count)
175 {
176  uint8_t cmd[5] {}; // read up to 4 bytes
177 
178  cmd[0] = DIR_READ(reg);
179 
180  int ret = transfer(&cmd[0], &cmd[0], count + 1);
181 
182  if (ret != PX4_OK) {
183  DEVICE_LOG("spi::transfer returned %d", ret);
184  return ret;
185  }
186 
187  memcpy(&data[0], &cmd[1], count);
188 
189  return ret;
190 }
191 
192 int
193 OSDatxxxx::writeRegister(unsigned reg, uint8_t data)
194 {
195  uint8_t cmd[2] {}; // write 1 byte
196 
197  cmd[0] = DIR_WRITE(reg);
198  cmd[1] = data;
199 
200  int ret = transfer(&cmd[0], nullptr, 2);
201 
202  if (OK != ret) {
203  DEVICE_LOG("spi::transfer returned %d", ret);
204  return ret;
205  }
206 
207  return ret;
208 }
209 
210 int
211 OSDatxxxx::add_character_to_screen(char c, uint8_t pos_x, uint8_t pos_y)
212 {
213  uint16_t position = (OSD_CHARS_PER_ROW * pos_y) + pos_x;
214  uint8_t position_lsb = 0;
215  int ret = PX4_ERROR;
216 
217  if (position > 0xFF) {
218  position_lsb = static_cast<uint8_t>(position) - 0xFF;
219  ret = writeRegister(0x05, 0x01); //DMAH
220 
221  } else {
222  position_lsb = static_cast<uint8_t>(position);
223  ret = writeRegister(0x05, 0x00); //DMAH
224  }
225 
226  if (ret != 0) {
227  return ret;
228  }
229 
230  ret = writeRegister(0x06, position_lsb); //DMAL
231 
232  if (ret != 0) {
233  return ret;
234  }
235 
236  ret = writeRegister(0x07, c);
237 
238  return ret;
239 }
240 
241 void
242 OSDatxxxx::add_string_to_screen_centered(const char *str, uint8_t pos_y, int max_length)
243 {
244  int len = strlen(str);
245 
246  if (len > max_length) {
247  len = max_length;
248  }
249 
250  int pos = (OSD_CHARS_PER_ROW - max_length) / 2;
251  int before = (max_length - len) / 2;
252 
253  for (int i = 0; i < before; ++i) {
254  add_character_to_screen(' ', pos++, pos_y);
255  }
256 
257  for (int i = 0; i < len; ++i) {
258  add_character_to_screen(str[i], pos++, pos_y);
259  }
260 
261  while (pos < (OSD_CHARS_PER_ROW + max_length) / 2) {
262  add_character_to_screen(' ', pos++, pos_y);
263  }
264 }
265 
266 void
267 OSDatxxxx::clear_line(uint8_t pos_x, uint8_t pos_y, int length)
268 {
269  for (int i = 0; i < length; ++i) {
270  add_character_to_screen(' ', pos_x + i, pos_y);
271  }
272 }
273 
274 int
275 OSDatxxxx::add_battery_info(uint8_t pos_x, uint8_t pos_y)
276 {
277  char buf[10];
278  int ret = PX4_OK;
279 
280  // TODO: show battery symbol based on battery fill level
281  snprintf(buf, sizeof(buf), "%c%5.2f", OSD_SYMBOL_BATT_3, (double)_battery_voltage_filtered_v);
282  buf[sizeof(buf) - 1] = '\0';
283 
284  for (int i = 0; buf[i] != '\0'; i++) {
285  ret |= add_character_to_screen(buf[i], pos_x + i, pos_y);
286  }
287 
288  ret |= add_character_to_screen('V', pos_x + 5, pos_y);
289 
290  pos_y++;
291  pos_x++;
292 
293  snprintf(buf, sizeof(buf), "%5d", (int)_battery_discharge_mah);
294  buf[sizeof(buf) - 1] = '\0';
295 
296  for (int i = 0; buf[i] != '\0'; i++) {
297  ret |= add_character_to_screen(buf[i], pos_x + i, pos_y);
298  }
299 
300  ret |= add_character_to_screen(OSD_SYMBOL_MAH, pos_x + 5, pos_y);
301 
302  return ret;
303 }
304 
305 int
306 OSDatxxxx::add_altitude(uint8_t pos_x, uint8_t pos_y)
307 {
308  char buf[16];
309  int ret = PX4_OK;
310 
311  snprintf(buf, sizeof(buf), "%c%5.2f%c", OSD_SYMBOL_ARROW_NORTH, (double)_local_position_z, OSD_SYMBOL_M);
312  buf[sizeof(buf) - 1] = '\0';
313 
314  for (int i = 0; buf[i] != '\0'; i++) {
315  ret |= add_character_to_screen(buf[i], pos_x + i, pos_y);
316  }
317 
318  return ret;
319 }
320 
321 int
322 OSDatxxxx::add_flighttime(float flight_time, uint8_t pos_x, uint8_t pos_y)
323 {
324  char buf[10];
325  int ret = PX4_OK;
326 
327  snprintf(buf, sizeof(buf), "%c%5.1f", OSD_SYMBOL_FLIGHT_TIME, (double)flight_time);
328  buf[sizeof(buf) - 1] = '\0';
329 
330  for (int i = 0; buf[i] != '\0'; i++) {
331  ret |= add_character_to_screen(buf[i], pos_x + i, pos_y);
332  }
333 
334  return ret;
335 }
336 
337 int
339 {
340  uint8_t data = 0;
341  int ret = PX4_OK;
342 
343  ret |= readRegister(0x00, &data, 1);
344  ret |= writeRegister(0x00, data | 0x48);
345 
346  return ret;
347 }
348 
349 int
351 {
352  uint8_t data = 0;
353  int ret = PX4_OK;
354 
355  ret |= readRegister(0x00, &data, 1);
356  ret |= writeRegister(0x00, data & 0xF7);
357 
358  return ret;
359 }
360 
361 int
363 {
364  /* update battery subscription */
365  if (_battery_sub.updated()) {
366  battery_status_s battery{};
367  _battery_sub.copy(&battery);
368 
369  if (battery.connected) {
370  _battery_voltage_filtered_v = battery.voltage_filtered_v;
371  _battery_discharge_mah = battery.discharged_mah;
372  _battery_valid = true;
373 
374  } else {
375  _battery_valid = false;
376  }
377  }
378 
379  /* update vehicle local position subscription */
381  vehicle_local_position_s local_position{};
382  _local_position_sub.copy(&local_position);
383 
384  _local_position_valid = local_position.z_valid;
385 
386  if (_local_position_valid) {
387  _local_position_z = -local_position.z;
388  }
389  }
390 
391  /* update vehicle status subscription */
393  vehicle_status_s vehicle_status{};
394  _vehicle_status_sub.copy(&vehicle_status);
395 
396  if (vehicle_status.arming_state == vehicle_status_s::ARMING_STATE_ARMED &&
397  _arming_state != vehicle_status_s::ARMING_STATE_ARMED) {
398  // arming
400 
401  } else if (vehicle_status.arming_state != vehicle_status_s::ARMING_STATE_ARMED &&
402  _arming_state == vehicle_status_s::ARMING_STATE_ARMED) {
403  // disarming
404  }
405 
406  _arming_state = vehicle_status.arming_state;
407  _nav_state = vehicle_status.nav_state;
408  }
409 
410  return PX4_OK;
411 }
412 
413 const char *
414 OSDatxxxx::get_flight_mode(uint8_t nav_state)
415 {
416  const char *flight_mode = "UNKNOWN";
417 
418  switch (nav_state) {
419  case vehicle_status_s::NAVIGATION_STATE_MANUAL:
420  flight_mode = "MANUAL";
421  break;
422 
423  case vehicle_status_s::NAVIGATION_STATE_ALTCTL:
424  flight_mode = "ALTITUDE";
425  break;
426 
427  case vehicle_status_s::NAVIGATION_STATE_POSCTL:
428  flight_mode = "POSITION";
429  break;
430 
431  case vehicle_status_s::NAVIGATION_STATE_AUTO_RTL:
432  flight_mode = "RETURN";
433  break;
434 
435  case vehicle_status_s::NAVIGATION_STATE_AUTO_MISSION:
436  flight_mode = "MISSION";
437  break;
438 
439  case vehicle_status_s::NAVIGATION_STATE_AUTO_LOITER:
440  case vehicle_status_s::NAVIGATION_STATE_AUTO_RCRECOVER:
441  case vehicle_status_s::NAVIGATION_STATE_AUTO_RTGS:
442  case vehicle_status_s::NAVIGATION_STATE_DESCEND:
443  case vehicle_status_s::NAVIGATION_STATE_AUTO_TAKEOFF:
444  case vehicle_status_s::NAVIGATION_STATE_AUTO_LAND:
445  case vehicle_status_s::NAVIGATION_STATE_AUTO_FOLLOW_TARGET:
446  case vehicle_status_s::NAVIGATION_STATE_AUTO_PRECLAND:
447  flight_mode = "AUTO";
448  break;
449 
450  case vehicle_status_s::NAVIGATION_STATE_AUTO_LANDENGFAIL:
451  case vehicle_status_s::NAVIGATION_STATE_AUTO_LANDGPSFAIL:
452  flight_mode = "FAILURE";
453  break;
454 
455  case vehicle_status_s::NAVIGATION_STATE_ACRO:
456  flight_mode = "ACRO";
457  break;
458 
459  case vehicle_status_s::NAVIGATION_STATE_TERMINATION:
460  flight_mode = "TERMINATE";
461  break;
462 
463  case vehicle_status_s::NAVIGATION_STATE_OFFBOARD:
464  flight_mode = "OFFBOARD";
465  break;
466 
467  case vehicle_status_s::NAVIGATION_STATE_STAB:
468  flight_mode = "STABILIZED";
469  break;
470 
471  case vehicle_status_s::NAVIGATION_STATE_RATTITUDE:
472  flight_mode = "RATTITUDE";
473  break;
474  }
475 
476  return flight_mode;
477 }
478 
479 int
481 {
482  int ret = PX4_OK;
483 
484  if (_battery_valid) {
485  ret |= add_battery_info(1, 1);
486 
487  } else {
488  clear_line(1, 1, 10);
489  clear_line(1, 2, 10);
490  }
491 
492  if (_local_position_valid) {
493  ret |= add_altitude(1, 3);
494 
495  } else {
496  clear_line(1, 3, 10);
497  }
498 
499  const char *flight_mode = "";
500 
501  if (_arming_state == vehicle_status_s::ARMING_STATE_ARMED) {
502  float flight_time_sec = static_cast<float>((hrt_absolute_time() - _arming_timestamp) / (1e6f));
503  ret |= add_flighttime(flight_time_sec, 1, 14);
504 
505  } else {
506  flight_mode = get_flight_mode(_nav_state);
507  }
508 
509  add_string_to_screen_centered(flight_mode, 12, 10);
510 
511  return ret;
512 }
513 
514 int
516 {
517  int ret = writeRegister(0x00, 0x02);
518  usleep(100);
519 
520  return ret;
521 }
522 
523 void
525 {
526  if (should_exit()) {
527  exit_and_cleanup();
528  return;
529  }
530 
531  update_topics();
532 
533  update_screen();
534 }
535 
536 int
537 OSDatxxxx::print_usage(const char *reason)
538 {
539  if (reason) {
540  printf("%s\n\n", reason);
541  }
542 
543  PRINT_MODULE_DESCRIPTION(
544  R"DESCR_STR(
545 ### Description
546 OSD driver for the ATXXXX chip that is mounted on the OmnibusF4SD board for example.
547 
548 It can be enabled with the OSD_ATXXXX_CFG parameter.
549 )DESCR_STR");
550 
551  PRINT_MODULE_USAGE_NAME("atxxxx", "driver");
552  PRINT_MODULE_USAGE_COMMAND_DESCR("start", "Start the driver");
553  PRINT_MODULE_USAGE_PARAM_INT('b', -1, 0, 100, "SPI bus (default: use board-specific bus)", true);
554  PRINT_MODULE_USAGE_DEFAULT_COMMANDS();
555 
556  return 0;
557 }
558 
559 int
560 OSDatxxxx::custom_command(int argc, char *argv[])
561 {
562  return print_usage("unrecognized command");
563 }
564 
565 int
566 atxxxx_main(int argc, char *argv[])
567 {
568  return OSDatxxxx::main(argc, argv);
569 }
int add_flighttime(float flight_time, uint8_t pos_x, uint8_t pos_y)
Definition: atxxxx.cpp:322
float _battery_voltage_filtered_v
Definition: atxxxx.h:141
#define OSD_SYMBOL_M
Definition: symbols.h:46
int enable_screen()
Definition: atxxxx.cpp:338
#define OSD_PAL_TX_MODE
Definition: atxxxx.h:77
uORB::Subscription _local_position_sub
Definition: atxxxx.h:137
#define OSD_NUM_ROWS_PAL
Definition: atxxxx.h:74
bool _local_position_valid
Definition: atxxxx.h:147
#define OSD_SPI_BUS_SPEED
Definition: atxxxx.h:68
#define OSD_ZERO_BYTE
Definition: atxxxx.h:76
int start()
Definition: atxxxx.cpp:132
void Run() override
Definition: atxxxx.cpp:524
static constexpr uint32_t OSD_UPDATE_RATE
Definition: atxxxx.cpp:47
int main(int argc, char **argv)
Definition: main.cpp:3
virtual int init()
Definition: atxxxx.cpp:98
static int print_usage(const char *reason=nullptr)
Definition: atxxxx.cpp:537
int readRegister(unsigned reg, uint8_t *data, unsigned count)
Definition: atxxxx.cpp:174
static int custom_command(int argc, char *argv[])
Definition: atxxxx.cpp:560
~OSDatxxxx()
Definition: atxxxx.cpp:56
void add_string_to_screen_centered(const char *str, uint8_t pos_y, int max_length)
Definition: atxxxx.cpp:242
int disable_screen()
Definition: atxxxx.cpp:350
#define DIR_READ
Definition: bmp388_spi.cpp:46
int add_altitude(uint8_t pos_x, uint8_t pos_y)
Definition: atxxxx.cpp:306
uint8_t _arming_state
Definition: atxxxx.h:150
float _local_position_z
Definition: atxxxx.h:146
void init()
Activates/configures the hardware registers.
bool _battery_valid
Definition: atxxxx.h:143
int update_topics()
Definition: atxxxx.cpp:362
#define DEVICE_LOG(FMT,...)
Definition: Device.hpp:51
uint8_t _nav_state
Definition: atxxxx.h:154
uint8_t * data
Definition: dataman.cpp:149
int reset()
Definition: atxxxx.cpp:515
int atxxxx_main(int argc, char *argv[])
Definition: atxxxx.cpp:566
int update_screen()
Definition: atxxxx.cpp:480
#define OSD_SYMBOL_FLIGHT_TIME
Definition: symbols.h:118
bool updated()
Check if there is a new update.
#define OSD_CHARS_PER_ROW
Definition: atxxxx.h:73
#define OSD_SYMBOL_BATT_3
Definition: symbols.h:104
float _battery_discharge_mah
Definition: atxxxx.h:142
#define DIR_WRITE
Definition: bmp388_spi.cpp:47
#define OSD_SYMBOL_MAH
Definition: symbols.h:113
#define OSD_NUM_ROWS_NTSC
Definition: atxxxx.h:75
void clear_line(uint8_t pos_x, uint8_t pos_y, int length)
Definition: atxxxx.cpp:267
int writeRegister(unsigned reg, uint8_t data)
Definition: atxxxx.cpp:193
static int task_spawn(int argc, char *argv[])
Definition: atxxxx.cpp:62
int add_battery_info(uint8_t pos_x, uint8_t pos_y)
Definition: atxxxx.cpp:275
Definition: bst.cpp:62
virtual int probe()
Definition: atxxxx.cpp:140
static const char * get_flight_mode(uint8_t nav_state)
Definition: atxxxx.cpp:414
uint64_t _arming_timestamp
Definition: atxxxx.h:151
#define OK
Definition: uavcan_main.cpp:71
uORB::Subscription _vehicle_status_sub
Definition: atxxxx.h:138
int init_osd()
Definition: atxxxx.cpp:156
#define OSD_SYMBOL_ARROW_NORTH
Definition: symbols.h:75
bool copy(void *dst)
Copy the struct.
int add_character_to_screen(char c, uint8_t pos_x, uint8_t pos_y)
Definition: atxxxx.cpp:211
OSDatxxxx(int bus=OSD_BUS)
Definition: atxxxx.cpp:49
__EXPORT hrt_abstime hrt_absolute_time(void)
Get absolute time in [us] (does not wrap).
uORB::Subscription _battery_sub
Definition: atxxxx.h:136