Microprocessor and peripheral 2 assignments for AUTH
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.
 
 
 
 
 
 

496 lines
12 KiB

  1. /*!
  2. * \file alcd.c
  3. * \brief
  4. * A target independent Alpharithmetic LCD driver
  5. *
  6. * Author: Christos Choutouridis AEM: 8997
  7. * email : <cchoutou@ece.auth.gr>
  8. *
  9. */
  10. #include "alcd.h"
  11. static int _inc_x (alcd_t *alcd);
  12. static int _dec_x (alcd_t *alcd);
  13. static void _inc_y (alcd_t *alcd);
  14. static void _dec_y (alcd_t *alcd);
  15. static void _set_bus (alcd_t *alcd, int8_t db);
  16. static void _write_data (alcd_t *alcd, int8_t data);
  17. static void _command (alcd_t *alcd, uint8_t c);
  18. static void _character (alcd_t *alcd, uint8_t c);
  19. static void _set_cursor (alcd_t *alcd, uint8_t x, uint8_t y);
  20. /*!
  21. * \brief
  22. * increase cursor's x position. Positions start from 1.
  23. * If the cursor loops returns 1, else returns 0.
  24. * \param alcd pointer to active alcd.
  25. * \return Change line status
  26. */
  27. static int _inc_x (alcd_t *alcd) {
  28. int ret=0;
  29. if (++alcd->c.x > alcd->columns) {
  30. alcd->c.x = 1;
  31. ret = 1;
  32. }
  33. return ret;
  34. }
  35. /*!
  36. * \brief
  37. * Decrease cursor's x position. Positions start from 1.
  38. * If the cursor loops returns 1, else returns 0.
  39. * \param alcd pointer to active alcd.
  40. * \return none
  41. */
  42. static int _dec_x (alcd_t *alcd) {
  43. int ret = 0;
  44. if (--alcd->c.x < 1) {
  45. alcd->c.x = alcd->columns;
  46. ret = 1;
  47. }
  48. return ret;
  49. }
  50. /*!
  51. * \brief
  52. * increase cursor's y position. Positions start from 1.
  53. * \param alcd pointer to active alcd.
  54. * \return none
  55. */
  56. static void _inc_y (alcd_t *alcd) {
  57. if (++alcd->c.y > alcd->lines) {
  58. alcd->c.y = 1;
  59. }
  60. }
  61. /*!
  62. * \brief
  63. * Decrease cursor's y position. Positions start from 1.
  64. * \param alcd pointer to active alcd.
  65. * \return none
  66. */
  67. static void _dec_y (alcd_t *alcd) {
  68. if (--alcd->c.y < 1) {
  69. alcd->c.y = alcd->lines;
  70. }
  71. }
  72. /*!
  73. * \brief
  74. * Update the bus I/O and send it to the device.
  75. * \param alcd pointer to active alcd.
  76. * \param db the bus data.
  77. * \return none
  78. */
  79. static void _set_bus (alcd_t *alcd, int8_t db)
  80. {
  81. alcd->io.db4 (db & 0x01); //Update port
  82. alcd->io.db5 (db & 0x02);
  83. alcd->io.db6 (db & 0x04);
  84. alcd->io.db7 (db & 0x08);
  85. jf_delay_us (10); // Wait to settle
  86. alcd->io.en (1); // Pulse out the data
  87. jf_delay_us (10);
  88. alcd->io.en (0);
  89. jf_delay_us (10); // Data hold
  90. }
  91. /*!
  92. * \brief
  93. * Write the byte date to the bus using _set_bus ()
  94. * \param alcd pointer to active alcd.
  95. * \param data the data byte.
  96. * \return none
  97. */
  98. static void _write_data (alcd_t *alcd, int8_t data)
  99. {
  100. _set_bus (alcd, data >> 4);
  101. _set_bus (alcd, data & 0x0F);
  102. }
  103. /*!
  104. * \brief
  105. * Send a command to alcd
  106. * \param alcd pointer to active alcd.
  107. * \param c the command byte.
  108. * \return none
  109. */
  110. static void _command (alcd_t *alcd, uint8_t c)
  111. {
  112. alcd->io.rs(0); // Enter command mode
  113. jf_delay_us (100); // Wait
  114. _write_data (alcd, c); // Send
  115. }
  116. /*!
  117. * \brief
  118. * Send a character to alcd
  119. * \param alcd pointer to active alcd.
  120. * \param c the character byte.
  121. * \return none
  122. */
  123. static void _character (alcd_t *alcd, uint8_t c)
  124. {
  125. alcd->io.rs(1); // Enter character mode
  126. jf_delay_us (100); // Wait
  127. _write_data (alcd, c); // Send
  128. }
  129. /*!
  130. * \brief
  131. * Set the Cursor to LCD's position line (y), column (x) starts from 1,2,...n
  132. * \param alcd pointer to active alcd.
  133. * \param x the x position.
  134. * \param y the y position.
  135. * \return none
  136. */
  137. static void _set_cursor (alcd_t *alcd, uint8_t x, uint8_t y)
  138. {
  139. uint8_t cmd;
  140. alcd->c.x = x; // Update alcd data
  141. alcd->c.y = y;
  142. // Calculate address
  143. switch (y) {
  144. default:
  145. case 1: cmd = 0x0; break;
  146. case 2: cmd = 0x40; break;
  147. case 3: cmd = 0x0 + alcd->columns; break;
  148. case 4: cmd = 0x40 + alcd->columns; break;
  149. }
  150. cmd |= (x - 1);
  151. // Calculate command
  152. cmd |= LCD_DDRAMMask;
  153. // Command out the alcd
  154. _command (alcd, cmd);
  155. }
  156. /*
  157. * ============================ Public Functions ============================
  158. */
  159. /*
  160. * Link and Glue functions
  161. */
  162. /*!
  163. * \brief
  164. * Link driver's db4 pin function to io struct.
  165. * \param alcd pointer to active alcd.
  166. * \param pfun driver's DB4 pin function
  167. * \return none
  168. */
  169. inline void alcd_link_db4 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db4 = pfun; }
  170. /*!
  171. * \brief
  172. * Link driver's db4 pin function to io struct.
  173. * \param alcd pointer to active alcd.
  174. * \param pfun driver's DB5 pin function
  175. * \return none
  176. */
  177. inline void alcd_link_db5 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db5 = pfun; }
  178. /*!
  179. * \brief
  180. * Link driver's db4 pin function to io struct.
  181. * \param alcd pointer to active alcd.
  182. * \param pfun driver's DB6 pin function
  183. * \return none
  184. */
  185. inline void alcd_link_db6 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db6 = pfun; }
  186. /*!
  187. * \brief
  188. * Link driver's db4 pin function to io struct.
  189. * \param alcd pointer to active alcd.
  190. * \param pfun driver's DB7 pin function
  191. * \return none
  192. */
  193. inline void alcd_link_db7 (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.db7 = pfun; }
  194. /*!
  195. * \brief
  196. * Link driver's db4 pin function to io struct.
  197. * \param alcd pointer to active alcd.
  198. * \param pfun driver's RS pin function
  199. * \return none
  200. */
  201. inline void alcd_link_rs (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.rs = pfun; }
  202. /*!
  203. * \brief
  204. * Link driver's db4 pin function to io struct.
  205. * \param alcd pointer to active alcd.
  206. * \param pfun driver's EN pin function
  207. * \return none
  208. */
  209. inline void alcd_link_en (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.en = pfun; }
  210. /*!
  211. * \brief
  212. * Link driver's db4 pin function to io struct.
  213. * \param alcd pointer to active alcd.
  214. * \param pfun driver's BL pin function
  215. * \return none
  216. */
  217. inline void alcd_link_bl (alcd_t *alcd, drv_pinout_ft pfun) { alcd->io.bl = pfun; }
  218. /*!
  219. * \brief
  220. * Send an ascii character to alcd.
  221. * \param alcd pointer to active alcd.
  222. * \param ch the character to send
  223. * \return the character send.
  224. *
  225. * \note
  226. * This is the driver's "putchar()" functionality to glue.
  227. * Tailor this function to redirect stdout to alcd.
  228. */
  229. int alcd_putchar (alcd_t *alcd, int ch)
  230. {
  231. alcd->status = DRV_BUSY;
  232. // LCD Character dispatcher
  233. switch (ch) {
  234. case 0:
  235. // don't send null termination to device
  236. break;
  237. case '\n':
  238. _inc_y (alcd);
  239. //break; This "no break" is intentional
  240. case '\r':
  241. _set_cursor (alcd, 1, alcd->c.y);
  242. break;
  243. case '\v':
  244. alcd->c.x = alcd->c.y = 1;
  245. _command (alcd, LCD_RETHOME);
  246. jf_delay_us(2000);
  247. break;
  248. case '\f':
  249. //alcd->c.x = alcd->c.y = 1;
  250. _set_cursor (alcd, 1, 1);
  251. //_command (alcd, LCD_CLRSCR);
  252. //jf_delay_us(5000);
  253. //_command (alcd, LCD_RETHOME);
  254. //_set_cursor (alcd, alcd->c.x, alcd->c.y);
  255. jf_delay_us(2000);
  256. break;
  257. case '\b':
  258. if (_dec_x (alcd)) _dec_y (alcd);
  259. _set_cursor (alcd, alcd->c.x, alcd->c.y);
  260. _character (alcd, ' ');
  261. _set_cursor (alcd, alcd->c.x, alcd->c.y);
  262. break;
  263. default:
  264. _character (alcd, ch);
  265. // Increase cursor and loop inside the same line
  266. if (_inc_x (alcd)) _set_cursor (alcd, alcd->c.x, alcd->c.y);
  267. break;
  268. }
  269. // Restore status
  270. alcd->status = DRV_READY;
  271. //ANSI C (C99) compatible mode
  272. return ch;
  273. }
  274. /*
  275. * Set functions
  276. */
  277. /*!
  278. * \brief
  279. * Set the number of lines for the attached lcd display
  280. * \param alcd pointer to active alcd.
  281. * \param lines The number of lines (usually 2 or 4)
  282. * \return None.
  283. */
  284. void alcd_set_lines (alcd_t *alcd, int lines) {
  285. alcd->lines = lines;
  286. }
  287. /*!
  288. * \brief
  289. * Set the number of columns for the attached lcd display
  290. * \param alcd pointer to active alcd.
  291. * \param lines The number of columns (usually 16 or 20)
  292. * \return None.
  293. */
  294. void alcd_set_columns (alcd_t *alcd, int columns) {
  295. alcd->columns = columns;
  296. }
  297. /*
  298. * User Functions
  299. */
  300. /*!
  301. * \brief
  302. * De-initialize the alcd.
  303. * \param alcd pointer to active alcd.
  304. * \return none
  305. */
  306. void alcd_deinit (alcd_t *alcd)
  307. {
  308. memset ((void*)alcd, 0, sizeof (alcd_t));
  309. /*!<
  310. * This leaves the status DRV_NOINIT
  311. */
  312. }
  313. /*!
  314. * \brief
  315. * Initialize the alcd.
  316. * \param alcd pointer to active alcd.
  317. * \return Zero on success, non zero on error
  318. */
  319. drv_status_en alcd_init (alcd_t *alcd, alcd_funset_en fs)
  320. {
  321. #define _lcd_assert(_x) if (!_x) return alcd->status = DRV_ERROR;
  322. drv_status_en st = jf_probe ();
  323. if (st == DRV_NODEV || st == DRV_BUSY)
  324. return alcd->status = DRV_ERROR;
  325. _lcd_assert (alcd->io.db4);
  326. _lcd_assert (alcd->io.db5);
  327. _lcd_assert (alcd->io.db6);
  328. _lcd_assert (alcd->io.db7);
  329. _lcd_assert (alcd->io.rs);
  330. _lcd_assert (alcd->io.en);
  331. //_lcd_assert (alcd->io.bl);
  332. /*
  333. * We are here, so all its OK. We can (re)initialize alcd.
  334. */
  335. alcd->status = DRV_NOINIT;
  336. alcd->c.x = alcd->c.y = 1;
  337. alcd->io.en (0);
  338. alcd->io.rs (0);
  339. jf_delay_us (100000);
  340. //Pre-Init phase 8bit at this point
  341. _set_bus (alcd, 0x3);
  342. jf_delay_us(50000);
  343. _set_bus (alcd, 0x3);
  344. jf_delay_us(5000);
  345. _set_bus (alcd, 0x3);
  346. jf_delay_us(5000);
  347. _set_bus (alcd, 0x2); //4bit selection
  348. jf_delay_us(10000);
  349. _command (alcd, fs); //4bit selection and Function Set
  350. jf_delay_us(5000);
  351. _command (alcd, LCD_DISP_OFF); //Display Off Control 4bit for now on
  352. jf_delay_us(5000);
  353. _command (alcd, LCD_CLRSCR); //Clear Display
  354. jf_delay_us(5000);
  355. _command (alcd, LCD_ENTRYMODE); //Entry Mode Set
  356. jf_delay_us(5000);
  357. _command (alcd, LCD_RETHOME);
  358. jf_delay_us(10000);
  359. _command (alcd, LCD_DISP_ON);
  360. jf_delay_us(5000);
  361. //alcd_backlight (alcd, 1);
  362. return alcd->status = DRV_READY;
  363. #undef _lcd_assert
  364. }
  365. /*!
  366. * \brief
  367. * Enables and disables the lcd backlight.
  368. * \param alcd pointer to active alcd.
  369. * \param on
  370. * \arg 0 disable the backlight
  371. * \arg 1 enable the backlight
  372. * \return none
  373. */
  374. void alcd_backlight (alcd_t *alcd, uint8_t on) {
  375. if (alcd->io.bl)
  376. alcd->io.bl ((on)?1:0);
  377. }
  378. /*!
  379. * \brief
  380. * Enables and disables the entire lcd (+backlight).
  381. * \param alcd pointer to active alcd.
  382. * \param on
  383. * \arg 0 disable the backlight
  384. * \arg 1 enable the backlight
  385. * \return none
  386. */
  387. void alcd_enable (alcd_t *alcd, uint8_t on)
  388. {
  389. if (on) {
  390. _command (alcd, LCD_DISP_ON);
  391. alcd_backlight (alcd, 1);
  392. } else {
  393. _command (alcd, LCD_DISP_OFF);
  394. alcd_backlight (alcd, 0);
  395. }
  396. }
  397. /*!
  398. * \brief
  399. * Clears screen and returns cursor at home position (1,1).
  400. * \param alcd pointer to active alcd.
  401. * \return none
  402. */
  403. void alcd_cls (alcd_t *alcd)
  404. {
  405. _command(alcd, LCD_CLRSCR);
  406. jf_delay_us(2000);
  407. _command (alcd, LCD_RETHOME);
  408. jf_delay_us(2000);
  409. }
  410. /*!
  411. * \brief
  412. * Shift alcd left or right for a \a pos characters.
  413. * \param alcd pointer to active alcd.
  414. * \param pos The number of position to shift.
  415. * A positive number shifts lcd data to left, so screen shows the data in the right.
  416. * A negative number shifts lcd data to right, so screen shows the data in the left.
  417. * \return none
  418. */
  419. void alcd_shift (alcd_t *alcd, int pos)
  420. {
  421. uint8_t i, cmd = LCD_SHIFT_LEFT;
  422. if (pos<0) {
  423. pos = -pos;
  424. cmd = LCD_SHIFT_RIGHT;
  425. }
  426. for (i=0 ; i<pos ; ++i) {
  427. _command (alcd, cmd);
  428. jf_delay_us(100);
  429. }
  430. }
  431. // Allows us to fill the first 8 CGRAM locations
  432. // with custom characters
  433. void alcd_createChar (alcd_t *alcd, uint8_t location, uint8_t charmap[])
  434. {
  435. location &= 0x7; // we only have 8 locations 0-7
  436. _command (alcd, LCD_SETCGRAMADDR | (location << 3));
  437. for (int i=0; i<8; i++) {
  438. _character (alcd, charmap[i]);
  439. }
  440. }