1 背景
BB-BLACK是16年買(mǎi)的一塊開(kāi)發(fā)板,時(shí)隔5年,板子上面卻看不到一絲歲月的痕跡,這兩天研究一下linux下串口的應(yīng)用開(kāi)發(fā)。
2 所使用的鏡像文件以及rootfs
| 名稱(chēng) | 描述 | 
|---|---|
| U-引導(dǎo) | bb-black-debian-u-boot.tar.bz2 | 
| 內(nèi)核 | bb-black-debian-kernel-3.8.tar.bz2 | 
| 根FS | prebuild-BBB-Exp-V2-eMMC-flasher-20140626.tar.gz/build/systems/Debian/rootfs.tar.gz | 
3 硬件連接
選擇串口4

4 cape的使用
BB-BLACK的一些引腳功能,外設(shè)接口設(shè)備都可以通過(guò)cape來(lái)管理,我們要使用串口4設(shè)備,那就需要向這個(gè)cape中插入串口4設(shè)備
4.1 添加環(huán)境變量
export SLOTS=/sys/devices/bone_capemgr.9/slots
4.2 查看當(dāng)前設(shè)備
cat $SLOTS 
 0: 54:PF--- 
 1: 55:PF--- 
 2: 56:PF--- 
 3: 57:PF--- 
 4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G
 5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
4.3 向cape中添加串口4設(shè)備
echo BB-UART4 > $SLOTS
4.4 檢查是否添加成功
ls ttyO*
ttyO0  ttyO4
4.5 設(shè)置波特率
stty -F /dev/ttyO4 115200
4.6 簡(jiǎn)單測(cè)試收發(fā)
echo "test" > /dev/ttyO4
cat /dev/ttyO4
5 安裝輔助工具
基本的收發(fā)測(cè)試正常后就可以進(jìn)行應(yīng)用開(kāi)發(fā)了,我們需要安裝一些工具來(lái)提高效率
5.1 掛載nfs文件系統(tǒng)
5.1.1 ubuntu下安裝nfs
sudo apt-get install nfs-kernel-server
sudo apt-get install nfs-common
5.1.2 設(shè)置路徑
/home/samba *(rw,sync,no_root_squash)
5.1.3 測(cè)試掛載
mount -t nfs 192.168.0.193:/home/samba /mnt -o nolock
如果報(bào)錯(cuò)提示報(bào)錯(cuò)可能是路徑不對(duì)、目錄權(quán)限問(wèn)題
mount.nfs: access denied by server while mounting
192.168.0.193:/home/samba
5.2 安裝samba實(shí)現(xiàn)windows和ubuntu之間的文件共享
5.2.1 安裝samba
sudo apt install samba samba-common -y
5.2.2 配置samba
sudo mkdir /home/samba
sudo vim /etc/samba/smb.conf
[samba]
    comment=samba
    path = /home/samba
    public = yes
    writable = yes
    create mask = 0777
    directory mask = 0777
5.2.3 開(kāi)機(jī)啟動(dòng)samba
systemctl enable smbd
5.2.4 啟動(dòng)samba
samba systemctl start smbd
5.3 安裝出錯(cuò)時(shí)可能需要重新配置一下dpkg
sudo dpkg --configure -a
sudo apt update
6 應(yīng)用測(cè)試
6.1 相關(guān)代碼
6.1.1 串口的配置、打開(kāi)、關(guān)閉、讀寫(xiě)接口
int uart_open(int fd,char*port)
{    
    fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);    
    if (fd<0)    
    {    
        perror("Can't Open Serial Port");    
        return(RES_UART_FALSE);    
    }    
    //恢復(fù)串口為阻塞狀態(tài)                                   
    if(fcntl(fd, F_SETFL, 0) < 0)    
    {    
        printf("fcntl failed!\\n");    
        return(RES_UART_FALSE);    
    }         
    else    
    {    
        printf("fcntl=%d\\n",fcntl(fd, F_SETFL,0));    
    }    
    return fd;    
}    
void uart_close(int fd)    
{    
    close(fd);    
}    
int uart_config(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)    
{    
    int   i;    
    int   status;    
    int   speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};    
    int   name_arr[] = {115200,  19200,  9600,  4800,  2400,  1200,  300};    
    struct termios options;    
    int res = tcgetattr( fd,&options);
    if( res  !=  0)    
    {    
        perror("SetupSerial 1");  
        return(RES_UART_FALSE);     
    }    
    //設(shè)置串口輸入波特率和輸出波特率    
    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]);      
        }    
    }         
    //修改控制模式,保證程序不會(huì)占用串口    
    options.c_cflag |= CLOCAL;    
    //修改控制模式,使得能夠從串口中讀取輸入數(shù)據(jù)    
    options.c_cflag |= CREAD;    
    //設(shè)置數(shù)據(jù)流控制    
    switch(flow_ctrl)    
    {    
        case 0 ://不使用流控制    
              options.c_cflag &= ~CRTSCTS;    
              break;       
        case 1 ://使用硬件流控制    
              options.c_cflag |= CRTSCTS;    
              break;    
        case 2 ://使用軟件流控制    
              options.c_cflag |= IXON | IXOFF | IXANY;    
              break;    
    }    
    //設(shè)置數(shù)據(jù)位    
    //屏蔽其他標(biāo)志位    
    options.c_cflag &= ~CSIZE;    
    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 (RES_UART_FALSE);     
    }    
    //設(shè)置校驗(yàn)位    
    switch (parity)    
    {      
        case 'n':    
        case 'N': //無(wú)奇偶校驗(yàn)位。
                 options.c_cflag &= ~PARENB;     
                 options.c_iflag &= ~INPCK;        
                 break;     
        case 'o':      
        case 'O'://設(shè)置為奇校驗(yàn)        
                 options.c_cflag |= (PARODD | PARENB);     
                 options.c_iflag |= INPCK;                 
                 break;     
        case 'e':     
        case 'E'://設(shè)置為偶校驗(yàn)      
                 options.c_cflag |= PARENB;           
                 options.c_cflag &= ~PARODD;           
                 options.c_iflag |= INPCK;          
                 break;    
        case 's':    
        case 'S': //設(shè)置為空格     
                 options.c_cflag &= ~PARENB;    
                 options.c_cflag &= ~CSTOPB;    
                 break;     
        default:      
                 fprintf(stderr,"Unsupported parity\\n");        
                 return (RES_UART_FALSE);     
    }     
    // 設(shè)置停止位     
    switch (stopbits)    
    {      
        case 1:       
                 options.c_cflag &= ~CSTOPB; break;     
        case 2:       
                 options.c_cflag |= CSTOPB; break;    
        default:       
                       fprintf(stderr,"Unsupported stop bits\\n");     
                       return (RES_UART_FALSE);    
    }    
    //修改輸出模式,原始數(shù)據(jù)輸出    
    options.c_oflag &= ~OPOST;    
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);    
    //options.c_lflag &= ~(ISIG | ICANON);    
    //設(shè)置等待時(shí)間和最小接收字符    
    options.c_cc[VTIME] = 1; /* 讀取一個(gè)字符等待1*(1/10)s */      
    options.c_cc[VMIN] = 1; /* 讀取字符的最少個(gè)數(shù)為1 */    
    //如果發(fā)生數(shù)據(jù)溢出,接收數(shù)據(jù),但是不再讀取 刷新收到的數(shù)據(jù)但是不讀    
    tcflush(fd,TCIFLUSH);    
    //激活配置 (將修改后的termios數(shù)據(jù)設(shè)置到串口中)    
    if (tcsetattr(fd,TCSANOW,&options) != 0)      
    {    
        perror("com set error!\\n");      
        return (RES_UART_FALSE);     
    }    
    return (RES_UART_TRUE);     
}    
int uart_init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)    
{    
    int err;    
    //設(shè)置串口數(shù)據(jù)幀格式    
    if (uart_config(fd,speed,flow_ctrl,databits,stopbits,parity) == RES_UART_FALSE)    
    {                                                             
        return RES_UART_FALSE;    
    }    
    else    
    {    
        return  RES_UART_TRUE;    
    }    
}    
int uart_read(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;    
    //使用select實(shí)現(xiàn)串口的多路通信    
    fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);    
    //printf("fs_sel = %d\\n",fs_sel);    
    if(fs_sel)    
    {    
        len = read(fd,rcv_buf,data_len);    
        return len;    
    }    
    else    
    {    
        return RES_UART_FALSE;    
    }         
}    
int uart_write(int fd, char *send_buf,int data_len)    
{    
    int len = 0;    
    len = write(fd,send_buf,data_len);    
    if (len == data_len )    
    {    
        printf("send data is %s\\n",send_buf);  
        return len;    
    }         
    else       
    {    
        tcflush(fd,TCOFLUSH);    
        return RES_UART_FALSE;    
    }    
}
6.1.2 測(cè)試代碼
#include "stdint.h"
#include "app_usart.h"
#include "app_config.h"
#include "rtservice.h"
#include "ful_communication.h"
#include       /*標(biāo)準(zhǔn)輸入輸出定義*/    
#include      /*標(biāo)準(zhǔn)函數(shù)庫(kù)定義*/    
#include      /*Unix 標(biāo)準(zhǔn)函數(shù)定義*/    
#include      
#include        
#include       /*文件控制定義*/    
#include     /*PPSIX 終端控制定義*/    
#include       /*錯(cuò)誤號(hào)定義*/    
#include 
#define SLOTS   "/sys/devices/bone_capemgr.9/slots"
#define CONFIG_SLOTS_UART_DEV "ADAFRUIT-UART4"
static int8_t s_read_firmware_version(uint32_t length, uint8_t *);
static int8_t s_config_baudrate(uint32_t length, uint8_t *);
extern void g_ful_com_detect_command(uint8_t checkData);
extern int8_t callback_register(action_t *obj);
int fd = -1;           //文件描述符,先定義一個(gè)與程序無(wú)關(guān)的值,防止fd為任意值導(dǎo)致程序出bug    
static action_t action_list[] =
{
    {CMD_READ_FW_VERSION,s_read_firmware_version},
    {CMD_CONFIG_BAUDRATE,s_config_baudrate},
};
static int8_t s_read_firmware_version(uint32_t length, uint8_t *pbuf)
{
    if(NULL == pbuf)
    {
        return -1;
    }
  printf("s_read_firmware_version length: [%d]\\n",length);
  printf("pbuf[0]=[%02x]\\n",pbuf[0]);
    if (3 == length)
    {
        uint8_t response_version[60]={0};
        sprintf(response_version,"%s_%s %s %s\\n",CONFIG_PRODUCT_NAME,CONFIG_FIRMWARE_VERSION,__DATE__,__TIME__);
        return uart_write(fd,response_version,strlen(response_version));
    }
    return 0;
}
static int8_t s_config_baudrate(uint32_t length, uint8_t *pbuf)
{
    if(NULL == pbuf)
    {
        return -1;
    }
    //if (3 == length)
    {
        uint32_t get_baudrate = (pbuf[1]<<24) | (pbuf[2]<<16) | (pbuf[3]<<8) | (pbuf[4]);
    printf("config baudrate to [%d]\\n",get_baudrate);
        int err = uart_init(fd,get_baudrate,0,8,1,'N');
    }
    return 0;
}
int main(int argc, char **argv)    
{
    int err;               //返回調(diào)用函數(shù)的狀態(tài)    
    int len;                            
    int i;    
    char rcv_buf[256];           
    if(argc != 2)    
    {    
        printf("Usage: %s /dev/ttySn      #1(receive data)\\n",argv[0]);
        printf("open failure : %s\\n", strerror(errno));
        return RES_UART_FALSE;    
    }  
    uint8_t action_index = 0;
    for (action_index=0;action_index<LENGTH_OF_ARRAY(action_list);action_index++)
    {
        callback_register(&action_list[action_index]);
    }
  int fd_drv_uart,count=0;
  //mount the Drive of Uart
  if ((fd_drv_uart = open(SLOTS, O_WRONLY)) < 0) 
  { 
    perror("SLOTS: Failed to open the file. \\n"); 
    return -1; 
  }
  printf("fd_drv_uart=%d\\n",fd_drv_uart); 
  if ((count = write(fd_drv_uart, CONFIG_SLOTS_UART_DEV,strlen(CONFIG_SLOTS_UART_DEV)))<0) 
  {
    perror("SLOTS:Failed to write to the file\\nFailed to mount the UART"); 
    //return -1; 
  }
    close(fd_drv_uart);
    fd = uart_open(fd,argv[1]); //打開(kāi)串口,返回文件描述符    
    do  
    {    
        printf("uart_init fd=%d\\n",fd); 
        err = uart_init(fd,115200,0,8,1,'N');
    if (err>=0)
        {
            break;
        }
        printf("err=%d\\n",err); 
    uart_close(fd);     
    fd = uart_open(fd,argv[1]);
    }while(RES_UART_FALSE == err || RES_UART_FALSE == fd); 
    while (1) 
    {   
        len = uart_read(fd, rcv_buf,sizeof(rcv_buf));  
        if(len > 0)    
        {    
            int idx = 0;
            printf("receive data len=%d\\n",len);  
            for (idx=0; idxuint8_t ch = rcv_buf[idx];
                printf("%02x ",ch);  
        g_ful_com_detect_command(ch);
            }
            printf("\\n");  
        }    
        usleep(10000);   
    }         
    uart_close(fd);     
    close(fd_drv_uart);
}
 
6.1.3 生成文件
CFILE = $(wildcard  *.c)
  CFILE += $(wildcard functions/*.c)
  CFILE += $(wildcard app/*.c)
  DIRSRC += -I functions/include
  DIRSRC += -I app/include
all:
  @gcc $(CFILE) $(DIRSRC) -o u_app
#gcc *.c  -o  usart
.PHONY:clean
clean:  
  @-rm u_app
6.1.4 目錄結(jié)構(gòu)
app
--include
--app_test.c
--app_uart.c
functions
--include
--fun_communication.c
Makefile
u_app
6.3 串口應(yīng)用的開(kāi)機(jī)自啟動(dòng)
開(kāi)機(jī)自啟動(dòng),運(yùn)行在后臺(tái),u_app需要添加可執(zhí)行權(quán)限
cp u_app /usr/my_app
vim /etc/rc.local
./usr/my_app/u_app /dev/ttyO4 &   
exit 0
6.4 測(cè)試
可正常解析指令,返回結(jié)果

7 總結(jié)
用虛擬機(jī)來(lái)跑ubuntu總是不太穩(wěn)定,如果不能進(jìn)入桌面系統(tǒng)也不能進(jìn)入tty控制臺(tái)基本上就要重裝了,還是得找個(gè)穩(wěn)定的ubuntu鏡像才行呀。
- 
                                Linux
                                +關(guān)注
關(guān)注
88文章
11586瀏覽量
217347 - 
                                串口
                                +關(guān)注
關(guān)注
15文章
1603瀏覽量
81655 - 
                                開(kāi)發(fā)板
                                +關(guān)注
關(guān)注
25文章
6051瀏覽量
111287 - 
                                應(yīng)用開(kāi)發(fā)
                                +關(guān)注
關(guān)注
0文章
63瀏覽量
9885 - 
                                BB-Black
                                +關(guān)注
關(guān)注
1文章
15瀏覽量
13457 
發(fā)布評(píng)論請(qǐng)先 登錄
基于Linux環(huán)境下串口通信的應(yīng)用設(shè)計(jì)
【教程】Linux下串口與工業(yè)協(xié)議開(kāi)發(fā) - 2
【教程】Linux下串口與工業(yè)協(xié)議開(kāi)發(fā) - 3
【教程】Linux下串口與工業(yè)協(xié)議開(kāi)發(fā) - 4
【教程】Linux下串口與工業(yè)協(xié)議開(kāi)發(fā) - 6
Linux下可用的串口助手在linux下使用串口終端
Linux下串口編程下載
Linux下ARM和單片機(jī)的串口通信設(shè)計(jì)
    
Linux下串口編程入門(mén)
基于WinCE_NET下串口驅(qū)動(dòng)開(kāi)發(fā)設(shè)計(jì)
    
基于嵌入式linux系統(tǒng)下的串口編程具體設(shè)置詳解
【嵌入式Linux(環(huán)境篇)】Linux下的串口助手 —— minicom或putty
    
          
        
        
linux下串口的應(yīng)用開(kāi)發(fā)
                
 
    
           
            
            
                
            
評(píng)論