嵌入式Linux串口操作接口

时间:2021-04-26 18:38:13

摘要

鉴于串口在嵌入式系统中广泛应用于操作GPRS模块、读卡器模块和GPS北斗模块等,串口操作成为嵌入式系统开发中必备工作,但是目前web中并没有提供一个封装良好的串口初始化和读写接口函数,因此本文基于工程实践提供了操作串口的接口函数和测试程序,为嵌入式Linux的串口操作提供了较大的便利。本文中提供的串口操作接口函数并未将常用串口参数如波特率、数据位、停止位和校验方式进行参数化,因此也有待进一步封装。

背景

由于智慧泊车工程项目中开发的硬件设备需要通过串口和AT命令访问GPRS模块,自己的工作环境也从涉密事业单位变成了能够随时能够访问Internet的公司,因此打算逐步将这些操作进行抽象和封装,以备不时之需。受到开发周期的限制,目前还未来得及将常用串口参数如波特率、数据位、停止位和校验方式进行参数化,在后续工作中会逐步完善和提高,也希望从业同行能够对其中不足进行批评指正,实现共同进步。

技术方案

串口操作涉及的文件主要是termios.h头文件中的struct termios结构,该结构的定义如下:

#define NCC 8
struct termio {
 unsigned short c_iflag;
 unsigned short c_oflag;
 unsigned short c_cflag;
 unsigned short c_lflag;
 unsigned char c_line;
 unsigned char c_cc[NCC];
};

详细参数定义内核代码中有较详细的解释,后续再博客修改时会陆续加上。
串口操作接口函数头文件uart.h文件

/* *@brief:uart operation interface *@author: *@email: *@date:30th Oct 2015 */
#ifndef __UART_H__
#define __UART_H__

#include <termios.h>
#include <unistd.h>
#include <fcntl.h>

#define DATA_BUFF_SIZE 256

int open_serial(char pchSerialName[], int nSerialNameLen);
int config_serial(int nFdSerial);
void signal_serial_data_prepared_handler(int nFdSerial);
int write_serial(int nFdSerial, char pchDataBuf[], int nDataBufLen);
int close_serial(int nFdSerial);

#endif

串口操作接口函数源文件uart.c文件

/* *@brief:uart operation interface *@author: *@email: *@date:30th Oct 2015 */
#include <string.h>
#include "uart.h"

char g_serial_data_buffer[DATA_BUFF_SIZE] = { 0 };

int open_serial(char pchSerialName[], int nSerialNameLen)
{
    int nFdSerial;
    if(pchSerialName == NULL || nSerialNameLen <= 0)
        return -1;
    nFdSerial = open(pchSerialName, O_RDWR|O_NOCTTY);
    if(nFdSerial < 0)
        return -1;
    return nFdSerial;
}

/* *@brief:configure serial port as 115200bps | 8 databits | 1 stopbit | no parity check *@param: nFdSerial:handle of serial port *@return: *@author: *@date:31th Oct 2015 *@todo:parameterize uart parameters */
int config_serial(int nFdSerial)
{
    struct termios tio;
    tcgetattr(nFdSerial, &tio);
    tcflush(nFdSerial, TCIFLUSH);
    cfsetispeed(&tio, B115200);//115200bps
    cfsetospeed(&tio, B115200);

    tio.c_cflag |= CS8;//8 data bits
    tio.c_cflag &= ~PARENB;//no parity check
    tio.c_oflag &= ~OPOST;
    tio.c_cflag &= ~CSTOPB;//1 stop bit
    tio.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
    tio.c_iflag &= ~(INPCK | BRKINT | ICRNL | ISTRIP | IXON);  

    tio.c_cc[VMIN] = 64;
    tio.c_cc[VTIME] = 1;

    if(tcsetattr(nFdSerial, TCSANOW, &tio) != 0)
    {
        perror("%s,L%d,in %s, configure serial failed.\n", __FILE__, __LINE__, __func__);
        return -1;
    }

    return 0;
}

void signal_serial_data_prepared_handler(int nFdSerial)
{
    int nReadBufLen = 0;
    memset(g_serial_data_buffer, 0, sizeof(g_serial_data_buffer));
    read(nFdSerial, g_serial_data_buffer, DATA_BUFF_SIZE);
}

int write_serial(int nFdSerial, char pchDataBuf[], int nDataBufLen)
{
    int nWriteBufLen = 0;
    nWriteBufLen = write(nFdSerial, pchDataBuf, nDataBufLen);
    return nWriteBufLen;
}

int close_serial(int nFdSerial)
{
    if(nFdSerial < 0)
    {
        perror("%s,L%d,in %s, close serial failed.\n", __FILE__, __LINE__, __func__);
        return -1;
    }
    close(nFdSerial);
    return 0;
}

串口测试主程序main.c文件如下。

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "uart.h"

#define UART_DEVICE_NAME "/dev/ttySAC0"

int nFdSerial = -1;

void system_exit(int nExitCode)
{
    if(nFdSerial > 0)
        close(nFdSerial);
    exit(nExitCode);
}

int main(int argc, char * * argv, char * * env)
{
    char chDataTest[16] = { 0 };
    int nIndex = 0;

    nFdSerial = open_serial(UART_DEVICE_NAME, strlen(UART_DEVICE_NAME));
    if(nFdSerial < 0)
    {
        printf("%s,L%d,in %s, open %s failed.\n", __FILE__, __LINE__, __func__, UART_DEVICE_NAME);
        return;
    }   

    config_serial(nFdSerial);

    signal(SIGINT, system_exit);

    for(nIndex = 0; nIndex < sizeof(chDataTest); nIndex++)
    {
        chDataTest[nIndex] = nIndex + 1;
    }   

    while(1)
    {
        write_serial(nFdSerial, chDataTest, sizeof(chDataTest));
        usleep(1000000);    
    }

    return 0;
}

在linux上交叉编译的Makefile文件如下。

#!/bin/sh
CROSS=arm-linux-
CC=$(CROSS)gcc
FLAGS=-lpthread -wall

objects = main.o uart.o 

uart_test:$(objects)
    $(CC) -o uart_test $(objects)  
    cp uart_test /mnt/hgfs/vmware_shared/images
clean:
    rm  -f uart_test *.o

测试结果如下图所示。
嵌入式Linux串口操作接口
上文中的源码链接如下:
http://www.pudn.com/downloads711/sourcecode/embedded/detail2854204.html

结语

目前提供的接口简化了串口操作,但是串口的主要参数还有待进一步参数化,另外还需要采用软中断用户自定义signal方式实现串口接收数据处理,在后续的工程开发中会进一步完善。