Linux下C語言編程(3):Uart編程

筆者今天來講講Linux下UART的使用,串口的發送與接收。

  來看看Beagle Bone的串口引腳設置。總共有5個串口,其中4個串口收發均可以使用,有一個串口3只可發不可收

在這裏插入圖片描述
  然後可以查看當前哪些串口設備可以使用。 (下圖中串口1,2,4可以直接使用 即通過 打開文件,寫和讀操作)
ls -l /dev/ttyO*
在這裏插入圖片描述

1. 串口初始化

打開串口設備
Uart_fd=open(buf,O_RDWR | O_NOCTTY | O_NONBLOCK| O_NDELAY);

設置串口參數:波特率、8位數據等等,具體參數可以查詢結構體 struct termios
newtio.c_cflag = BAUDRATE |CS8|CLOCAL|CREAD;
newtio.c_iflag = IGNPAR| ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag=~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 0;


typedef enum
{
    BBIO_OK, // No error
    BBIO_ACCESS, // Error accessing a file
    BBIO_SYSFS, // Some error with Sysfs files
    BBIO_CAPE, // some error with capes
    BBIO_INVARG, // Invalid argument
    BBIO_MEM,
    BBIO_GEN // General error
} BBIO_err;



typedef struct
{
    const char *name;
    const char *path;
    const char * dt;
    const char *rx;
    const char *tx;
} uart_t;


typedef struct
{
    const char *name;
    const char *path;
    const char * dt;
    const char *pin;
} adc_t;


//引腳設置
uart_t uart_table[] =
{
    { "UART1", "/dev/ttyO1","BB-UART1 ","P9_26", "P9_24"},
    { "UART2", "/dev/ttyO2","BB-UART2 ","P9_22", "P9_21"},
    { "UART3", "/dev/ttyO3","BB-UART3 ","P9_42", ""},
    { "UART4", "/dev/ttyO4","BB-UART4 ","P9_11", "P9_13"},
    { "UART5", "/dev/ttyO5","BB-UART5 ","P8_38", "P8_37"},
    { NULL, NULL,  NULL,  0, 0 }
};

int UartInit(int uartindex)
{
    struct termios newtio;
    int Uart_fd;
    printf("start...\r\n");
    BBIO_err bt;
    bt=uart_setup(uart_table[uartindex].dt);

    printf("UART Tree load %d\r\n",bt);
    set_pin_mode(uart_table[uartindex].rx,"uart");
    set_pin_mode(uart_table[uartindex].tx,"uart");

    char buf[30] = "/dev/ttyO";
    char port_nr[2];
    sprintf(port_nr, "%d", uartindex+1);
    strcat(buf,port_nr);

    Uart_fd=open(buf,O_RDWR | O_NOCTTY | O_NONBLOCK| O_NDELAY);
    if(Uart_fd<0)
    {
        perror(uart_table[uartindex].name);
        return -1;
    }
    printf("open ... baudrate %d\r\n",115200);

    bzero(&newtio,sizeof(newtio));
    newtio.c_cflag = BAUDRATE |CS8|CLOCAL|CREAD;
    newtio.c_iflag = IGNPAR| ICRNL;
    newtio.c_oflag = 0;
    newtio.c_lflag=~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN]  = 0;
    tcflush(Uart_fd,TCIFLUSH);
    tcsetattr(Uart_fd,TCSANOW,&newtio);
    printf("reading...\n\r");
    return Uart_fd;
}


BBIO_err load_device_tree(const char *name)
{
    char line[256];
    FILE *file = NULL;

    char slots[41];
    snprintf(ctrl_dir, sizeof(ctrl_dir), "/sys/devices/platform/bone_capemgr");


    /* Check if the Linux kernel booted with u-boot overlays enabled.
       If enabled, do not load overlays via slots file as the write
       will hang due to kernel bug in cape manager driver. Just return
       BBIO_OK in order to avoid cape manager bug. */
       
    if(uboot_overlay_enabled()) {
      return BBIO_OK;
    }

    if(pocketbeagle()) {
      return BBIO_OK;
    }
    snprintf(slots, sizeof(slots), "%s/slots", ctrl_dir);


    file = fopen(slots, "r+");
    if (!file) {

        return BBIO_CAPE;
    }

    while (fgets(line, sizeof(line), file))
    {
        //the device is already loaded, return 1

        if (strstr(line, name)) {
            fclose(file);
            return BBIO_OK;
        }
    }
    //if the device isn't already loaded, load it, and return
    fprintf(file, "%s", name);
    fclose(file);
    //0.2 second delay
    nanosleep((struct timespec[]){{0, 200000000}}, NULL);

    return BBIO_OK;
}


int set_pin_mode(const char *key, const char *mode)
{
	// char ocp_dir[] defined in common.h
	char ocp_dir[50];
	char path[60]; // "/sys/devices/platform/ocp/ocp:P#_##_pinmux/state"
	char pinmux_dir[20]; // "ocp:P#_##_pinmux"
	char pin[6]; //"P#_##"
	FILE *f = NULL;

	if (strlen(key) == 4)	// Key P#_# format, must inject '0' to be P#_0#
		snprintf(pin, sizeof(pin), "%.3s0%c", key,key[3]);
	else	//copy string
		snprintf(pin, sizeof(pin), "%s", key);
	strncpy(ocp_dir, "/sys/devices/platform/ocp", sizeof(ocp_dir));


	snprintf(pinmux_dir, sizeof(pinmux_dir), "ocp:%s_pinmux", pin);
	snprintf(path, sizeof(path), "%s/%s/state", ocp_dir, pinmux_dir);

	f = fopen(path, "w");
	if (NULL == f) {
		return -1;
	}
	syslog(LOG_DEBUG, "Pinmux file %s access OK", path);
	fprintf(f, "%s", mode);
	fclose(f);
	syslog(LOG_DEBUG, " set_pin_mode() :: Set pinmux mode to %s for %s", mode, pin);
	return 0;
}
2. 串口發送

串口發送比較簡單,直接調用write函數即可發送。

參數:串口設備文件句柄發送緩存發送的字節數

write(Uart_fd,Buf,ByteNum);
3. 串口接收

  串口接收比較複雜一點,需要調用一個函數select,來觸發接收,Linux下面中斷不太好用,所以需要調用這樣一個函數。

int select(int fd_max, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

作用:監視一個或多個文件句柄的狀態變化的,可阻塞也可不阻塞。

fd_max:傳入的監視文件描述符集合中最大的文件描述符數值 + 1,因爲select是從0開始一直遍歷到數值最大的標識符。

readfds:文件描述符集合,檢查該組文件描述符的可讀性。

writefds:文件描述符集合,檢查該組文件描述符的可寫性。

exceptfds:文件描述符集合,檢查該組文件描述符的異常條件。

struct timeval {
time_t tv_sec; /*秒 /
time_t tv_usec; /微秒/
};

	int  Uart_fd = UartInit(UART2);
	int retval;

	int RecByteNum=0;

	fd_set rfds_Uart;
	FD_ZERO(&rfds_Uart);
	FD_SET(Uart_fd, &rfds_Uart);
	//printf("--%d\n",rfds);
	retval=select(Uart_fd + 1, &rfds_Uart, NULL, NULL, NULL);
							 //tv控制選擇的時間,若規定時間內沒有收到數據
							 //則不監控該rfds。則若爲NULL,一直等到接收到數據
							 //再繼續程序執行。
	printf("--%d\n",rfds_Uart);
	printf("--%d\n",retval);

  瞭解這個函數之後,我們就可以調用這個函數監控串口設備是否有接收字符,如果存在字符,則調用read函數接收。

read函數參數:串口設備句柄接收字符的地址緩存區大小

函數返回接收的字符數

int ReadCom(int Uart_fd,char Readbuff[],fd_set rfds)
{
	int index=0;
	if(FD_ISSET(Uart_fd, &rfds))
	{
		int rc=0;
		index=0;
		do
		{
			usleep(2000);
			rc=read(Uart_fd, &Readbuff[index], R_BUF_LEN-index);
			index+=rc;
			//printf("--%d\n",rc);
		}
		while(rc!=0 && (index<R_BUF_LEN));
	}
	return index;
}

  最後筆者開啓一個線程來接收串口數據並把接收到的數據發送出去

pthread_t UsartId;
int UsartArg = 1;
int temp;
temp = MyThreadStart(pthread_UsartFun,&UsartId,&UsartArg);
if(temp == 0)
{
	printf("UsartFun successfully!\n");
}

void *pthread_UsartFun(void *arg)
{
	int  Uart_fd = UartInit(UART2);
	int retval;

	int RecByteNum=0;

	fd_set rfds_Uart;
	FD_ZERO(&rfds_Uart);
	FD_SET(Uart_fd, &rfds_Uart);
	//printf("--%d\n",rfds);
	retval=select(Uart_fd + 1, &rfds_Uart, NULL, NULL, NULL);
							 //tv控制選擇的時間,若規定時間內沒有收到數據
							 //則不監控該rfds。則若爲NULL,一直等到接收到數據
							 //再繼續程序執行。
	printf("--%d\n",rfds_Uart);
	printf("--%d\n",retval);

	while(*(int*)arg)
	{
		memset(RXBuf,0x00,sizeof(RXBuf));
		RecByteNum=ReadCom(Uart_fd,RXBuf,rfds_Uart);
		if(RecByteNum>0)
		{
			printf("UART:%s--%d**%d\n",RXBuf,strlen(RXBuf),RecByteNum);
			write(Uart_fd,RXBuf,RecByteNum);
			memset(RXBuf,0x00,sizeof(RXBuf));
			RecByteNum=0;
		}
	}
}

接收顯示串口助手打印分別如下。

在這裏插入圖片描述
在這裏插入圖片描述
上一篇:Linux下C語言編程(2):ADC編程
下一篇:Linux下C語言編程(4):TCP/IP 網絡通信

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章