1. 程式人生 > >嵌入式Linux驅動學習之USART串列埠控制:基於AT91SAM9261EK

嵌入式Linux驅動學習之USART串列埠控制:基於AT91SAM9261EK

    普通微控制器的串列埠操作比較容易,但是基於Linux系統的串列埠操作難不難呢?其實,基於Linux作業系統的串列埠操作分為兩個部分:串列埠驅動部分(底層驅動與設備註冊)與串列埠的應用程式(使用者程式)。一般廠家或是Linux核心已經提供了基於開發板的串列埠驅動,只需要修改或是註冊相應的串列埠裝置,就可以直接使用了。因此,只需要寫使用者應用程式即可。

   我手頭上有一塊AT91SAM9261EK的開發板,為了學習Linux買的,現在用到了,因此,學習一下串列埠的操作。


   首先看一下核心是否註冊了所有的串列埠及開發板可用的串列埠,檢視硬體板子,我這裡有 三個串列埠,其中一個為:DEBUG串列埠,另外兩個串列埠可以用來測試。啟動開發板,Linux啟動資訊會在控制檯打印出串列埠註冊的相關資訊,開始只有DEBUG註冊了,因此,需要修改核心裡的開發板相關的檔案:我這裡為:


linux-2.6.32.2/arch/arm/mach-at91/board-sam9261ek.c

找到相應的串列埠初始化註冊的函式:

static void __init ek_map_io(void) {     /* Initialize processor: 18.432 MHz crystal */     at91sam9261_initialize(18432000);     /* Setup the LEDs */     at91_init_leds(AT91_PIN_PA13, AT91_PIN_PA14);     /* DBGU on ttyS0. (Rx & Tx only) */     at91_register_uart(0, 0, 0);     /* Add USART0 USART1 USART2 */
    at91_register_uart(AT91SAM9261_ID_US0, 1, 0);     at91_register_uart(AT91SAM9261_ID_US1, 2, 0);     at91_register_uart(AT91SAM9261_ID_US2, 3, 0);     /* set serial console to ttyS0 (ie, DBGU) */     at91_set_serial_console(0); }
    然後重新編譯核心,發現另外三個串列埠的設備註冊成功。分別為:

atmel_usart.0: ttyS0 at MMIO 0xfefff200 (irq = 1) is a ATMEL_SERIAL
atmel_usart.1: ttyS1 at MMIO 0xfffb0000 (irq = 6) is a ATMEL_SERIAL atmel_usart.2: ttyS2 at MMIO 0xfffb4000 (irq = 7) is a ATMEL_SERIAL atmel_usart.3: ttyS3 at MMIO 0xfffb8000 (irq = 8) is a ATMEL_SERIAL
        串列埠註冊成功,操作的方式就像開啟檔案一樣了。分別為 /dev/ttyS0 /dev/ttyS1 /dev/ttyS2

       下面為操作串列埠的應用程式,為網上找到,自己修改了一下,實驗好用。連線好串列埠線,DEBUG也連線,我這裡使用USB轉串列埠的,設定好波特率:程式裡設定為:9600 8 N 1,串列埠助手傳送,開發板收到後,通過DEBUG口列印接收到的,並且再通過相應的串列埠傳送回來。

這裡涉及到串列埠的配置,檔案的open 與write。

程式如下:

//rs232_test.c

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>

#define BAUDRATE B9600


static int open_com(char *dev_name, long com_baud, int com_data, 
		int com_pry, int com_stop)
{
	uint	baud = 0, data = 0, parity = 0, stop = 0;
	struct	termios	strNewTio;
	int	fd;

	//set baudrate:
	switch(com_baud) {
	case 50: baud = B50; break;
	case 75: baud = B75; break;
	case 110: baud = B110; break;
	case 134: baud = B134; break;
	case 150: baud = B150; break;
	case 200: baud = B200; break;
	case 300: baud = B300; break;
	case 600: baud = B600; break;
	case 1200: baud = B1200; break;
	case 1800: baud = B1800; break;
	case 4800: baud = B4800; break;
	case 9600: baud = B9600; break;
	case 19200: baud = B19200; break;
	case 38400: baud = B38400; break;
	case 57600: baud = B57600; break;
	case 115200: baud = B115200; break;
	case 230400: baud = B230400; break;
	default:
		baud = B9600;
	}

	//set databits:
	switch(com_data) {
	case 5:	data = CS5; break;
	case 6:	data = CS6; break;
	case 7:	data = CS7; break;
	case 8:	data = CS8; break;
	default:
		data = CS8;
	}

	//set parity:
	switch(com_pry) {
	case 0: parity = 0; break;
	case 1: parity = PARENB | PARODD; break;
	case 2: parity = PARENB; break;
	default:
		parity = 0;
	}

	//set stopbits:
	switch(com_stop) {
	case 1: stop = 0; break;
	case 2: stop = CSTOPB; break;
	default:
		stop = 0;
	}

	fd = open(dev_name, O_RDWR | O_NOCTTY );
	if (fd < 0) {
		perror("com  open() error");
		return -1;
	}

	//set parameters:
	tcgetattr(fd, &strNewTio);

	strNewTio.c_cflag = baud | data | parity | stop | CREAD | CLOCAL;
	strNewTio.c_iflag = 0;
	strNewTio.c_lflag = 0;
	strNewTio.c_oflag = 0;

	if (tcsetattr(fd, TCSANOW, &strNewTio) < 0) {
		close(fd);
		perror("com tcsetattr()  error");
		return -1;
	}

	return fd;
}


int main(int argc, char *argv[])
{
    struct timeval tp;
    long time1=0;
    long time2=0;
    int fd1,fd2;
    struct termios tio1,tio2;

    int res,cnt,i;
    int mode = 1;            //rs232 mode
    unsigned char c;
    
    unsigned char dev1[10];
    unsigned char dev2[10];
    int tmp1,tmp2;

    if (argc == 0x0) {
        printf("Usage : rs232 com_no\n");
        return 0;
    }
    else {
        tmp1 = atoi(argv[1]);
        
        if (tmp1 > 16 ) {
            printf("Usage : rs232_loopback com_no com_no\n");
            return 0;
        }
        else {        
            sprintf(dev1, "/dev/ttyS%d", tmp1);
        }
    }
    fd1 = open_com(dev1, 9600, 8, 0, 1);

       
    if (fd1 < 0) {
        printf("can not open %s\n", dev1);
        close(fd1);
        return 0;
    }
    else
	{
 	printf("Open COM %s Success!!\n", dev1);
    }   

// clear the noise, made by change UART mode
usleep(10000);

	char	wr = 'k';
	char	rd [1000];
	int	ret;

	rd[0] = 0;
	while(1)
	{
		printf("=======%d\n ", i++);


		//write(fd1, &wr, 1);
		printf("=send:%c ", wr);

		usleep(100);

		ret = read(fd1, rd, 512);
		if (ret)
		{
			printf("\n=========receive:%s\n", rd);
			write(fd1, rd, strlen(rd));
			memset(rd, 0, 512);
		}
	}

	return 0;
}



      Makefile檔案如下:

all:
arm-none-linux-gnueabi-gcc rs232_test.c -o rs232_test
clean:
rm -rf *.o rs232_test

    在Shell下直接 執行#   make,就可以生成在開發板上執行的交叉編譯的可執行檔案,設定一下執行許可權,chmod 777 rs232_test

     然後連線好此串列埠的串列埠線,通過ftp等放到開發板的根檔案系統裡,在執行目錄下執行:

#  ./rs232_test 1              //這裡開啟/dev/ttyS1

開啟成功,就會提示成功的資訊,就可以通過串列埠助手往相應的串列埠上傳送資料了。


      通過串列埠助手傳送資料,串列埠助手就可以收到回覆,回覆的內容是傳送的內容。同時DEBUG控制檯會打印出收到的資訊。
     至此,基本的串列埠操作實現了。