摘要
鉴于串口在嵌入式系统中广泛应用于操作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
测试结果如下图所示。
上文中的源码链接如下:
http://www.pudn.com/downloads711/sourcecode/embedded/detail2854204.html
结语
目前提供的接口简化了串口操作,但是串口的主要参数还有待进一步参数化,另外还需要采用软中断用户自定义signal方式实现串口接收数据处理,在后续的工程开发中会进一步完善。