PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/branches/full-calibration/flight/OpenPilot/Modules/MK/MKSerial/MKSerial.c

https://github.com/CorvusCorax/my_OpenPilot_mods
C | 559 lines | 396 code | 78 blank | 85 comment | 60 complexity | 24db6e87a775b2c8d57172cf9590158f MD5 | raw file
  1. /**
  2. ******************************************************************************
  3. * @addtogroup OpenPilotModules OpenPilot Modules
  4. * @{
  5. * @addtogroup MKSerialModule MK Serial Control Module
  6. * @brief Connect to MK module
  7. * @{
  8. *
  9. * @file MKSerial.c
  10. * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
  11. * @brief Interfacing with MK via serial port
  12. * @see The GNU Public License (GPL) Version 3
  13. *
  14. *****************************************************************************/
  15. /*
  16. * This program is free software; you can redistribute it and/or modify
  17. * it under the terms of the GNU General Public License as published by
  18. * the Free Software Foundation; either version 3 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful, but
  22. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  23. * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  24. * for more details.
  25. *
  26. * You should have received a copy of the GNU General Public License along
  27. * with this program; if not, write to the Free Software Foundation, Inc.,
  28. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  29. */
  30. #include "openpilot.h"
  31. #include "MkSerial.h"
  32. #include "attitudeactual.h" // object that will be updated by the module
  33. #include "positionactual.h"
  34. #include "flightbatterystate.h"
  35. //
  36. // Configuration
  37. //
  38. #define PORT PIOS_COM_AUX
  39. #define DEBUG_PORT PIOS_COM_GPS
  40. #define STACK_SIZE 1024
  41. #define TASK_PRIORITY (tskIDLE_PRIORITY + 3)
  42. #define MAX_NB_PARS 100
  43. //#define ENABLE_DEBUG_MSG
  44. //#define GENERATE_BATTERY_INFO // The MK can report battery voltage, but normally the current sensor will be used, so this module should not report battery state
  45. #if PORT == PIOS_COM_AUX
  46. #ifndef PIOS_ENABLE_AUX_UART
  47. #error "This module cannot be included without the AUX UART enabled"
  48. #endif
  49. #endif
  50. //
  51. // Private constants
  52. //
  53. #define MSGCMD_ANY 0
  54. #define MSGCMD_GET_DEBUG 'd'
  55. #define MSGCMD_DEBUG 'D'
  56. #define MSGCMD_GET_VERSION 'v'
  57. #define MSGCMD_VERSION 'V'
  58. #define MSGCMD_GET_OSD 'o'
  59. #define MSGCMD_OSD 'O'
  60. #define DEBUG_MSG_NICK_IDX (2+2*2)
  61. #define DEBUG_MSG_ROLL_IDX (2+3*2)
  62. #define OSD_MSG_CURRPOS_IDX 1
  63. #define OSD_MSG_NB_SATS_IDX 50
  64. #define OSD_MSG_BATT_IDX 57
  65. #define OSD_MSG_GNDSPEED_IDX 58
  66. #define OSD_MSG_COMPHEADING_IDX 62
  67. #define OSD_MSG_NICK_IDX 64
  68. #define OSD_MSG_ROLL_IDX 65
  69. #ifdef ENABLE_DEBUG_MSG
  70. #define DEBUG_MSG(format, ...) PIOS_COM_SendFormattedString(DEBUG_PORT, format, ## __VA_ARGS__)
  71. #else
  72. #define DEBUG_MSG(format, ...)
  73. #endif
  74. //
  75. // Private types
  76. //
  77. typedef struct {
  78. uint8_t address;
  79. uint8_t cmd;
  80. uint8_t nbPars;
  81. uint8_t pars[MAX_NB_PARS];
  82. } MkMsg_t;
  83. typedef struct {
  84. float longitute;
  85. float latitude;
  86. float altitude;
  87. uint8_t status;
  88. } GpsPosition_t;
  89. enum {
  90. MK_ADDR_ALL = 0,
  91. MK_ADDR_FC = 1,
  92. MK_ADDR_NC = 2,
  93. MK_ADDR_MAG = 3,
  94. };
  95. //
  96. // Private variables
  97. //
  98. //
  99. // Private functions
  100. //
  101. static void OnError(int line);
  102. //static void PrintMsg(const MkMsg_t* msg);
  103. static int16_t Par2Int16(const MkMsg_t * msg, uint8_t index);
  104. static int32_t Par2Int32(const MkMsg_t * msg, uint8_t index);
  105. static int8_t Par2Int8(const MkMsg_t * msg, uint8_t index);
  106. static void GetGpsPos(const MkMsg_t * msg, uint8_t index, GpsPosition_t * pos);
  107. static uint8_t WaitForBytes(uint8_t * buf, uint8_t nbBytes, portTickType xTicksToWait);
  108. static bool WaitForMsg(uint8_t cmd, MkMsg_t * msg, portTickType xTicksToWait);
  109. static void SendMsg(const MkMsg_t * msg);
  110. static void SendMsgParNone(uint8_t address, uint8_t cmd);
  111. static void SendMsgPar8(uint8_t address, uint8_t cmd, uint8_t par0);
  112. static void MkSerialTask(void *parameters);
  113. static void OnError(int line)
  114. {
  115. DEBUG_MSG("MKProcol error %d\n\r", line);
  116. }
  117. #if 0
  118. static void PrintMsg(const MkMsg_t * msg)
  119. {
  120. switch (msg->address) {
  121. case MK_ADDR_ALL:
  122. DEBUG_MSG("ALL ");
  123. break;
  124. case MK_ADDR_FC:
  125. DEBUG_MSG("FC ");
  126. break;
  127. case MK_ADDR_NC:
  128. DEBUG_MSG("NC ");
  129. break;
  130. case MK_ADDR_MAG:
  131. DEBUG_MSG("MAG ");
  132. break;
  133. default:
  134. DEBUG_MSG("??? ");
  135. break;
  136. }
  137. DEBUG_MSG("%c ", msg->cmd);
  138. for (int i = 0; i < msg->nbPars; i++) {
  139. DEBUG_MSG("%02x ", msg->pars[i]);
  140. }
  141. DEBUG_MSG("\n\r");
  142. }
  143. #endif
  144. static int16_t Par2Int16(const MkMsg_t * msg, uint8_t index)
  145. {
  146. int16_t res;
  147. res = (int)(msg->pars[index + 1]) * 256 + msg->pars[index];
  148. if (res > 0xFFFF / 2)
  149. res -= 0xFFFF;
  150. return res;
  151. }
  152. static int32_t Par2Int32(const MkMsg_t * msg, uint8_t index)
  153. {
  154. uint32_t val = 0;
  155. val = (((int)msg->pars[index]) << 0) + (((int)msg->pars[index + 1]) << 8);
  156. val += (((int)msg->pars[index + 2]) << 16) + ((int)msg->pars[index + 3] << 24);
  157. if (val > 0xFFFFFFFF / 2)
  158. val -= 0xFFFFFFFF;
  159. return (int32_t) val;
  160. }
  161. static int8_t Par2Int8(const MkMsg_t * msg, uint8_t index)
  162. {
  163. if (msg->pars[index] > 127)
  164. return msg->pars[index] - 256;
  165. else
  166. return msg->pars[index];
  167. }
  168. static void GetGpsPos(const MkMsg_t * msg, uint8_t index, GpsPosition_t * pos)
  169. {
  170. pos->longitute = (float)Par2Int32(msg, index) * (float)1e-7;
  171. pos->latitude = (float)Par2Int32(msg, index + 4) * (float)1e-7;
  172. pos->altitude = (float)Par2Int32(msg, index + 8) * (float)1e-3;
  173. pos->status = msg->pars[index + 12];
  174. }
  175. static uint8_t WaitForBytes(uint8_t * buf, uint8_t nbBytes, portTickType xTicksToWait)
  176. {
  177. uint8_t nbBytesLeft = nbBytes;
  178. xTimeOutType xTimeOut;
  179. vTaskSetTimeOutState(&xTimeOut);
  180. // Loop until
  181. // - all bytes are received
  182. // - \r is seen
  183. // - Timeout occurs
  184. do {
  185. // Check if timeout occured
  186. if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait))
  187. break;
  188. // Check if there are some bytes
  189. if (PIOS_COM_ReceiveBufferUsed(PORT)) {
  190. *buf = PIOS_COM_ReceiveBuffer(PORT);
  191. nbBytesLeft--;
  192. if (buf[0] == '\r')
  193. break;
  194. buf++;
  195. } else {
  196. // Avoid tight loop
  197. // FIXME: should be blocking
  198. vTaskDelay(5);
  199. }
  200. } while (nbBytesLeft);
  201. return nbBytes - nbBytesLeft;
  202. }
  203. static bool WaitForMsg(uint8_t cmd, MkMsg_t * msg, portTickType xTicksToWait)
  204. {
  205. uint8_t buf[10];
  206. uint8_t n;
  207. bool done = FALSE;
  208. bool error = FALSE;
  209. unsigned int checkVal;
  210. xTimeOutType xTimeOut;
  211. vTaskSetTimeOutState(&xTimeOut);
  212. while (!done && !error) {
  213. // When we are here, it means we did not encounter the message we are waiting for
  214. // Check if we did not timeout yet.
  215. // Wait for start
  216. buf[0] = 0;
  217. do {
  218. if (xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait)) {
  219. return FALSE;
  220. }
  221. WaitForBytes(buf, 1, 100 / portTICK_RATE_MS);
  222. } while (buf[0] != '#');
  223. // Wait for cmd and address
  224. if (WaitForBytes(buf, 2, 10 / portTICK_RATE_MS) != 2) {
  225. OnError(__LINE__);
  226. continue;
  227. }
  228. // Is this the command we are waiting for?
  229. if (cmd == 0 || cmd == buf[1]) {
  230. // OK follow this message to the end
  231. msg->address = buf[0] - 'a';
  232. msg->cmd = buf[1];
  233. checkVal = '#' + buf[0] + buf[1];
  234. // Parse parameters
  235. msg->nbPars = 0;
  236. while (!done && !error) {
  237. n = WaitForBytes(buf, 4, 10 / portTICK_RATE_MS);
  238. if (n > 0 && buf[n - 1] == '\r') {
  239. n--;
  240. // This is the end of the message
  241. // Get check bytes
  242. if (n >= 2) {
  243. unsigned int msgCeckVal;
  244. msgCeckVal = (buf[n - 1] - '=') + (buf[n - 2] - '=') * 64;
  245. //printf("%x %x\n", msgCeckVal, checkVal&0xFFF);
  246. n -= 2;
  247. if (msgCeckVal == (checkVal & 0xFFF)) {
  248. done = TRUE;
  249. } else {
  250. OnError(__LINE__);
  251. error = TRUE;
  252. }
  253. } else {
  254. OnError(__LINE__);
  255. error = TRUE;
  256. }
  257. } else if (n == 4) {
  258. // Parse parameters
  259. int i;
  260. for (i = 0; i < 4; i++) {
  261. checkVal += buf[i];
  262. buf[i] -= '=';
  263. }
  264. if (msg->nbPars < MAX_NB_PARS) {
  265. msg->pars[msg->nbPars] = (((buf[0] << 2) & 0xFF) | ((buf[1] >> 4)));
  266. msg->nbPars++;
  267. }
  268. if (msg->nbPars < MAX_NB_PARS) {
  269. msg->pars[msg->nbPars] = ((buf[1] & 0x0F) << 4 | (buf[2] >> 2));
  270. msg->nbPars++;
  271. }
  272. if (msg->nbPars < MAX_NB_PARS) {
  273. msg->pars[msg->nbPars] = ((buf[2] & 0x03) << 6 | buf[3]);
  274. msg->nbPars++;
  275. }
  276. } else {
  277. OnError(__LINE__);
  278. error = TRUE;
  279. }
  280. }
  281. }
  282. }
  283. return (done && !error);
  284. }
  285. static void SendMsg(const MkMsg_t * msg)
  286. {
  287. uint8_t buf[10];
  288. uint16_t checkVal;
  289. uint8_t nbParsRemaining;
  290. const uint8_t *pPar;
  291. // Header
  292. buf[0] = '#';
  293. buf[1] = msg->address + 'a';
  294. buf[2] = msg->cmd;
  295. PIOS_COM_SendBuffer(PORT, buf, 3);
  296. checkVal = (unsigned int)'#' + buf[1] + buf[2];
  297. // Parameters
  298. nbParsRemaining = msg->nbPars;
  299. pPar = msg->pars;
  300. while (nbParsRemaining) {
  301. uint8_t a, b, c;
  302. a = *pPar;
  303. b = 0;
  304. c = 0;
  305. nbParsRemaining--;
  306. pPar++;
  307. if (nbParsRemaining) {
  308. b = *pPar;
  309. nbParsRemaining--;
  310. pPar++;
  311. if (nbParsRemaining) {
  312. c = *pPar;
  313. nbParsRemaining--;
  314. pPar++;
  315. }
  316. }
  317. buf[0] = (a >> 2) + '=';
  318. buf[1] = (((a & 0x03) << 4) | ((b & 0xf0) >> 4)) + '=';
  319. buf[2] = (((b & 0x0f) << 2) | ((c & 0xc0) >> 6)) + '=';
  320. buf[3] = (c & 0x3f) + '=';
  321. checkVal += buf[0];
  322. checkVal += buf[1];
  323. checkVal += buf[2];
  324. checkVal += buf[3];
  325. PIOS_COM_SendBuffer(PORT, buf, 4);
  326. }
  327. checkVal &= 0xFFF;
  328. buf[0] = (checkVal / 64) + '=';
  329. buf[1] = (checkVal % 64) + '=';
  330. buf[2] = '\r';
  331. PIOS_COM_SendBuffer(PORT, buf, 3);
  332. }
  333. static void SendMsgParNone(uint8_t address, uint8_t cmd)
  334. {
  335. MkMsg_t msg;
  336. msg.address = address;
  337. msg.cmd = cmd;
  338. msg.nbPars = 0;
  339. SendMsg(&msg);
  340. }
  341. static void SendMsgPar8(uint8_t address, uint8_t cmd, uint8_t par0)
  342. {
  343. MkMsg_t msg;
  344. msg.address = address;
  345. msg.cmd = cmd;
  346. msg.nbPars = 1;
  347. msg.pars[0] = par0;
  348. SendMsg(&msg);
  349. }
  350. static uint16_t VersionMsg_GetVersion(const MkMsg_t * msg)
  351. {
  352. return msg->pars[0] * 100 + msg->pars[1];
  353. }
  354. static void DoConnectedToFC(void)
  355. {
  356. AttitudeActualData attitudeData;
  357. MkMsg_t msg;
  358. DEBUG_MSG("FC\n\r");
  359. memset(&attitudeData, 0, sizeof(attitudeData));
  360. // Configure FC for fast reporting of the debug-message
  361. SendMsgPar8(MK_ADDR_ALL, MSGCMD_GET_DEBUG, 10);
  362. while (TRUE) {
  363. if (WaitForMsg(MSGCMD_DEBUG, &msg, 500 / portTICK_RATE_MS)) {
  364. int16_t nick;
  365. int16_t roll;
  366. //PrintMsg(&msg);
  367. nick = Par2Int16(&msg, DEBUG_MSG_NICK_IDX);
  368. roll = Par2Int16(&msg, DEBUG_MSG_ROLL_IDX);
  369. DEBUG_MSG("Att: Nick=%5d Roll=%5d\n\r", nick, roll);
  370. attitudeData.Pitch = -(float)nick / 10;
  371. attitudeData.Roll = -(float)roll / 10;
  372. AttitudeActualSet(&attitudeData);
  373. } else {
  374. DEBUG_MSG("TO\n\r");
  375. break;
  376. }
  377. }
  378. }
  379. static void DoConnectedToNC(void)
  380. {
  381. MkMsg_t msg;
  382. GpsPosition_t pos;
  383. AttitudeActualData attitudeData;
  384. PositionActualData positionData;
  385. FlightBatteryStateData flightBatteryData;
  386. #ifdef GENERATE_BATTERY_INFO
  387. uint8_t battStateCnt = 0;
  388. #endif
  389. DEBUG_MSG("NC\n\r");
  390. memset(&attitudeData, 0, sizeof(attitudeData));
  391. memset(&positionData, 0, sizeof(positionData));
  392. memset(&flightBatteryData, 0, sizeof(flightBatteryData));
  393. // Configure NC for fast reporting of the osd-message
  394. SendMsgPar8(MK_ADDR_ALL, MSGCMD_GET_OSD, 10);
  395. while (TRUE) {
  396. if (WaitForMsg(MSGCMD_OSD, &msg, 500 / portTICK_RATE_MS)) {
  397. //PrintMsg(&msg);
  398. GetGpsPos(&msg, OSD_MSG_CURRPOS_IDX, &pos);
  399. #if 0
  400. DEBUG_MSG("Bat=%d\n\r", msg.pars[OSD_MSG_BATT_IDX]);
  401. DEBUG_MSG("Nick=%d Roll=%d\n\r", Par2Int8(&msg, OSD_MSG_NICK_IDX), Par2Int8(&msg, OSD_MSG_ROLL_IDX));
  402. DEBUG_MSG("POS #Sats=%d stat=%d lat=%d lon=%d alt=%d\n\r", msg.pars[OSD_MSG_NB_SATS_IDX], pos.status, (int)pos.latitude,
  403. (int)pos.longitute, (int)pos.altitude);
  404. #else
  405. DEBUG_MSG(".");
  406. #endif
  407. attitudeData.Pitch = -Par2Int8(&msg, OSD_MSG_NICK_IDX);
  408. attitudeData.Roll = -Par2Int8(&msg, OSD_MSG_ROLL_IDX);
  409. AttitudeActualSet(&attitudeData);
  410. positionData.Longitude = pos.longitute;
  411. positionData.Latitude = pos.latitude;
  412. positionData.Altitude = pos.altitude;
  413. positionData.Satellites = msg.pars[OSD_MSG_NB_SATS_IDX];
  414. positionData.Heading = Par2Int16(&msg, OSD_MSG_COMPHEADING_IDX);
  415. positionData.Groundspeed = ((float)Par2Int16(&msg, OSD_MSG_GNDSPEED_IDX)) / 100 /* cm/s => m/s */ ;
  416. if (positionData.Satellites < 5) {
  417. positionData.Status = POSITIONACTUAL_STATUS_NOFIX;
  418. } else {
  419. positionData.Status = POSITIONACTUAL_STATUS_FIX3D;
  420. }
  421. PositionActualSet(&positionData);
  422. #if GENERATE_BATTERY_INFO
  423. if (++battStateCnt > 2) {
  424. flightBatteryData.Voltage = (float)msg.pars[OSD_MSG_BATT_IDX] / 10;
  425. FlightBatteryStateSet(&flightBatteryData);
  426. battStateCnt = 0;
  427. }
  428. #endif
  429. } else {
  430. DEBUG_MSG("TO\n\r");
  431. break;
  432. }
  433. }
  434. }
  435. static void MkSerialTask(void *parameters)
  436. {
  437. MkMsg_t msg;
  438. uint32_t version;
  439. bool connectionOk = FALSE;
  440. PIOS_COM_ChangeBaud(PORT, 57600);
  441. PIOS_COM_ChangeBaud(DEBUG_PORT, 57600);
  442. DEBUG_MSG("MKSerial Started\n\r");
  443. while (1) {
  444. // Wait until we get version from MK
  445. while (!connectionOk) {
  446. SendMsgParNone(MK_ADDR_ALL, MSGCMD_GET_VERSION);
  447. DEBUG_MSG("Version... ");
  448. if (WaitForMsg(MSGCMD_VERSION, &msg, 250 / portTICK_RATE_MS)) {
  449. version = VersionMsg_GetVersion(&msg);
  450. DEBUG_MSG("%d\n\r", version);
  451. connectionOk = TRUE;
  452. } else {
  453. DEBUG_MSG("TO\n\r");
  454. }
  455. }
  456. // Dependent on version, decide it we are connected to NC or FC
  457. // TODO: use slave-addr to distinguish FC/NC -> much safer
  458. if (version < 60) {
  459. DoConnectedToNC(); // Will only return after an error
  460. } else {
  461. DoConnectedToFC(); // Will only return after an error
  462. }
  463. connectionOk = FALSE;
  464. vTaskDelay(250 / portTICK_RATE_MS);
  465. }
  466. }
  467. /**
  468. * Initialise the module
  469. * \return -1 if initialisation failed
  470. * \return 0 on success
  471. */
  472. int32_t MKSerialInitialize(void)
  473. {
  474. // Start gps task
  475. xTaskCreate(MkSerialTask, (signed char *)"MkSerial", STACK_SIZE, NULL, TASK_PRIORITY, NULL);
  476. return 0;
  477. }
  478. /**
  479. * @}
  480. * @}
  481. */