1 /******************************************************************************
\r
2 ** FILE IDENTIFICATION
\r
3 ** Name: serial_lcd_tester.c
\r
4 ** Purpose: Tests Serial_Lcd
\r
5 ** Author: Kevin M. Rosenberg
\r
6 ** Last Modification: March 2007
\r
9 ** Linux and Windows/Cygwin
\r
11 ** Custom character bitmaps by Carl W. Livingston
\r
12 ******************************************************************************/
\r
14 // Will likely need to change for your system and LCD display
\r
16 #define DEFAULT_PORT "/dev/com7"
\r
17 static unsigned char lcdCols = 20;
\r
18 static unsigned char lcdRows = 4;
\r
20 #include <stdio.h> /* Standard input/output definitions */
\r
21 #include <string.h> /* String function definitions */
\r
22 #include <unistd.h> /* UNIX standard function definitions */
\r
23 #include <fcntl.h> /* File control definitions */
\r
24 #include <errno.h> /* Error number definitions */
\r
25 #include <termios.h> /* POSIX terminal control definitions */
\r
26 #include <sys/select.h>
\r
28 #include <sys/time.h>
\r
31 void MakeCustomChar (const char *Symbol, char Address);
\r
32 void delay_ms(unsigned int t);
\r
33 void clear_display(void);
\r
34 void display_banner(void);
\r
36 int open_serial_port (char const* const);
\r
37 void close_serial_port (void);
\r
38 void serial_puts (const char* str);
\r
39 void serial_putc (const unsigned char c);
\r
40 void run_test(void);
\r
42 static inline double get_time(void) {
\r
44 gettimeofday (&tv, NULL);
\r
45 return tv.tv_sec + tv.tv_usec/1e6;
\r
48 // Integer delay_ms routine
\r
49 // 65.535 seconds maximum
\r
50 void delay_ms(unsigned int t)
\r
52 double start = get_time();
\r
53 double end = start + t/1000;
\r
54 while (get_time() < end) {}
\r
58 void usage(char const* const prog) {
\r
59 fprintf(stderr, "usage: %s <portname>\n", basename(prog));
\r
60 fprintf(stderr, "Port name is in Cygwin format.\n");
\r
61 fprintf(stderr, "Example: COM1 would be /dev/com1\n");
\r
65 int main(int argc, char ** argv) {
\r
67 portName[sizeof(portName)] = 0; // ensure string is zero-terminated by below strncpy
\r
69 strncpy (portName, DEFAULT_PORT, sizeof(portName)-1);
\r
70 fprintf (stderr, "Using default serial port of %s\n", portName);
\r
71 } else if (argc == 2) {
\r
72 strncpy (portName, argv[1], sizeof(portName));
\r
77 if (! open_serial_port (portName)) {
\r
78 fprintf(stderr, "Unable to open serial port %s\n", portName);
\r
82 close_serial_port();
\r
86 // Serial_Lcd command delimiter
\r
87 #define SERIAL_LCD_CMD_MODE 0xFE
\r
89 // Display clear text command
\r
90 #define LCD_CLR_CMD 0x01
\r
91 // Display home cursor command
\r
92 #define LCD_HOME_CMD 0x02
\r
94 // Display entry mode command & parameters
\r
95 #define LCD_ENTRY_CMD 0x04
\r
96 #define CURSOR_DIR 0x02
\r
97 #define CURSOR_MOVE 0x01
\r
99 // Display control mode & parameters
\r
100 #define LCD_CONTROL_CMD 0x08
\r
101 #define LCD_ON 0x04
\r
102 #define CURSOR_ON 0x02
\r
103 #define CURSOR_BLINK 0x01
\r
105 // Display cursor control & parameters
\r
106 #define LCD_CURSOR_CMD 0x10
\r
107 #define LCD_SHIFT 0x08
\r
108 #define DISPLAY_L_R 0x04
\r
110 // Display line position addressing
\r
111 #define LINE1_ADDR 0x80
\r
112 #define LINE2_ADDR 0xC0
\r
113 #define LINE3_ADDR 0x94
\r
114 #define LINE4_ADDR 0xD4
\r
116 // Display data & character generator selection bits
\r
117 #define DD_RAM 0x80
\r
118 #define CG_RAM 0x40
\r
120 // Custom Character DDRAM location addressing
\r
121 #define DDRAM_START_ADD_0 0x00
\r
122 #define DDRAM_START_ADD_1 0x01
\r
123 #define DDRAM_START_ADD_2 0x02
\r
124 #define DDRAM_START_ADD_3 0x03
\r
125 #define DDRAM_START_ADD_4 0x04
\r
126 #define DDRAM_START_ADD_5 0x05
\r
127 #define DDRAM_START_ADD_6 0x06
\r
128 #define DDRAM_START_ADD_7 0x07
\r
130 // Custom Character CGRAM Starting addresses
\r
131 #define CGRAM_START_ADD_0 0x40
\r
132 #define CGRAM_START_ADD_1 0x48
\r
133 #define CGRAM_START_ADD_2 0x50
\r
134 #define CGRAM_START_ADD_3 0x58
\r
135 #define CGRAM_START_ADD_4 0x60
\r
136 #define CGRAM_START_ADD_5 0x68
\r
137 #define CGRAM_START_ADD_6 0x70
\r
138 #define CGRAM_START_ADD_7 0x78
\r
140 // ASCII control code (0xFB) to set LED brightness.
\r
141 // Next byte sets brightness (from 0 to 255)
\r
142 #define CMD_SET_BRIGHTNESS 0xFB
\r
143 #define NUM_BRIGHTNESS_LEVELS 8
\r
144 #define BRIGHTNESS_SCALE(b) ((unsigned char) ((b * 254.) / (double) (NUM_BRIGHTNESS_LEVELS-1)) + 1)
\r
146 // ASCII control code (0xFC) turns on LED
\r
147 #define LED_ON 0xFC
\r
148 // ASCII control code (0xFD) turns on LED
\r
149 #define LED_OFF 0xFD
\r
152 const char Smiley_face[] = {// Smiley face
\r
153 0x0E, // 0b00001110,
\r
154 0x1F, // 0b00011111,
\r
155 0x00, // 0b00000000,
\r
156 0x0A, // 0b00001010,
\r
157 0x04, // 0b00000100,
\r
158 0x11, // 0b00010001,
\r
159 0x0E, // 0b00001110,
\r
160 0x00, // 0b00000000
\r
162 const char Thin_cross[] = {// Thin cross
\r
163 0x11, // 0b00010001,
\r
164 0x00, // 0b00000000,
\r
165 0x1F, // 0b00011111,
\r
166 0x15, // 0b00010101,
\r
167 0x1B, // 0b00011011,
\r
168 0x0E, // 0b00001110,
\r
169 0x11, // 0b00010001,
\r
170 0x00, // 0b00000000
\r
172 const char Thick_cross[] = {// Thick cross
\r
173 0x00, // 0b00000000,
\r
174 0x0E, // 0b00001110,
\r
175 0x1B, // 0b00011011,
\r
176 0x11, // 0b00010001,
\r
177 0x1B, // 0b00011011,
\r
178 0x0E, // 0b00001110,
\r
179 0x00, // 0b00000000,
\r
180 0x00, // 0b00000000
\r
182 const char Music_note[] = {// Music note
\r
183 0x03, // 0b00000011,
\r
184 0x02, // 0b00000010,
\r
185 0x07, // 0b00000111,
\r
186 0x02, // 0b00000010,
\r
187 0x1E, // 0b00011110,
\r
188 0x12, // 0b00010010,
\r
189 0x1E, // 0b00011110,
\r
190 0x00, // 0b00000000
\r
192 const char Loop_Antenna[] = {// Loop Antenna
\r
193 0x11, // 0b00010001,
\r
194 0x11, // 0b00010001,
\r
195 0x1F, // 0b00011111,
\r
196 0x15, // 0b00010101,
\r
197 0x15, // 0b00010101,
\r
198 0x04, // 0b00000100,
\r
199 0x0E, // 0b00001110,
\r
200 0x00, // 0b00000000
\r
202 const char Lh_Yagi_Antenna[] = {// Lh Yagi Antenna
\r
203 0x10, // 0b00010000,
\r
204 0x14, // 0b00010100,
\r
205 0x15, // 0b00010101,
\r
206 0x1F, // 0b00011111,
\r
207 0x15, // 0b00010101,
\r
208 0x14, // 0b00010100,
\r
209 0x10, // 0b00010000,
\r
210 0x00, // 0b00000000
\r
212 const char Diamond[] = {// Diamond
\r
213 0x00, // 0b00000000,
\r
214 0x1F, // 0b00011111,
\r
215 0x11, // 0b00010001,
\r
216 0x15, // 0b00010101,
\r
217 0x11, // 0b00010001,
\r
218 0x1F, // 0b00011111,
\r
219 0x00, // 0b00000000,
\r
220 0x00, // 0b00000000
\r
222 const char Rh_Yagi_Antenna[] = {// Rh Yagi Antenna
\r
223 0x01, // 0b00000001,
\r
224 0x05, // 0b00000101,
\r
225 0x15, // 0b00010101,
\r
226 0x1F, // 0b00011111,
\r
227 0x15, // 0b00010101,
\r
228 0x05, // 0b00000101,
\r
229 0x01, // 0b00000001,
\r
230 0x00, // 0b00000000
\r
233 const char BannerMessage1[] = "Serial Lcd";
\r
234 const char BannerMessage2[] = "Tester By";
\r
235 const char BannerMessage3[] = "Kevin M. Rosenberg";
\r
236 const char BannerMessage4[] = "www.avrcode.com";
\r
239 unsigned char brightness = NUM_BRIGHTNESS_LEVELS - 1;
\r
241 void run_test (void) {
\r
242 double start_time = get_time();
\r
244 delay_ms(40); // Ensure time for LCD to be ready
\r
246 serial_putc (LED_OFF); // turn off the LED
\r
248 MakeCustomChar (Smiley_face, CGRAM_START_ADD_0);
\r
249 MakeCustomChar (Lh_Yagi_Antenna, CGRAM_START_ADD_1);
\r
250 MakeCustomChar (Diamond, CGRAM_START_ADD_2);
\r
251 MakeCustomChar (Rh_Yagi_Antenna, CGRAM_START_ADD_3);
\r
252 MakeCustomChar (Thin_cross, CGRAM_START_ADD_4);
\r
253 MakeCustomChar (Thick_cross, CGRAM_START_ADD_5);
\r
254 MakeCustomChar (Music_note, CGRAM_START_ADD_6);
\r
255 MakeCustomChar (Loop_Antenna, CGRAM_START_ADD_7);
\r
257 // Put the banner message on the display
\r
258 clear_display(); // Clear the display for the banner message
\r
259 display_banner(); // Display the banner message
\r
260 delay_ms(1000); // Display banner message for 1 seconds
\r
262 struct termios inopt;
\r
263 funlockfile(stdin);
\r
265 tcgetattr(stdin->_file, &inopt);
\r
267 tcgetattr(stdin->_fileno, &inopt);
\r
269 inopt.c_lflag &= ~ICANON; // non-canonical mode: no waiting on read
\r
270 inopt.c_cc[VMIN] = 0; // minimum read characters
\r
271 inopt.c_cc[VTIME] = 0; // timeout in deciseconds
\r
273 if (tcsetattr (stdin->_file, TCSANOW, &inopt) < 0) {
\r
275 if (tcsetattr (stdin->_fileno, TCSANOW, &inopt) < 0) {
\r
277 fprintf(stderr, "Error setting stdin parameters (%d: %s)\n",
\r
278 errno, strerror(errno));
\r
282 clear_display(); // Clear the display for updating data
\r
286 serial_putc (SERIAL_LCD_CMD_MODE); // Put the LCD in command mode
\r
287 serial_putc (LINE2_ADDR); // Set the LCD cursor position
\r
288 double elapsed = get_time() - start_time;
\r
289 snprintf(cntStr, sizeof(cntStr), "%.3lf", elapsed);
\r
290 serial_puts (cntStr);
\r
292 // Write the custom characters to the display
\r
293 serial_putc (SERIAL_LCD_CMD_MODE); // Put the LCD in command mode
\r
294 serial_putc (LINE1_ADDR); // Set the LCD cursor position
\r
295 serial_putc (DDRAM_START_ADD_0); // Display custom character 0
\r
296 serial_putc (DDRAM_START_ADD_1); // Display custom character 1
\r
297 serial_putc (DDRAM_START_ADD_2); // Display custom character 2
\r
298 serial_putc (DDRAM_START_ADD_3); // Display custom character 3
\r
299 serial_putc (DDRAM_START_ADD_4); // Display custom character 4
\r
300 serial_putc (DDRAM_START_ADD_5); // Display custom character 5
\r
301 serial_putc (DDRAM_START_ADD_6); // Display custom character 6
\r
302 serial_putc (DDRAM_START_ADD_7); // Display custom character 7
\r
305 char ch = getc_unlocked(stdin);
\r
308 serial_putc (LED_OFF); // Else, turn off the LED
\r
311 serial_putc (LED_ON); // Turn on the LED
\r
314 } else if (ch == 'b') {
\r
315 if (brightness >= NUM_BRIGHTNESS_LEVELS - 1)
\r
319 serial_putc (CMD_SET_BRIGHTNESS);
\r
320 printf("%u(%u)", brightness, (unsigned int) BRIGHTNESS_SCALE(brightness));
\r
321 serial_putc (BRIGHTNESS_SCALE(brightness));
\r
326 void MakeCustomChar (const char *Symbol, char Address) {
\r
327 // Set up the custom characters
\r
328 serial_putc (SERIAL_LCD_CMD_MODE); // Put the LCD in command mode
\r
329 serial_putc (Address); // Set the display to CGRAM mode, address 0
\r
330 // Output the 8 bytes of the custom character
\r
331 for (unsigned char i = 0; i < 8; i++) {
\r
332 serial_putc (Symbol[n]);
\r
336 // Clear the display
\r
337 void clear_display(void)
\r
339 serial_putc (SERIAL_LCD_CMD_MODE); // Put the LCD in command mode
\r
340 serial_putc (LCD_CLR_CMD); // Send the clear the LCD display
\r
341 delay_ms (30); // Wait for LCD controller to startup
\r
344 void tx_string_center (const char* str, unsigned char line_num) {
\r
346 switch (line_num) {
\r
362 serial_putc (SERIAL_LCD_CMD_MODE); // Put the LCD in command mode
\r
363 char offset = (lcdCols - strlen(str)) / 2; // offset to center line
\r
366 serial_putc (pos + offset); // Set LCD cursor position
\r
367 serial_puts (str); // Display string
\r
370 // Print the banner message
\r
371 void display_banner(void) {
\r
372 tx_string_center (BannerMessage1, 0);
\r
373 tx_string_center (BannerMessage2, 1);
\r
374 tx_string_center (BannerMessage3, 2);
\r
375 tx_string_center (BannerMessage4, 3);
\r
379 // Serial port section
\r
381 static int serialFd = -1; // serial port file descriptor
\r
383 int open_serial_port(char const* const port ) {
\r
384 struct termios options;
\r
387 serialFd = open (port, O_RDWR | O_NOCTTY | O_NONBLOCK);
\r
388 if (serialFd == -1)
\r
391 fcntl(serialFd, F_SETFL, 0);
\r
393 if (tcgetattr(serialFd, &options) < 0) {
\r
394 fprintf(stderr, "Error reading serial port parameters (%d: %s)\n",
\r
395 errno, strerror(errno));
\r
399 cfsetispeed (&options, B115200); // 115200 baud
\r
400 cfsetospeed (&options, B115200); // 115200 baud
\r
402 options.c_cflag |= (CLOCAL | CREAD); /* enable */
\r
403 options.c_cflag &= ~PARENB; // turn off parity
\r
404 options.c_cflag &= ~CSTOPB; // one stop bit
\r
405 options.c_cflag &= ~CSIZE;
\r
406 options.c_cflag |= CS8; // 8-bit input
\r
407 options.c_cflag &= ~CRTSCTS; // no flow control
\r
409 options.c_lflag &= ~ICANON; // non-canonical mode: no waiting on read
\r
410 options.c_lflag &= ~ECHO; // Do not echo bytes written
\r
411 options.c_lflag &= ~ECHOE;
\r
412 options.c_lflag &= ~ECHONL; // Do not echo newline
\r
413 options.c_lflag &= ~IEXTEN; // Disable implementation input processing
\r
414 options.c_lflag &= ~ISIG; // Disable signal processing
\r
416 options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INPCK | IXOFF
\r
417 | INLCR | IGNCR | ICRNL | IXON | IXANY);
\r
419 options.c_oflag &= ~OPOST; // Disable implementation output processing
\r
421 options.c_cc[VMIN] = 0; // minimum read characters
\r
422 options.c_cc[VTIME] = 0; // timeout in deciseconds
\r
424 /* set all of the options */
\r
425 tcflush(serialFd, TCIFLUSH);
\r
426 if (tcsetattr (serialFd, TCSANOW, &options) < 0) {
\r
427 fprintf(stderr, "Error setting serial port parameters (%d: %s)\n",
\r
428 errno, strerror(errno));
\r
433 if (tcgetattr(serialFd, &options) < 0) {
\r
434 fprintf(stderr, "Error to re-read serial port parameters (%d: %s)\n",
\r
435 errno, strerror(errno));
\r
438 if (options.c_lflag & ICANON) {
\r
439 fprintf(stderr, "Error: still in canonical serial mode\n");
\r
442 if (options.c_cc[VTIME] != 0) {
\r
443 fprintf(stderr, "Error: VTIME does not equal 0, but is %d\n", options.c_cc[VTIME]);
\r
446 if (options.c_cc[VMIN] != 0) {
\r
447 fprintf(stderr, "Error: VMIN does not equal 0, but is %d\n", options.c_cc[VMIN]);
\r
455 void close_serial_port(void) {
\r
456 if (serialFd >= 0) {
\r
462 void serial_puts (const char* str) {
\r
463 int len = strlen(str);
\r
464 int written = write (serialFd, str, len);
\r
466 fprintf (stderr, "Unable to write '%s' to fd %d, errno = %d (%s)\n",
\r
467 str, serialFd, errno, strerror(errno));
\r
470 } else if (written != len) {
\r
471 fprintf (stderr, "Unable to fully write '%s', %d bytes written\n",
\r
479 void serial_putc (const unsigned char c) {
\r
480 int written = write (serialFd, &c, 1);
\r
482 fprintf (stderr, "Unable to write '%c' to fd %d, errno = %d (%s)\n",
\r
483 c, serialFd, errno, strerror(errno));
\r