Linux Serial Port

1. First check the Linux kernel version on the target board with the following command:

uname -r

It must be 3.16.0 or above.

2. Make sure the serial ports are initialized properly in the kernel by running command:

dmesg | grep tty

You should see something similar to the following:

[ 0.000000] console [tty0] enabled
[ 2.159659] 0000:00:1e.3: ttyS0 at MMIO 0xd0820000 (irq = 18, base_baud = 2764800) is a 16550A
[ 2.160221] 0000:00:1e.4: ttyS1 at MMIO 0xd081e000 (irq = 19, base_baud = 2764800) is a 16550A

On 32 bit Ubuntu 14.04.2 there may be a message “failed to request DMA” but it should be OK. Serial ports should still work. Note: It is important that the user checks kernel version and serial port names with commands in steps 1 & 2 before setting up Minicom. Normally, the port names show up as ttyS0 & ttyS1, but on Ubuntu 14.04.2 they are ttyS4 & ttyS5

3. Start Minicom with the following command:

sudo minicom –s

If Minicom is not already installed, install it with command:

sudo apt-get install minicom

  • On the popup menu, select “Serial port setup” -> Select “A” -> Change Serial Device to your desired COM port. E.g: /dev/ttyS0
  • Hit [Enter] twice to set serial port config information to Minicom
  • Select “Save setup as dlf”
  • Select “Exit”
  • Set up a second test station with Minicom or compatible terminal program.
  • With an RS -232 null modem cable, connect the active serial ports between both test stations
  • (Amazon: Null Modem Cable)
  • Type something on the Minicom console of the first ADLE3800PC board that data should appears on the terminal console on the second system.

Note: The procedure above has been verified on the following Linux distros:

  • Debian 8.1.0 –i386 with kernel 3.16.0
  • Fedora 22 –x86_64 with kernel 4.1.3
  • Ubuntu 14.04.2 –i386 with kernel 3.16.0
  • Lubuntu 14.04.3 –i386 with kernel 3.19.0

As we all know, most of the devices are treated as files in Linux, so is the UART for which I will give some introduction about the programming, the operations on UART are the same as the operations on files.

1. Steps about the UART programming in Linux

  1. Open the UART
  2. Initialize the UART
  3. Read from or Write to the UART
  4. Close the UART

2. Open the UART

Since the UART is treated as file, we must open the file before any other operation is done on the file.

2.1 device name

In linux, the UART device is accessed through the device file of UART device, which means we access the UART device by accessing the device files(e.g. /dev/ttyS0, /dev/ttyS1, /dev/ttyS2)

2.2 options

Call the Open() function to open the UART device, we need to use the parameter "O_NOCTTY" when opening the UART device. O_NOCTTY: It means we have opened one terminal device, the program will not become the controlling terminal of this port, if this parameter is not used, any input will affect the process of the system. O_NDELAY: It means we don't care the status of the UART's DCD signal.

2.3. Steps of opening the UART

2.3.1 open()

Open the UART by calling the function Open(), the returned value is the file descriptor.

2.3.2 fcntl()

Get the status of the UART, make sure whether the mode is blocked or not.

2.3.3 isatty()

Test whether the opened file descriptor is terminal device or not.

2.3.4 Function

int UART0_Open(int fd, char *port)
{
	fd = open(port, O_RDWR|O_NOCTTY|O_NDELAY);
	if(fd==FALSE)
	{
		perror("Can't open the serial port\n");
		return(FALSE);
	}
	if(fcntl(fd, F_SETFL, 0)<0)
	{
		printf("Fcntl failed\n");
		return(FALSE);
	}
	else
	{
		printf("Fcntl=%d\n", fcntl(fd,F_SETFL,0));
	}
	if(isatty(STDIN_FILENO)==0)
	{
		printf("Standard input is not a terminal device\n");
		return(FALSE);
	}
	else
	{
		printf("Isatty success\n");
	}
	printf("fd->open=%d\n", fd);
	return(fd);
}

3. Initialize the UART

In order to initialize the UART into a workable status, it's necessary to configure the UART with suitable baudrate, flow control, frame format(e.g. data bits, stop bit, parity bit).

3.1 Steps of initializing the UART

3.1.1 Set the baudrate

3.1.2 Set the data flow control

3.1.3 Set the frame format

We will use the data structure 'termios' to set the UART, first of all, we need to get the data point which points to the structure of the termios by calling the function tcgettattr(fd, &options). We can set the data bits by modifying the c_flag of the termios structure, CS5 means 5 data bits, CS6 means 6 data bits and so forth, remember to use CSIZE as the mask. Data flow control determins which flag we will use to mark the start and stop of the data transferring. After the above steps, we still need to set the minimum waiting time and minimum receiving data bytes. Finally we need to call tcsetattr() to active the configuration.

3.1.4 Function

int UART0_Set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
	int i;
	int status;
	int speed_arr[] = {B38400, B19200, B9600, B4800, B2400, B1200, B300};
	int name_arr[] = {38400, 19200, 9600, 4800, 2400, 1200, 300};
	struct termios options;
	if(tcgetattr(fd, &options)!=0)
	{
		perror("Setup UART\n");
		return(FALSE);
	}

	for( i=0; i < sizeof(speed_arr)/sizeof(int); i++)
	{
		if(speed == name_arr[i])
		{
			cfsetispeed(&Options, speed_arr[i]);
			cfsetospeed(&Options, speed_arr[i]);
		}
	}     

	options.c_cflag |= CLOCAL;
	options.c_cflag |= CREAD;

	switch(flow_ctrl)
	{
		case 0:	// No flow control is used
			options.c_cflag &= ~CRTSCTS;
			break;
		case 1:	// Hardware flow control
			options.c_cflag |= CRTSCTS;
			break;
		case 2:	// Software flow control
			options.c_cflag |= IXON | IXOFF | IXANY;
			break;
	}

	options.c_cflag &= ~CSIZE;	// Mask other flag bits
	switch (databits)
	{
		case 5:
			options.c_cflag |= CS5;
			break;
		case 6:
			options.c_cflag |= CS6;
			break;
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:    
			options.c_cflag |= CS8;
			break;  
		default:   
			fprintf(stderr, "Unsupported data size\n");
			return(FALSE);
	}

	switch(parity)
	{  
		case 'n':
		case 'N':	//No Parity bit
			options.c_cflag &= ~PARENB; 
			options.c_iflag &= ~INPCK;    
			break; 
		case 'o':  
		case 'O':	//Odd parity check
			options.c_cflag |= (PARODD | PARENB); 
			options.c_iflag |= INPCK;             
			break; 
		case 'e': 
		case 'E':	//Even parity check
			options.c_cflag |= PARENB;       
			options.c_cflag &= ~PARODD;       
			options.c_iflag |= INPCK;       
			break;
		case 's':
		case 'S':	//Space
			options.c_cflag &= ~PARENB;
			options.c_cflag &= ~CSTOPB;
			break; 
		default:  
			fprintf(stderr, "Unsupported parity\n");   
			return(FALSE); 
	} 

	//Set stop bit
	switch (stopbits)
	{  
		case 1:   
			options.c_cflag &= ~CSTOPB; 
			break; 
		case 2:   
			options.c_cflag |= CSTOPB; 
			break;
		default:   
			fprintf(stderr,"Unsupported stop bits\n"); 
		return (FALSE);
	}

    //Modify output mode, RAW data mode
	options.c_oflag &= ~OPOST;

	//set the minimum waiting time and minimum receiving bytes before unblocking
	options.c_cc[VTIME] = 1;
	options.c_cc[VMIN] = 1;

	tcflush(fd,TCIFLUSH);
	//active the configuration
	if(tcsetattr(fd, TCSANOW, &options) != 0)
	{
		perror("com set error!\n");  
		return (FALSE); 
	}

	return(TRUE);
}

4. Read and Write function

4.1 Read() and Write()

Reading from and Writing to UART is implemented by using Read() and Write() functions.

4.2 program

int UART0_Recv(int fd, char *rcv_buf, int data_len)
{
	int len,fs_sel;
	fd_set fs_read;
	struct timeval time;

	FD_ZERO(&fs_read);
	FD_SET(fd, &fs_read);

	time.tv_sec = 10;
	time.tv_usec = 0;

	// Using Select() function to realize the multiply channels' communication

	fs_sel = select(fd+1, &fs_read, NULL, NULL, &time);
	if(fs_sel)
	{
		len = read(fd, data, data_len);
		return(len);
	}
	else
	{
		return(FALSE);
	}     
}
int UART0_Send(int fd, char *send_buf, int data_len)
{
	int len = 0;
	len = write(fd, send_buf, data_len);
	if(len == data_len)
	{
		return(len);
	}     
	else
	{
		tcflush(fd,TCOFLUSH);
		return(FALSE);
	}
}

5. Close the UART

We must close the UART after using it, following is the implementation of the close action.

void UART0_Close(int fd)
{
	close(fd);
}

源码下载