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 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218
  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(FSTR_P 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, FSTR_P 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, FSTR_P 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. const uint8_t pad_bytes[] = { 0, 0, 0, 0 };
  818. return _write_unaligned(data, len) &&
  819. _write_unaligned(pad_bytes, padding);
  820. }
  821. #else // FTDI_API_LEVEL != 800 ...
  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_MSG("Waiting for ", len + padding, " bytes in command queue, now free: ", Command_Space);
  855. #endif
  856. do {
  857. Command_Space = mem_read_32(REG::CMDB_SPACE) & 0x0FFF;
  858. if (has_fault()) {
  859. #if ENABLED(TOUCH_UI_DEBUG)
  860. SERIAL_ECHOLNPGM("... fault");
  861. #endif
  862. return false;
  863. }
  864. } while (Command_Space < len + padding);
  865. #if ENABLED(TOUCH_UI_DEBUG)
  866. SERIAL_ECHOLNPGM("... done");
  867. #endif
  868. }
  869. mem_write_bulk(REG::CMDB_WRITE, data, len, padding);
  870. return true;
  871. }
  872. #endif // ... FTDI_API_LEVEL != 800
  873. template bool CLCD::CommandFifo::write(const void*, uint16_t);
  874. template bool CLCD::CommandFifo::write(FSTR_P, uint16_t);
  875. // CO_PROCESSOR COMMANDS
  876. void CLCD::CommandFifo::str(const char * data, size_t maxlen) {
  877. // Write the string without the terminating '\0'
  878. const size_t len = strnlen(data, maxlen);
  879. write(data, len);
  880. // If padding was added by the previous write, then
  881. // the string is terminated. Otherwise write four
  882. // more zeros.
  883. const uint8_t padding = MULTIPLE_OF_4(len) - len;
  884. if (padding == 0) {
  885. const uint8_t pad_bytes[] = {0, 0, 0, 0};
  886. write(pad_bytes, 4);
  887. }
  888. }
  889. void CLCD::CommandFifo::str(const char * data) {
  890. write(data, strlen(data)+1);
  891. }
  892. void CLCD::CommandFifo::str(FSTR_P data) {
  893. write(data, strlen_P((const char*)data)+1);
  894. }
  895. /******************* LCD INITIALIZATION ************************/
  896. void CLCD::init() {
  897. spi_init(); // Set Up I/O Lines for SPI and FT800/810 Control
  898. ftdi_reset(); // Power down/up the FT8xx with the appropriate delays
  899. host_cmd(Use_Crystal ? CLKEXT : CLKINT, 0);
  900. host_cmd(FTDI::ACTIVE, 0); // Activate the System Clock
  901. 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.
  902. /* read the device-id until it returns 0x7C or times out, should take less than 150ms */
  903. uint8_t counter;
  904. for (counter = 0; counter < 250; counter++) {
  905. uint8_t device_id = mem_read_8(REG::ID); // Read Device ID, Should Be 0x7C;
  906. if (device_id == 0x7C) {
  907. if (ENABLED(TOUCH_UI_DEBUG)) SERIAL_ECHO_MSG("FTDI chip initialized ");
  908. break;
  909. }
  910. else
  911. delay(1);
  912. if (TERN0(TOUCH_UI_DEBUG, counter > 248))
  913. SERIAL_ECHO_MSG("Timeout waiting for device ID, should be 124, got ", device_id);
  914. }
  915. /* Ensure all units are in working condition, usually the touch-controller needs a little more time */
  916. for (counter = 0; counter < 100; counter++) {
  917. uint8_t reset_status = mem_read_8(REG::CPURESET) & 0x03;
  918. if (reset_status == 0x00) {
  919. if (ENABLED(TOUCH_UI_DEBUG)) SERIAL_ECHO_MSG("FTDI chip all units running ");
  920. break;
  921. }
  922. else
  923. delay(1);
  924. if (TERN0(TOUCH_UI_DEBUG, counter > 98))
  925. SERIAL_ECHO_MSG("Timeout waiting for reset status. Should be 0x00, got ", reset_status);
  926. }
  927. #if ENABLED(USE_GT911) /* switch BT815 to use Goodix GT911 touch controller */
  928. mem_write_32(REG::TOUCH_CONFIG, 0x000005D1);
  929. #endif
  930. #if ENABLED(PATCH_GT911) /* patch FT813 use Goodix GT911 touch controller */
  931. mem_write_pgm(REG::CMDB_WRITE, GT911_data, sizeof(GT911_data)); /* write binary blob to command-fifo */
  932. delay(10);
  933. mem_write_8(REG::TOUCH_OVERSAMPLE, 0x0F); /* setup oversample to 0x0f as "hidden" in binary-blob for AN_336 */
  934. mem_write_16(REG::TOUCH_CONFIG, 0x05d0); /* write magic cookie as requested by AN_336 */
  935. /* specific to the EVE2 modules from Matrix-Orbital we have to use GPIO3 to reset GT911 */
  936. 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 */
  937. delay(1); /* wait more than 100µs */
  938. mem_write_8(REG::CPURESET, 0x00); /* clear all resets */
  939. delay(56); /* wait more than 55ms */
  940. mem_write_16(REG::GPIOX_DIR,0x8000); /* setting GPIO3 back to input */
  941. #endif
  942. mem_write_8(REG::PWM_DUTY, 0); // turn off Backlight, Frequency already is set to 250Hz default
  943. /* Configure the FT8xx Registers */
  944. mem_write_16(REG::HCYCLE, FTDI::Hcycle);
  945. mem_write_16(REG::HOFFSET, FTDI::Hoffset);
  946. mem_write_16(REG::HSYNC0, FTDI::Hsync0);
  947. mem_write_16(REG::HSYNC1, FTDI::Hsync1);
  948. mem_write_16(REG::VCYCLE, FTDI::Vcycle);
  949. mem_write_16(REG::VOFFSET, FTDI::Voffset);
  950. mem_write_16(REG::VSYNC0, FTDI::Vsync0);
  951. mem_write_16(REG::VSYNC1, FTDI::Vsync1);
  952. mem_write_16(REG::HSIZE, FTDI::Hsize);
  953. mem_write_16(REG::VSIZE, FTDI::Vsize);
  954. mem_write_8(REG::SWIZZLE, FTDI::Swizzle);
  955. mem_write_8(REG::PCLK_POL, FTDI::Pclkpol);
  956. mem_write_8(REG::CSPREAD, FTDI::CSpread);
  957. /* write a basic display-list to get things started */
  958. mem_write_32(MAP::RAM_DL, DL::CLEAR_COLOR_RGB);
  959. mem_write_32(MAP::RAM_DL + 4, (DL::CLEAR | 0x07)); /* clear color, stencil and tag buffer */
  960. mem_write_32(MAP::RAM_DL + 8, DL::DL_DISPLAY); /* end of display list */
  961. mem_write_8(REG::DLSWAP, 0x02); // activate display list, Bad Magic Cookie 2 = switch to new list after current frame is scanned out
  962. //mem_write_8(REG::TOUCH_MODE, 0x03); // Configure the Touch Screen, Bad Magic Cookie, 3 = CONTINUOUS = Reset Default
  963. //mem_write_8(REG::TOUCH_ADC_MODE, 0x01); // Bad Magic Cookie, 1 = single touch = Reset Default
  964. //mem_write_8(REG::TOUCH_OVERSAMPLE, 0x0F); // Reset Default = 7 - why 15?
  965. mem_write_16(REG::TOUCH_RZTHRESH, touch_threshold); /* setup touch sensitivity */
  966. mem_write_8(REG::VOL_SOUND, 0x00); // Turn Synthesizer Volume Off
  967. /* turn on the display by setting DISP high */
  968. /* turn on the Audio Amplifier by setting GPIO_1 high for the select few modules supporting this */
  969. /* no need to use GPIOX here since DISP/GPIO_0 and GPIO_1 are on REG::GPIO for FT81x as well */
  970. if (GPIO_1_Audio_Shutdown) {
  971. mem_write_8(REG::GPIO_DIR, GPIO_DISP | GPIO_GP1);
  972. mem_write_8(REG::GPIO, GPIO_DISP | GPIO_GP1);
  973. }
  974. else if (GPIO_0_Audio_Enable) {
  975. mem_write_8(REG::GPIO_DIR, GPIO_DISP | GPIO_GP0);
  976. mem_write_8(REG::GPIO, GPIO_DISP | GPIO_GP0);
  977. }
  978. else
  979. mem_write_8(REG::GPIO, GPIO_DISP); /* REG::GPIO_DIR is set to output for GPIO_DISP by default */
  980. mem_write_8(REG::PCLK, Pclk); // Turns on Clock by setting PCLK Register to the value necessary for the module
  981. mem_write_16(REG::PWM_HZ, 0x00FA);
  982. // Turning off dithering seems to help prevent horizontal line artifacts on certain colors
  983. mem_write_8(REG::DITHER, 0);
  984. default_touch_transform();
  985. default_display_orientation();
  986. }
  987. void CLCD::default_touch_transform() {
  988. // Set Initial Values for Touch Transform Registers
  989. mem_write_32(REG::ROTATE, 0);
  990. mem_write_32(REG::TOUCH_TRANSFORM_A, FTDI::default_transform_a);
  991. mem_write_32(REG::TOUCH_TRANSFORM_B, FTDI::default_transform_b);
  992. mem_write_32(REG::TOUCH_TRANSFORM_C, FTDI::default_transform_c);
  993. mem_write_32(REG::TOUCH_TRANSFORM_D, FTDI::default_transform_d);
  994. mem_write_32(REG::TOUCH_TRANSFORM_E, FTDI::default_transform_e);
  995. mem_write_32(REG::TOUCH_TRANSFORM_F, FTDI::default_transform_f);
  996. }
  997. void CLCD::default_display_orientation() {
  998. #if FTDI_API_LEVEL >= 810
  999. // Set the initial display orientation. On the FT810, we use the command
  1000. // processor to do this since it will also update the transform matrices.
  1001. CommandFifo cmd;
  1002. cmd.setrotate(
  1003. ENABLED(TOUCH_UI_MIRRORED) * 4
  1004. + ENABLED(TOUCH_UI_PORTRAIT) * 2
  1005. + ENABLED(TOUCH_UI_INVERTED) * 1
  1006. );
  1007. cmd.execute();
  1008. #elif EITHER(TOUCH_UI_PORTRAIT, TOUCH_UI_MIRRORED)
  1009. #error "PORTRAIT or MIRRORED orientation not supported on the FT800."
  1010. #elif ENABLED(TOUCH_UI_INVERTED)
  1011. mem_write_32(REG::ROTATE, 1);
  1012. #endif
  1013. }
  1014. #endif // FTDI_BASIC