這兒說的串口包括兩種,232和485。其實,二者沒有本質的區(qū)別,驅動都是一樣的,只是232是雙工,而485是半雙工。
所以,485在正常情況下出于接收狀態(tài),一旦需要發(fā)送數據時,需要設置對于的IO的狀態(tài),使其出于發(fā)送狀態(tài)。
除此,沒有太多區(qū)別。下面已485為例具體總結。
2 打開串口
fd = open("/dev/s3c2410_serial0", O_RDWR|O_NOCTTY|O_NDELAY);
if (fd < 0)
{
perror("rs485: open 485 device");
return -1;
}
? ?就像打開普通的文件一樣操作open。? ?"/dev/s3c2410_serial0":是串口設備名
? ?O_RDWR:以可讀可寫的方式打開
? ?O_NOCTTY:不是控制終端控制的程序,否則,任何輸入都會影響該程序。具體沒有測試過,讓加就加上唄。
? ?O_NDELAY:不關心DCD信號線狀態(tài),即其他端口是否允許。否則,程序會在DCD信號線為低電平時停止。
? ? 目前,還不是很明白什么意思。暫時理解為設置為非阻塞狀態(tài)吧。
3 參數設置
串口的參數放在了結構體struct termios中。
首先,需要獲取參數信息。
struct termios options;
if(tcgetattr( fd,&options) != 0)
{
perror("tcgetattr");
return -1;
}
然后,具體設置參數。這塊是下面重點說的。我們以9600,8,N,1為例。因為,這是經典的配置。最后,保存新設置的參數。
tcflush(fd,TCIFLUSH);
if (tcsetattr(fd,TCSANOW,&options) != 0)
{
perror("tcsetattr");
return -1;
}
至此,參數設置完畢。3.1 波特率
/*波特率9600*/
? ? cfsetispeed(&options, B9600);
? ? cfsetospeed(&options, B9600);
3.2 數據位
? /*數據位8*/? ? options.c_cflag &= ~CSIZE;
? ? options.c_cflag |= CS8;
? ?設置數據位之前,必須先設置options.c_cflag &= ~CSIZE;
? ?作用是先屏蔽其他標志,然后修改數據位。
3.3 奇偶校驗
? ??/*奇偶校驗,無*/
? ? options.c_cflag &= ~PARENB;
? ? options.c_iflag &= ~INPCK;
3.4 停止位
? ??/*停止位,1*/
? ? options.c_cflag &= ~CSTOPB;
3.5 控制設置
? /*保證程序不會占用串口,并且可以能夠從串口中讀取輸入數據*/
? ? options.c_cflag |= (CLOCAL|CREAD);
? ? 這兩個標記必須加上。
3.6 輸入輸出
? options.c_oflag &= ~OPOST;
? 當OPOST不被使能,c_oflag的其他位也被忽略,效果相當于c_oflag=0
? options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
3.7 等待時間
? ??/*設置等待時間*/? ? options.c_cc[VTIME] = 0;?
? ? options.c_cc[VMIN] = 0;
? ? 關于這兩項設置,內容挺多的,首先VTIME是設置等待時間,最小單位是0.1s;VMIN是設置等待字符數。
? ? 有四種情況,具體如下:
? ? VTIME=0,VMIN=0:read時,有數據就讀,沒有就立馬返回。
? ? VTIME=1,VMIN=0:read時,有數據時就讀,讀的過程中沒有延時。沒有數據時,會阻塞0.1s,然后返回。
? ? VTIME=0,VMIN=5:read時,有數據時,讀到5個字節(jié),立馬返回。沒有數據時,會一直阻塞著,非得等到那個字節(jié)的到來。
? ? VTIME=1,VMIN=5:read時,有數據是,讀到一個字節(jié)后,才開始計時,然后,如果時間到或已經讀取了5個字節(jié),則返回。
? ? 沒有數據時,會一直阻塞著等待字符的到來,此時時間不起作用,因為還沒有計時。
? ? 注意:只有在打開設備時,設置為阻塞,即沒有設置O_NONBLOCK或O_NDELAY標志,上面的情況才會有效。
上面的1和5只是為了方便說明而已。總體來說,只要設置了VMIN,在沒有數據可讀時,就會一直阻塞。 ? ? ?
4 讀串口
關于讀串口,網上說了很多方式,有select查詢,軟中斷等。當然,最終實際去讀的都是用read函數。
我采取的方式是先用select查詢是否有數據需要讀,然后,每次讀取一個字節(jié),再去用狀態(tài)機檢查該字節(jié)。這樣一來,不用考慮
每次讀取多少,以及是否讀全等問題。代碼如下:
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
FD_ZERO(&readfd);
FD_SET(fd,&readfd);
ret=select(fd+1,&readfd,NULL,NULL,&timeout);
if(ret > 0)
{
if(FD_ISSET(fd,&readfd))
{
ret = read(fd, &cmd, 1);
if(ret > 0)
{
/*有信息,則去處理信息*/
}
}
}
? ? 處理信息函數根據當前狀態(tài)處理該字節(jié),并進入下一狀態(tài)。? ? 目前,不太確定加上select和直接用來read哪個效率高,有沒有必要加select。
5 寫串口
按說串口的寫只是用一個write函數即可實現(xiàn)的。但是,現(xiàn)在是485串口,寫串口時,需首先更改串口狀態(tài)。
下面重點說明一下如何更改串口狀態(tài)。
5.1 /sys/class/gpio
它是gpio到文件系統(tǒng)的映射,通過操作里面的文件,可以直接操作gpio。
首先,查看系統(tǒng)中有沒有/sys/class/gpio目錄。
如果沒有,需要在配置內核時加上Device Drivers —> GPIO Support —> /sys/class/gpio/。
5.2 文件介紹
該目錄下有export和unexport兩個可寫文件和其他的類似gpiochip146的軟連接。
export:通知系統(tǒng)需要導出控制的GPIO引腳編號。
unexport:通知系統(tǒng)取消導出。
gpiochip146:保存系統(tǒng)中GPIO寄存器的信息。該目錄在/sys/devices/virtual/gpio/gpiochip146
目錄下有l(wèi)abel、base、ngpio、subsystem、uevent。代表的意義如下:
label:設備信息,如cat label,得到的是GPH2
base:設備所管理的gpio初始編號,如cat base,得到的是146
ngpio:設備所管理的gpio總是,如cat ngpio,得到的是8,
即該gpiochip146管理從146到153這8個gpio接口
subsystem:符號鏈接,指向父目錄
uevent:內核與udev(自動設備發(fā)現(xiàn)程序)之間的通信接口
5.3 導出GPIO接口
首先,需要計算出引腳編號。
引腳編號 = 控制引腳的寄存器基數 + 控制引腳寄存器位數
該引腳即是控制485狀態(tài)轉換的引腳。根據具體的硬件連接來定,我的是146。
fd = open("/sys/class/gpio/export", O_WRONLY);
write(fd, "146", 3);
導出成功后,會在當前目錄下生成新的目錄gpio146。
5.4 gpio146
該目錄下會有幾個個文件,其中有value、direction和active_low
direction:具有讀寫屬性,控制GPIO接口的輸入輸出方向。
如果將"out"寫入該文件,該GPIO接口為輸出狀態(tài);
如果將"in"寫入該文件,該GPIO接口為輸入狀態(tài);
如果將"high"寫入該文件,那么在將GPIO接口置為輸出狀態(tài)的同時,也將value的值置為"1";
如果將"low"寫入value
當GPIO的方向為輸入時,可以通過v文件,那么在將GPIO接口置為輸出狀態(tài)的同時,將"0"寫入value文件。
通過對direction文件的讀操作還可以判斷當前GPIO接口的輸入/輸出狀態(tài)("in"/"out")。
value:具有讀寫屬性,表示當前GPIO接口的電平狀態(tài)。
當GPIO的方向為輸入時,可以通過value讀出當前GPIO接口的電平狀態(tài)高低("1"/"0",均以ASCII碼表示);
當GPIO方向為輸出時,可以向該文件寫入"1"/"0",控制當前GPIO接口的高/低電平。
active_low:具有讀寫屬性,值為"0"或"1",用于決定value中的值是否進行翻轉。
當值為"0"時,value中的"0"表示低電平,"1"表示高電平;
當值為"1"時,value中的"1"表示低電平,"0"表示高電平。
5.5 輸出
設置引腳為輸出方向。
fd = open("/sys/class/gpio/gpio146/direction", O_WRONLY); ?
write(fd, "out", 3);
5.6 發(fā)送模式
fd = open("/sys/class/gpio/gpio146/value", O_WRONLY); ?
write(fd, "1", 1);
此處高低根據硬件情況來。
5.7 取消導出
fd = open("/sys/class/gpio/unexport", O_WRONLY);
write(fd, "146", 3);
5.8 接收模式
發(fā)送完之后,需等待一會,然后再設置成接收模式。
這個時間我暫時設置成usleep(1000*len);
len:發(fā)送數據的長度。
fd = open("/sys/class/gpio/gpio146/value", O_WRONLY);
write(fd, "0", 1);
6 總結
232的操作比這簡單,寫時不需要再通過GPIO來更改狀態(tài)了。