My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

commands.cpp 32KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. /****************
  2. * commands.cpp *
  3. ****************/
  4. /****************************************************************************
  5. * Written By Mark Pelletier 2017 - Aleph Objects, Inc. *
  6. * Written By Marcio Teixeira 2018 - Aleph Objects, Inc. *
  7. * *
  8. * This program is free software: you can redistribute it and/or modify *
  9. * it under the terms of the GNU General Public License as published by *
  10. * the Free Software Foundation, either version 3 of the License, or *
  11. * (at your option) any later version. *
  12. * *
  13. * This program is distributed in the hope that it will be useful, *
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  16. * GNU General Public License for more details. *
  17. * *
  18. * To view a copy of the GNU General Public License, go to the following *
  19. * location: <https://www.gnu.org/licenses/>. *
  20. ****************************************************************************/
  21. #include "ftdi_basic.h"
  22. #ifdef FTDI_BASIC
  23. #define MULTIPLE_OF_4(val) ((((val)+3)>>2)<<2)
  24. using namespace FTDI;
  25. using namespace FTDI::SPI;
  26. void CLCD::enable() {
  27. mem_write_8(REG::PCLK, Pclk);
  28. }
  29. void CLCD::disable() {
  30. mem_write_8(REG::PCLK, 0x00);
  31. }
  32. void CLCD::set_brightness(uint8_t brightness) {
  33. mem_write_8(REG::PWM_DUTY, min(128,brightness));
  34. }
  35. uint8_t CLCD::get_brightness() {
  36. return mem_read_8(REG::PWM_DUTY);
  37. }
  38. void CLCD::turn_on_backlight() {
  39. mem_write_8(REG::PWM_DUTY, 128);
  40. }
  41. void CLCD::FontMetrics::load(const uint8_t font) {
  42. static_assert(sizeof(FontMetrics) == 148, "Sizeof font metrics is incorrect");
  43. uint32_t rom_fontroot = mem_read_32(MAP::ROM_FONT_ADDR);
  44. mem_read_bulk(rom_fontroot + 148 * (font - 16), (uint8_t*) this, 148);
  45. }
  46. uint16_t CLCD::FontMetrics::get_text_width(const char *str, size_t n) const {
  47. uint16_t width = 0;
  48. const uint8_t *p = (const uint8_t *) str;
  49. for (;;) {
  50. const uint8_t val = *p++; n--;
  51. if (!val || n == 0) break;
  52. width += val < 128 ? char_widths[val] : 0;
  53. }
  54. return width;
  55. }
  56. uint16_t CLCD::FontMetrics::get_text_width(progmem_str str, size_t n) const {
  57. uint16_t width = 0;
  58. const uint8_t *p = (const uint8_t *) str;
  59. for (;;) {
  60. const uint8_t val = pgm_read_byte(p++); n--;
  61. if (!val || n == 0) break;
  62. width += val < 128 ? char_widths[val] : 0;
  63. }
  64. return width;
  65. }
  66. /************************** HOST COMMAND FUNCTION *********************************/
  67. void CLCD::host_cmd(unsigned char host_command, unsigned char byte2) { // Sends 24-Bit Host Command to LCD
  68. if (host_command != FTDI::ACTIVE) {
  69. host_command |= 0x40;
  70. }
  71. spi_ftdi_select();
  72. spi_send(host_command);
  73. spi_send(byte2);
  74. spi_send(0x00);
  75. spi_ftdi_deselect();
  76. }
  77. /************************** MEMORY READ FUNCTIONS *********************************/
  78. void CLCD::spi_read_addr(uint32_t reg_address) {
  79. spi_send((reg_address >> 16) & 0x3F); // Address [21:16]
  80. spi_send((reg_address >> 8 ) & 0xFF); // Address [15:8]
  81. spi_send((reg_address >> 0) & 0xFF); // Address [7:0]
  82. spi_send(0x00); // Dummy Byte
  83. }
  84. // Write 4-Byte Address, Read Multiple Bytes
  85. void CLCD::mem_read_bulk(uint32_t reg_address, uint8_t *data, uint16_t len) {
  86. spi_ftdi_select();
  87. spi_read_addr(reg_address);
  88. spi_read_bulk (data, len);
  89. spi_ftdi_deselect();
  90. }
  91. // Write 4-Byte Address, Read 1-Byte Data
  92. uint8_t CLCD::mem_read_8(uint32_t reg_address) {
  93. spi_ftdi_select();
  94. spi_read_addr(reg_address);
  95. uint8_t r_data = spi_read_8();
  96. spi_ftdi_deselect();
  97. return r_data;
  98. }
  99. // Write 4-Byte Address, Read 2-Bytes Data
  100. uint16_t CLCD::mem_read_16(uint32_t reg_address) {
  101. using namespace SPI::least_significant_byte_first;
  102. spi_ftdi_select();
  103. spi_read_addr(reg_address);
  104. uint16_t r_data = spi_read_16();
  105. spi_ftdi_deselect();
  106. return r_data;
  107. }
  108. // Write 4-Byte Address, Read 4-Bytes Data
  109. uint32_t CLCD::mem_read_32(uint32_t reg_address) {
  110. using namespace SPI::least_significant_byte_first;
  111. spi_ftdi_select();
  112. spi_read_addr(reg_address);
  113. uint32_t r_data = spi_read_32();
  114. spi_ftdi_deselect();
  115. return r_data;
  116. }
  117. /************************** MEMORY WRITE FUNCTIONS *********************************/
  118. // Generic operations for transforming a byte, for use with _mem_write_bulk:
  119. static inline uint8_t reverse_byte(uint8_t a) {
  120. return ((a & 0x1) << 7) | ((a & 0x2) << 5) |
  121. ((a & 0x4) << 3) | ((a & 0x8) << 1) |
  122. ((a & 0x10) >> 1) | ((a & 0x20) >> 3) |
  123. ((a & 0x40) >> 5) | ((a & 0x80) >> 7);
  124. }
  125. static inline uint8_t xbm_write(const uint8_t *p) {return reverse_byte(pgm_read_byte(p));}
  126. void CLCD::spi_write_addr(uint32_t reg_address) {
  127. spi_send((reg_address >> 16) | 0x80); // Address [21:16]
  128. spi_send((reg_address >> 8 ) & 0xFF); // Address [15:8]
  129. spi_send((reg_address >> 0) & 0xFF); // Address [7:0]
  130. }
  131. // Write 3-Byte Address, Multiple Bytes, plus padding bytes, from RAM
  132. void CLCD::mem_write_bulk(uint32_t reg_address, const void *data, uint16_t len, uint8_t padding) {
  133. spi_ftdi_select();
  134. spi_write_addr(reg_address);
  135. spi_write_bulk<ram_write>(data, len, padding);
  136. spi_ftdi_deselect();
  137. }
  138. // Write 3-Byte Address, Multiple Bytes, plus padding bytes, from PROGMEM
  139. void CLCD::mem_write_bulk(uint32_t reg_address, progmem_str str, uint16_t len, uint8_t padding) {
  140. spi_ftdi_select();
  141. spi_write_addr(reg_address);
  142. spi_write_bulk<pgm_write>(str, len, padding);
  143. spi_ftdi_deselect();
  144. }
  145. // Write 3-Byte Address, Multiple Bytes, plus padding bytes, from PROGMEM
  146. void CLCD::mem_write_pgm(uint32_t reg_address, const void *data, uint16_t len, uint8_t padding) {
  147. spi_ftdi_select();
  148. spi_write_addr(reg_address);
  149. spi_write_bulk<pgm_write>(data, len, padding);
  150. spi_ftdi_deselect();
  151. }
  152. // Write 3-Byte Address, Multiple Bytes, plus padding bytes, from PROGMEM, reversing bytes (suitable for loading XBM images)
  153. void CLCD::mem_write_xbm(uint32_t reg_address, progmem_str data, uint16_t len, uint8_t padding) {
  154. spi_ftdi_select();
  155. spi_write_addr(reg_address);
  156. spi_write_bulk<xbm_write>(data, len, padding);
  157. spi_ftdi_deselect();
  158. }
  159. // Write 3-Byte Address, Write 1-Byte Data
  160. void CLCD::mem_write_8(uint32_t reg_address, uint8_t data) {
  161. spi_ftdi_select();
  162. spi_write_addr(reg_address);
  163. spi_write_8(data);
  164. spi_ftdi_deselect();
  165. }
  166. // Write 3-Byte Address, Write 2-Bytes Data
  167. void CLCD::mem_write_16(uint32_t reg_address, uint16_t data) {
  168. using namespace SPI::least_significant_byte_first;
  169. spi_ftdi_select();
  170. spi_write_addr(reg_address);
  171. spi_write_16(data);
  172. spi_ftdi_deselect();
  173. }
  174. // Write 3-Byte Address, Write 4-Bytes Data
  175. void CLCD::mem_write_32(uint32_t reg_address, uint32_t data) {
  176. using namespace SPI::least_significant_byte_first;
  177. spi_ftdi_select();
  178. spi_write_addr(reg_address);
  179. spi_write_32(data);
  180. spi_ftdi_deselect();
  181. }
  182. // Fill area of len size with repeated data bytes
  183. void CLCD::mem_write_fill(uint32_t reg_address, uint8_t data, uint16_t len) {
  184. spi_ftdi_select();
  185. spi_write_addr(reg_address);
  186. while (len--) spi_write_8(data);
  187. spi_ftdi_deselect();
  188. }
  189. /******************* FT800/810 Co-processor Commands *********************************/
  190. #if FTDI_API_LEVEL == 800
  191. uint32_t CLCD::CommandFifo::command_write_ptr = 0xFFFFFFFFul;
  192. #endif
  193. void CLCD::CommandFifo::cmd(uint32_t cmd32) {
  194. write((void*)&cmd32, sizeof(uint32_t));
  195. }
  196. void CLCD::CommandFifo::cmd(void *data, uint16_t len) {
  197. write(data, len);
  198. }
  199. void CLCD::CommandFifo::bgcolor(uint32_t rgb) {
  200. cmd(CMD_BGCOLOR);
  201. cmd(rgb);
  202. }
  203. void CLCD::CommandFifo::fgcolor(uint32_t rgb) {
  204. cmd(CMD_FGCOLOR);
  205. cmd(rgb);
  206. }
  207. void CLCD::CommandFifo::gradcolor(uint32_t rgb) {
  208. cmd(CMD_GRADCOLOR);
  209. cmd(rgb);
  210. }
  211. // This sends the a text command to the command preprocessor, must be followed by str()
  212. void CLCD::CommandFifo::button(int16_t x, int16_t y, int16_t w, int16_t h, int16_t font, uint16_t option) {
  213. struct {
  214. int32_t type = CMD_BUTTON;
  215. int16_t x;
  216. int16_t y;
  217. int16_t w;
  218. int16_t h;
  219. int16_t font;
  220. uint16_t option;
  221. } cmd_data;
  222. cmd_data.x = x;
  223. cmd_data.y = y;
  224. cmd_data.w = w;
  225. cmd_data.h = h;
  226. cmd_data.font = font;
  227. cmd_data.option = option;
  228. cmd( &cmd_data, sizeof(cmd_data) );
  229. }
  230. // This sends the a text command to the command preprocessor, must be followed by str()
  231. void CLCD::CommandFifo::text(int16_t x, int16_t y, int16_t font, uint16_t options) {
  232. struct {
  233. int32_t type = CMD_TEXT;
  234. int16_t x;
  235. int16_t y;
  236. int16_t font;
  237. uint16_t options;
  238. } cmd_data;
  239. cmd_data.x = x;
  240. cmd_data.y = y;
  241. cmd_data.font = font;
  242. cmd_data.options = options;
  243. cmd( &cmd_data, sizeof(cmd_data) );
  244. }
  245. // This sends the a toggle command to the command preprocessor, must be followed by str()
  246. void CLCD::CommandFifo::toggle(int16_t x, int16_t y, int16_t w, int16_t font, uint16_t options, bool state) {
  247. struct {
  248. int32_t type = CMD_TOGGLE;
  249. int16_t x;
  250. int16_t y;
  251. int16_t w;
  252. int16_t font;
  253. uint16_t options;
  254. uint16_t state;
  255. } cmd_data;
  256. cmd_data.x = x;
  257. cmd_data.y = y;
  258. cmd_data.w = w;
  259. cmd_data.font = font;
  260. cmd_data.options = options;
  261. cmd_data.state = state ? 65535 : 0;
  262. cmd( &cmd_data, sizeof(cmd_data) );
  263. }
  264. // This sends the a keys command to the command preprocessor, must be followed by str()
  265. void CLCD::CommandFifo::keys(int16_t x, int16_t y, int16_t w, int16_t h, int16_t font, uint16_t options) {
  266. struct {
  267. int32_t type = CMD_KEYS;
  268. int16_t x;
  269. int16_t y;
  270. int16_t w;
  271. int16_t h;
  272. int16_t font;
  273. uint16_t options;
  274. } cmd_data;
  275. cmd_data.x = x;
  276. cmd_data.y = y;
  277. cmd_data.w = w;
  278. cmd_data.h = h;
  279. cmd_data.font = font;
  280. cmd_data.options = options;
  281. cmd( &cmd_data, sizeof(cmd_data) );
  282. }
  283. void CLCD::CommandFifo::clock(int16_t x, int16_t y, int16_t r, uint16_t options, int16_t h, int16_t m, int16_t s, int16_t ms)
  284. {
  285. struct {
  286. int32_t type = CMD_CLOCK;
  287. int16_t x;
  288. int16_t y;
  289. int16_t r;
  290. uint16_t options;
  291. int16_t h;
  292. int16_t m;
  293. int16_t s;
  294. int16_t ms;
  295. } cmd_data;
  296. cmd_data.x = x;
  297. cmd_data.y = y;
  298. cmd_data.r = r;
  299. cmd_data.options = options;
  300. cmd_data.h = h;
  301. cmd_data.m = m;
  302. cmd_data.s = s;
  303. cmd_data.ms = ms;
  304. cmd( &cmd_data, sizeof(cmd_data) );
  305. }
  306. void CLCD::CommandFifo::gauge(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t major, uint16_t minor, uint16_t val, uint16_t range)
  307. {
  308. struct {
  309. int32_t type = CMD_GAUGE;
  310. int16_t x;
  311. int16_t y;
  312. int16_t r;
  313. uint16_t options;
  314. uint16_t major;
  315. uint16_t minor;
  316. uint16_t val;
  317. uint16_t range;
  318. } cmd_data;
  319. cmd_data.x = x;
  320. cmd_data.y = y;
  321. cmd_data.r = r;
  322. cmd_data.options = options;
  323. cmd_data.major = major;
  324. cmd_data.minor = minor;
  325. cmd_data.val = val;
  326. cmd_data.range = range;
  327. cmd( &cmd_data, sizeof(cmd_data) );
  328. }
  329. void CLCD::CommandFifo::dial(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t val)
  330. {
  331. struct {
  332. int32_t type = CMD_DIAL;
  333. int16_t x;
  334. int16_t y;
  335. int16_t r;
  336. uint16_t options;
  337. uint16_t val;
  338. } cmd_data;
  339. cmd_data.x = x;
  340. cmd_data.y = y;
  341. cmd_data.r = r;
  342. cmd_data.options = options;
  343. cmd_data.val = val;
  344. cmd( &cmd_data, sizeof(cmd_data) );
  345. }
  346. void CLCD::CommandFifo::scrollbar(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t size, uint16_t range) {
  347. struct {
  348. int32_t type = CMD_SCROLLBAR;
  349. int16_t x;
  350. int16_t y;
  351. int16_t w;
  352. uint16_t h;
  353. uint16_t options;
  354. uint16_t val;
  355. uint16_t size;
  356. uint16_t range;
  357. } cmd_data;
  358. cmd_data.x = x;
  359. cmd_data.y = y;
  360. cmd_data.w = w;
  361. cmd_data.h = h;
  362. cmd_data.options = options;
  363. cmd_data.val = val;
  364. cmd_data.size = size;
  365. cmd_data.range = range;
  366. cmd( &cmd_data, sizeof(cmd_data) );
  367. }
  368. void CLCD::CommandFifo::progress(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range) {
  369. struct {
  370. int32_t type = CMD_PROGRESS;
  371. int16_t x;
  372. int16_t y;
  373. int16_t w;
  374. int16_t h;
  375. uint16_t options;
  376. uint16_t val;
  377. uint16_t range;
  378. } cmd_data;
  379. cmd_data.x = x;
  380. cmd_data.y = y;
  381. cmd_data.w = w;
  382. cmd_data.h = h;
  383. cmd_data.options = options;
  384. cmd_data.val = val;
  385. cmd_data.range = range;
  386. cmd( &cmd_data, sizeof(cmd_data) );
  387. }
  388. void CLCD::CommandFifo::slider(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range) {
  389. struct {
  390. int32_t type = CMD_SLIDER;
  391. int16_t x;
  392. int16_t y;
  393. int16_t w;
  394. int16_t h;
  395. uint16_t options;
  396. uint16_t val;
  397. uint16_t range;
  398. } cmd_data;
  399. cmd_data.x = x;
  400. cmd_data.y = y;
  401. cmd_data.w = w;
  402. cmd_data.h = h;
  403. cmd_data.options = options;
  404. cmd_data.val = val;
  405. cmd_data.range = range;
  406. cmd( &cmd_data, sizeof(cmd_data) );
  407. }
  408. void CLCD::CommandFifo::gradient(int16_t x0, int16_t y0, uint32_t rgb0, int16_t x1, int16_t y1, uint32_t rgb1) {
  409. struct {
  410. int32_t type = CMD_GRADIENT;
  411. int16_t x0;
  412. int16_t y0;
  413. uint32_t rgb0;
  414. int16_t x1;
  415. int16_t y1;
  416. uint32_t rgb1;
  417. } cmd_data;
  418. cmd_data.x0 = x0;
  419. cmd_data.y0 = y0;
  420. cmd_data.rgb0 = rgb0;
  421. cmd_data.x1 = x1;
  422. cmd_data.y1 = y1;
  423. cmd_data.rgb1 = rgb1;
  424. cmd( &cmd_data, sizeof(cmd_data) );
  425. }
  426. void CLCD::CommandFifo::number(int16_t x, int16_t y, int16_t font, uint16_t options, int32_t n) {
  427. struct {
  428. int32_t type = CMD_NUMBER;
  429. int16_t x;
  430. int16_t y;
  431. int16_t font;
  432. uint16_t options;
  433. int16_t n;
  434. } cmd_data;
  435. cmd_data.x = x;
  436. cmd_data.y = y;
  437. cmd_data.font = font;
  438. cmd_data.options = options;
  439. cmd_data.n = n;
  440. cmd( &cmd_data, sizeof(cmd_data) );
  441. }
  442. void CLCD::CommandFifo::memzero(uint32_t ptr, uint32_t size) {
  443. struct {
  444. uint32_t type = CMD_MEMZERO;
  445. uint32_t ptr;
  446. uint32_t size;
  447. } cmd_data;
  448. cmd_data.ptr = ptr;
  449. cmd_data.size = size;
  450. cmd( &cmd_data, sizeof(cmd_data) );
  451. }
  452. void CLCD::CommandFifo::memset(uint32_t ptr, uint32_t val, uint32_t size) {
  453. struct {
  454. uint32_t type = CMD_MEMSET;
  455. uint32_t ptr;
  456. uint32_t val;
  457. uint32_t size;
  458. } cmd_data;
  459. cmd_data.ptr = ptr;
  460. cmd_data.val = val;
  461. cmd_data.size = size;
  462. cmd( &cmd_data, sizeof(cmd_data) );
  463. }
  464. void CLCD::CommandFifo::memcpy(uint32_t dst, uint32_t src, uint32_t size) {
  465. struct {
  466. uint32_t type = CMD_MEMCPY;
  467. uint32_t dst;
  468. uint32_t src;
  469. uint32_t size;
  470. } cmd_data;
  471. cmd_data.dst = dst;
  472. cmd_data.src = src;
  473. cmd_data.size = size;
  474. cmd( &cmd_data, sizeof(cmd_data) );
  475. }
  476. void CLCD::CommandFifo::memcrc(uint32_t ptr, uint32_t num, uint32_t result) {
  477. struct {
  478. uint32_t type = CMD_MEMCRC;
  479. uint32_t ptr;
  480. uint32_t num;
  481. uint32_t result;
  482. } cmd_data;
  483. cmd_data.ptr = ptr;
  484. cmd_data.num = num;
  485. cmd_data.result = result;
  486. cmd( &cmd_data, sizeof(cmd_data) );
  487. }
  488. void CLCD::CommandFifo::memwrite(uint32_t ptr, uint32_t value) {
  489. struct {
  490. uint32_t type = CMD_MEMWRITE;
  491. uint32_t ptr;
  492. uint32_t num;
  493. uint32_t value;
  494. } cmd_data;
  495. cmd_data.ptr = ptr;
  496. cmd_data.num = 4;
  497. cmd_data.value = value;
  498. cmd( &cmd_data, sizeof(cmd_data) );
  499. }
  500. void CLCD::CommandFifo::append(uint32_t ptr, uint32_t size) {
  501. struct {
  502. uint32_t type = CMD_APPEND;
  503. uint32_t ptr;
  504. uint32_t size;
  505. } cmd_data;
  506. cmd_data.ptr = ptr;
  507. cmd_data.size = size;
  508. cmd( &cmd_data, sizeof(cmd_data) );
  509. }
  510. void CLCD::CommandFifo::inflate(uint32_t ptr) {
  511. struct {
  512. uint32_t type = CMD_INFLATE;
  513. uint32_t ptr;
  514. } cmd_data;
  515. cmd_data.ptr = ptr;
  516. cmd( &cmd_data, sizeof(cmd_data) );
  517. }
  518. void CLCD::CommandFifo::getptr(uint32_t result) {
  519. struct {
  520. uint32_t type = CMD_GETPTR;
  521. uint32_t result;
  522. } cmd_data;
  523. cmd_data.result = result;
  524. cmd( &cmd_data, sizeof(cmd_data) );
  525. }
  526. void CLCD::CommandFifo::track(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t tag) {
  527. struct {
  528. uint32_t type = CMD_TRACK;
  529. int16_t x;
  530. int16_t y;
  531. int16_t w;
  532. int16_t h;
  533. int16_t tag;
  534. } cmd_data;
  535. cmd_data.x = x;
  536. cmd_data.y = y;
  537. cmd_data.w = w;
  538. cmd_data.h = h;
  539. cmd_data.tag = tag;
  540. cmd( &cmd_data, sizeof(cmd_data) );
  541. }
  542. void CLCD::CommandFifo::sketch(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t ptr, uint16_t format) {
  543. struct {
  544. uint32_t type = CMD_SKETCH;
  545. int16_t x;
  546. int16_t y;
  547. uint16_t w;
  548. uint16_t h;
  549. uint32_t ptr;
  550. uint16_t format;
  551. } cmd_data;
  552. cmd_data.x = x;
  553. cmd_data.y = y;
  554. cmd_data.w = w;
  555. cmd_data.h = h;
  556. cmd_data.ptr = ptr;
  557. cmd_data.format = format;
  558. cmd( &cmd_data, sizeof(cmd_data) );
  559. }
  560. void CLCD::CommandFifo::snapshot(uint32_t ptr) {
  561. struct {
  562. uint32_t type = CMD_SNAPSHOT;
  563. uint32_t ptr;
  564. } cmd_data;
  565. cmd_data.ptr = ptr;
  566. cmd( &cmd_data, sizeof(cmd_data) );
  567. }
  568. void CLCD::CommandFifo::spinner(int16_t x, int16_t y, uint16_t style, uint16_t scale) {
  569. struct {
  570. uint32_t type = CMD_SPINNER;
  571. uint16_t x;
  572. uint16_t y;
  573. uint16_t style;
  574. uint16_t scale;
  575. } cmd_data;
  576. cmd_data.x = x;
  577. cmd_data.y = y;
  578. cmd_data.style = style;
  579. cmd_data.scale = scale;
  580. cmd( &cmd_data, sizeof(cmd_data) );
  581. }
  582. void CLCD::CommandFifo::loadimage(uint32_t ptr, uint32_t options) {
  583. struct {
  584. uint32_t type = CMD_LOADIMAGE;
  585. uint32_t ptr;
  586. uint32_t options;
  587. } cmd_data;
  588. cmd_data.ptr = ptr;
  589. cmd_data.options = options;
  590. cmd( &cmd_data, sizeof(cmd_data) );
  591. }
  592. void CLCD::CommandFifo::getprops(uint32_t ptr, uint32_t width, uint32_t height) {
  593. struct {
  594. uint32_t type = CMD_GETPROPS;
  595. uint32_t ptr;
  596. uint32_t width;
  597. uint32_t height;
  598. } cmd_data;
  599. cmd_data.ptr = ptr;
  600. cmd_data.width = width;
  601. cmd_data.height = height;
  602. cmd( &cmd_data, sizeof(cmd_data) );
  603. }
  604. void CLCD::CommandFifo::scale(int32_t sx, int32_t sy) {
  605. struct {
  606. uint32_t type = CMD_SCALE;
  607. int32_t sx;
  608. int32_t sy;
  609. } cmd_data;
  610. cmd_data.sx = sx;
  611. cmd_data.sy = sy;
  612. cmd( &cmd_data, sizeof(cmd_data) );
  613. }
  614. void CLCD::CommandFifo::rotate(int32_t a) {
  615. struct {
  616. uint32_t type = CMD_ROTATE;
  617. int32_t a;
  618. } cmd_data;
  619. cmd_data.a = a;
  620. cmd( &cmd_data, sizeof(cmd_data) );
  621. }
  622. void CLCD::CommandFifo::translate(int32_t tx, int32_t ty) {
  623. struct {
  624. uint32_t type = CMD_TRANSLATE;
  625. int32_t tx;
  626. int32_t ty;
  627. } cmd_data;
  628. cmd_data.tx = tx;
  629. cmd_data.ty = ty;
  630. cmd( &cmd_data, sizeof(cmd_data) );
  631. }
  632. #if FTDI_API_LEVEL >= 810
  633. void CLCD::CommandFifo::setbase(uint8_t base) {
  634. struct {
  635. int32_t type = CMD_SETBASE;
  636. uint32_t base;
  637. } cmd_data;
  638. cmd_data.base = base;
  639. cmd( &cmd_data, sizeof(cmd_data) );
  640. }
  641. #endif
  642. #if FTDI_API_LEVEL >= 810
  643. void CLCD::CommandFifo::setbitmap(uint32_t addr, uint16_t fmt, uint16_t w, uint16_t h) {
  644. struct {
  645. uint32_t type = CMD_SETBITMAP;
  646. uint32_t addr;
  647. uint16_t fmt;
  648. uint16_t w;
  649. uint16_t h;
  650. uint16_t dummy;
  651. } cmd_data;
  652. cmd_data.addr = addr;
  653. cmd_data.fmt = fmt;
  654. cmd_data.w = w;
  655. cmd_data.h = h;
  656. cmd_data.dummy = 0;
  657. cmd( &cmd_data, sizeof(cmd_data) );
  658. }
  659. #endif
  660. #if FTDI_API_LEVEL >= 810
  661. void CLCD::CommandFifo::snapshot2(uint32_t format, uint32_t ptr, int16_t x, int16_t y, uint16_t w, uint16_t h) {
  662. struct {
  663. uint32_t type = CMD_SNAPSHOT2;
  664. uint32_t format;
  665. uint32_t ptr;
  666. int16_t x;
  667. int16_t y;
  668. uint16_t w;
  669. uint16_t h;
  670. } cmd_data;
  671. cmd_data.format = format;
  672. cmd_data.ptr = ptr;
  673. cmd_data.x = x;
  674. cmd_data.y = y;
  675. cmd_data.w = w;
  676. cmd_data.h = h;
  677. cmd( &cmd_data, sizeof(cmd_data) );
  678. }
  679. #endif
  680. #if FTDI_API_LEVEL >= 810
  681. void CLCD::CommandFifo::mediafifo(uint32_t ptr, uint32_t size) {
  682. struct {
  683. uint32_t type = CMD_MEDIAFIFO;
  684. uint32_t ptr;
  685. uint32_t size;
  686. } cmd_data;
  687. cmd_data.ptr = ptr;
  688. cmd_data.size = size;
  689. cmd( &cmd_data, sizeof(cmd_data) );
  690. }
  691. #endif
  692. #if FTDI_API_LEVEL >= 810
  693. void CLCD::CommandFifo::videostart() {
  694. cmd( CMD_VIDEOSTART );
  695. }
  696. #endif
  697. #if FTDI_API_LEVEL >= 810
  698. void CLCD::CommandFifo::videoframe(uint32_t dst, uint32_t ptr) {
  699. struct {
  700. uint32_t type = CMD_VIDEOFRAME;
  701. uint32_t dst;
  702. uint32_t ptr;
  703. } cmd_data;
  704. cmd_data.dst = dst;
  705. cmd_data.ptr = ptr;
  706. cmd( &cmd_data, sizeof(cmd_data) );
  707. }
  708. #endif
  709. #if FTDI_API_LEVEL >= 810
  710. void CLCD::CommandFifo::playvideo(uint32_t options) {
  711. struct {
  712. uint32_t type = CMD_PLAYVIDEO;
  713. uint32_t options;
  714. } cmd_data;
  715. cmd_data.options = options;
  716. cmd( &cmd_data, sizeof(cmd_data) );
  717. }
  718. #endif
  719. #if FTDI_API_LEVEL >= 810
  720. void CLCD::CommandFifo::setrotate(uint8_t rotation) {
  721. struct {
  722. uint32_t type = CMD_SETROTATE;
  723. uint32_t rotation;
  724. } cmd_data;
  725. cmd_data.rotation = rotation;
  726. cmd( &cmd_data, sizeof(cmd_data) );
  727. }
  728. #endif
  729. #if FTDI_API_LEVEL >= 810
  730. void CLCD::CommandFifo::romfont(uint8_t font, uint8_t romslot) {
  731. struct {
  732. uint32_t type = CMD_ROMFONT;
  733. uint32_t font;
  734. uint32_t romslot;
  735. } cmd_data;
  736. cmd_data.font = font;
  737. cmd_data.romslot = romslot;
  738. cmd( &cmd_data, sizeof(cmd_data) );
  739. }
  740. #endif
  741. /**************************** FT800/810 Co-Processor Command FIFO ****************************/
  742. bool CLCD::CommandFifo::is_processing() {
  743. return (mem_read_32(REG::CMD_READ) & 0x0FFF) != (mem_read_32(REG::CMD_WRITE) & 0x0FFF);
  744. }
  745. bool CLCD::CommandFifo::has_fault() {
  746. uint16_t Cmd_Read_Reg = mem_read_32(REG::CMD_READ) & 0x0FFF;
  747. return Cmd_Read_Reg == 0x0FFF;
  748. }
  749. #if FTDI_API_LEVEL == 800
  750. void CLCD::CommandFifo::start() {
  751. if (command_write_ptr == 0xFFFFFFFFul) {
  752. command_write_ptr = mem_read_32(REG::CMD_WRITE) & 0x0FFF;
  753. }
  754. }
  755. void CLCD::CommandFifo::execute() {
  756. if (command_write_ptr != 0xFFFFFFFFul) {
  757. mem_write_32(REG::CMD_WRITE, command_write_ptr);
  758. }
  759. }
  760. void CLCD::CommandFifo::reset() {
  761. safe_delay(100);
  762. mem_write_32(REG::CPURESET, 0x00000001);
  763. mem_write_32(REG::CMD_WRITE, 0x00000000);
  764. mem_write_32(REG::CMD_READ, 0x00000000);
  765. mem_write_32(REG::CPURESET, 0x00000000);
  766. safe_delay(300);
  767. command_write_ptr = 0xFFFFFFFFul;
  768. };
  769. template <class T> bool CLCD::CommandFifo::_write_unaligned(T data, uint16_t len) {
  770. const char *ptr = (const char*)data;
  771. uint32_t bytes_tail, bytes_head;
  772. uint32_t command_read_ptr;
  773. #if ENABLED(TOUCH_UI_DEBUG)
  774. if (command_write_ptr == 0xFFFFFFFFul)
  775. SERIAL_ECHO_MSG("Attempt to write to FIFO before CommandFifo::Cmd_Start().");
  776. #endif
  777. /* Wait until there is enough space in the circular buffer for the transfer */
  778. do {
  779. command_read_ptr = mem_read_32(REG::CMD_READ) & 0x0FFF;
  780. if (command_read_ptr <= command_write_ptr) {
  781. bytes_tail = 4096U - command_write_ptr;
  782. bytes_head = command_read_ptr;
  783. }
  784. else {
  785. bytes_tail = command_read_ptr - command_write_ptr;
  786. bytes_head = 0;
  787. }
  788. // Check for faults which can lock up the command processor
  789. if (has_fault()) {
  790. #if ENABLED(TOUCH_UI_DEBUG)
  791. SERIAL_ECHOLNPGM("Fault waiting for space in the command processor");
  792. #endif
  793. return false;
  794. }
  795. } while ((bytes_tail + bytes_head) < len);
  796. /* Write as many bytes as possible following REG::CMD_WRITE */
  797. uint16_t bytes_to_write = min(len, bytes_tail);
  798. mem_write_bulk (MAP::RAM_CMD + command_write_ptr, T(ptr), bytes_to_write);
  799. command_write_ptr += bytes_to_write;
  800. ptr += bytes_to_write;
  801. len -= bytes_to_write;
  802. if (len > 0) {
  803. /* Write remaining bytes at start of circular buffer */
  804. mem_write_bulk (MAP::RAM_CMD, T(ptr), len);
  805. command_write_ptr = len;
  806. }
  807. if (command_write_ptr == 4096U) {
  808. command_write_ptr = 0;
  809. }
  810. return true;
  811. }
  812. // Writes len bytes into the FIFO, if len is not
  813. // divisible by four, zero bytes will be written
  814. // to align to the boundary.
  815. template <class T> bool CLCD::CommandFifo::write(T data, uint16_t len) {
  816. const uint8_t padding = MULTIPLE_OF_4(len) - len;
  817. uint8_t pad_bytes[] = {0, 0, 0, 0};
  818. return _write_unaligned(data, len) &&
  819. _write_unaligned(pad_bytes, padding);
  820. }
  821. #else
  822. void CLCD::CommandFifo::start() {
  823. }
  824. void CLCD::CommandFifo::execute() {
  825. }
  826. void CLCD::CommandFifo::reset() {
  827. #if ENABLED(TOUCH_UI_DEBUG)
  828. SERIAL_ECHOLNPGM("Resetting command processor");
  829. #endif
  830. safe_delay(100);
  831. mem_write_32(REG::CPURESET, 0x00000001);
  832. mem_write_32(REG::CMD_WRITE, 0x00000000);
  833. mem_write_32(REG::CMD_READ, 0x00000000);
  834. mem_write_32(REG::CPURESET, 0x00000000);
  835. safe_delay(300);
  836. };
  837. // Writes len bytes into the FIFO, if len is not
  838. // divisible by four, zero bytes will be written
  839. // to align to the boundary.
  840. template <class T> bool CLCD::CommandFifo::write(T data, uint16_t len) {
  841. const uint8_t padding = MULTIPLE_OF_4(len) - len;
  842. if (has_fault()) {
  843. #if ENABLED(TOUCH_UI_DEBUG)
  844. SERIAL_ECHOLNPGM("Faulted... ignoring write.");
  845. #endif
  846. return false;
  847. }
  848. // The FT810 provides a special register that can be used
  849. // for writing data without us having to do our own FIFO
  850. // management.
  851. uint16_t Command_Space = mem_read_32(REG::CMDB_SPACE) & 0x0FFF;
  852. if (Command_Space < (len + padding)) {
  853. #if ENABLED(TOUCH_UI_DEBUG)
  854. SERIAL_ECHO_START();
  855. SERIAL_ECHOPAIR("Waiting for ", len + padding);
  856. SERIAL_ECHOLNPAIR(" bytes in command queue, now free: ", Command_Space);
  857. #endif
  858. do {
  859. Command_Space = mem_read_32(REG::CMDB_SPACE) & 0x0FFF;
  860. if (has_fault()) {
  861. #if ENABLED(TOUCH_UI_DEBUG)
  862. SERIAL_ECHOLNPGM("... fault");
  863. #endif
  864. return false;
  865. }
  866. } while (Command_Space < len + padding);
  867. #if ENABLED(TOUCH_UI_DEBUG)
  868. SERIAL_ECHOLNPGM("... done");
  869. #endif
  870. }
  871. mem_write_bulk(REG::CMDB_WRITE, data, len, padding);
  872. return true;
  873. }
  874. #endif
  875. template bool CLCD::CommandFifo::write(const void*, uint16_t);
  876. template bool CLCD::CommandFifo::write(progmem_str, uint16_t);
  877. // CO_PROCESSOR COMMANDS
  878. void CLCD::CommandFifo::str(const char * data) {
  879. write(data, strlen(data)+1);
  880. }
  881. void CLCD::CommandFifo::str(progmem_str data) {
  882. write(data, strlen_P((const char*)data)+1);
  883. }
  884. /******************* LCD INITIALIZATION ************************/
  885. void CLCD::init() {
  886. spi_init(); // Set Up I/O Lines for SPI and FT800/810 Control
  887. ftdi_reset(); // Power down/up the FT8xx with the apropriate delays
  888. host_cmd(Use_Crystal ? CLKEXT : CLKINT, 0);
  889. host_cmd(FTDI::ACTIVE, 0); // Activate the System Clock
  890. delay(40); // FTDI/BRT recommendation: no SPI traffic during startup. EVE needs at the very least 45ms to start, so leave her alone for a little while.
  891. /* read the device-id until it returns 0x7C or times out, should take less than 150ms */
  892. uint8_t counter;
  893. for (counter = 0; counter < 250; counter++) {
  894. uint8_t device_id = mem_read_8(REG::ID); // Read Device ID, Should Be 0x7C;
  895. if (device_id == 0x7C) {
  896. if (ENABLED(TOUCH_UI_DEBUG)) SERIAL_ECHO_MSG("FTDI chip initialized ");
  897. break;
  898. }
  899. else
  900. delay(1);
  901. if (TERN0(TOUCH_UI_DEBUG, counter > 248))
  902. SERIAL_ECHO_MSG("Timeout waiting for device ID, should be 124, got ", device_id);
  903. }
  904. /* Ensure all units are in working condition, usually the touch-controller needs a little more time */
  905. for (counter = 0; counter < 100; counter++) {
  906. uint8_t reset_status = mem_read_8(REG::CPURESET) & 0x03;
  907. if (reset_status == 0x00) {
  908. if (ENABLED(TOUCH_UI_DEBUG)) SERIAL_ECHO_MSG("FTDI chip all units running ");
  909. break;
  910. }
  911. else
  912. delay(1);
  913. if (TERN0(TOUCH_UI_DEBUG, counter > 98))
  914. SERIAL_ECHO_MSG("Timeout waiting for reset status. Should be 0x00, got ", reset_status);
  915. }
  916. #if ENABLED(USE_GT911) /* switch BT815 to use Goodix GT911 touch controller */
  917. mem_write_32(REG::TOUCH_CONFIG, 0x000005D1);
  918. #endif
  919. #if ENABLED(PATCH_GT911) /* patch FT813 use Goodix GT911 touch controller */
  920. mem_write_pgm(REG::CMDB_WRITE, GT911_data, sizeof(GT911_data)); /* write binary blob to command-fifo */
  921. delay(10);
  922. mem_write_8(REG::TOUCH_OVERSAMPLE, 0x0F); /* setup oversample to 0x0f as "hidden" in binary-blob for AN_336 */
  923. mem_write_16(REG::TOUCH_CONFIG, 0x05d0); /* write magic cookie as requested by AN_336 */
  924. /* specific to the EVE2 modules from Matrix-Orbital we have to use GPIO3 to reset GT911 */
  925. mem_write_16(REG::GPIOX_DIR,0x8008); /* Reset-Value is 0x8000, adding 0x08 sets GPIO3 to output, default-value for REG_GPIOX is 0x8000 -> Low output on GPIO3 */
  926. delay(1); /* wait more than 100µs */
  927. mem_write_8(REG::CPURESET, 0x00); /* clear all resets */
  928. delay(56); /* wait more than 55ms */
  929. mem_write_16(REG::GPIOX_DIR,0x8000); /* setting GPIO3 back to input */
  930. #endif
  931. mem_write_8(REG::PWM_DUTY, 0); // turn off Backlight, Frequency already is set to 250Hz default
  932. /* Configure the FT8xx Registers */
  933. mem_write_16(REG::HCYCLE, FTDI::Hcycle);
  934. mem_write_16(REG::HOFFSET, FTDI::Hoffset);
  935. mem_write_16(REG::HSYNC0, FTDI::Hsync0);
  936. mem_write_16(REG::HSYNC1, FTDI::Hsync1);
  937. mem_write_16(REG::VCYCLE, FTDI::Vcycle);
  938. mem_write_16(REG::VOFFSET, FTDI::Voffset);
  939. mem_write_16(REG::VSYNC0, FTDI::Vsync0);
  940. mem_write_16(REG::VSYNC1, FTDI::Vsync1);
  941. mem_write_16(REG::HSIZE, FTDI::Hsize);
  942. mem_write_16(REG::VSIZE, FTDI::Vsize);
  943. mem_write_8(REG::SWIZZLE, FTDI::Swizzle);
  944. mem_write_8(REG::PCLK_POL, FTDI::Pclkpol);
  945. mem_write_8(REG::CSPREAD, FTDI::CSpread);
  946. /* write a basic display-list to get things started */
  947. mem_write_32(MAP::RAM_DL, DL::CLEAR_COLOR_RGB);
  948. mem_write_32(MAP::RAM_DL + 4, (DL::CLEAR | 0x07)); /* clear color, stencil and tag buffer */
  949. mem_write_32(MAP::RAM_DL + 8, DL::DL_DISPLAY); /* end of display list */
  950. mem_write_8(REG::DLSWAP, 0x02); // activate display list, Bad Magic Cookie 2 = switch to new list after current frame is scanned out
  951. //mem_write_8(REG::TOUCH_MODE, 0x03); // Configure the Touch Screen, Bad Magic Cookie, 3 = CONTINUOUS = Reset Default
  952. //mem_write_8(REG::TOUCH_ADC_MODE, 0x01); // Bad Magic Cookie, 1 = single touch = Reset Default
  953. //mem_write_8(REG::TOUCH_OVERSAMPLE, 0x0F); // Reset Default = 7 - why 15?
  954. mem_write_16(REG::TOUCH_RZTHRESH, touch_threshold); /* setup touch sensitivity */
  955. mem_write_8(REG::VOL_SOUND, 0x00); // Turn Synthesizer Volume Off
  956. /* turn on the display by setting DISP high */
  957. /* turn on the Audio Amplifier by setting GPIO_1 high for the select few modules supporting this */
  958. /* no need to use GPIOX here since DISP/GPIO_0 and GPIO_1 are on REG::GPIO for FT81x as well */
  959. if (GPIO_1_Audio_Shutdown) {
  960. mem_write_8(REG::GPIO_DIR, GPIO_DISP | GPIO_GP1);
  961. mem_write_8(REG::GPIO, GPIO_DISP | GPIO_GP1);
  962. }
  963. else if (GPIO_0_Audio_Enable) {
  964. mem_write_8(REG::GPIO_DIR, GPIO_DISP | GPIO_GP0);
  965. mem_write_8(REG::GPIO, GPIO_DISP | GPIO_GP0);
  966. }
  967. else
  968. mem_write_8(REG::GPIO, GPIO_DISP); /* REG::GPIO_DIR is set to output for GPIO_DISP by default */
  969. mem_write_8(REG::PCLK, Pclk); // Turns on Clock by setting PCLK Register to the value necessary for the module
  970. mem_write_16(REG::PWM_HZ, 0x00FA);
  971. // Turning off dithering seems to help prevent horizontal line artifacts on certain colors
  972. mem_write_8(REG::DITHER, 0);
  973. default_touch_transform();
  974. default_display_orientation();
  975. }
  976. void CLCD::default_touch_transform() {
  977. // Set Initial Values for Touch Transform Registers
  978. mem_write_32(REG::ROTATE, 0);
  979. mem_write_32(REG::TOUCH_TRANSFORM_A, FTDI::default_transform_a);
  980. mem_write_32(REG::TOUCH_TRANSFORM_B, FTDI::default_transform_b);
  981. mem_write_32(REG::TOUCH_TRANSFORM_C, FTDI::default_transform_c);
  982. mem_write_32(REG::TOUCH_TRANSFORM_D, FTDI::default_transform_d);
  983. mem_write_32(REG::TOUCH_TRANSFORM_E, FTDI::default_transform_e);
  984. mem_write_32(REG::TOUCH_TRANSFORM_F, FTDI::default_transform_f);
  985. }
  986. void CLCD::default_display_orientation() {
  987. #if FTDI_API_LEVEL >= 810
  988. // Set the initial display orientation. On the FT810, we use the command
  989. // processor to do this since it will also update the transform matrices.
  990. CommandFifo cmd;
  991. cmd.setrotate(
  992. ENABLED(TOUCH_UI_MIRRORED) * 4
  993. + ENABLED(TOUCH_UI_PORTRAIT) * 2
  994. + ENABLED(TOUCH_UI_INVERTED) * 1
  995. );
  996. cmd.execute();
  997. #elif ANY(TOUCH_UI_PORTRAIT, TOUCH_UI_MIRRORED)
  998. #error "PORTRAIT or MIRRORED orientation not supported on the FT800."
  999. #elif ENABLED(TOUCH_UI_INVERTED)
  1000. mem_write_32(REG::ROTATE, 1);
  1001. #endif
  1002. }
  1003. #endif // FTDI_BASIC