用C語(yǔ)言在Linux下實(shí)現(xiàn)CC2530上位機(jī)-1
0、前言
網(wǎng)友提問(wèn)如下:

本地進(jìn)程之間 pipe shm msg 消息隊(duì)列, sem
兩個(gè)pc之間 socket /unix
raw 套接字:
BSD socket unix -> bill joy bsd分支,
匯總下這個(gè)網(wǎng)友的問(wèn)題,其實(shí)就是實(shí)現(xiàn)一個(gè)網(wǎng)關(guān)程序,內(nèi)容分為幾塊:
下位機(jī),通過(guò)串口與上位機(jī)相連;
下位機(jī)要能夠接收上位機(jī)下發(fā)的命令,并解析這些命令;
下位機(jī)能夠根據(jù)這些命令配置對(duì)應(yīng)的外設(shè)、讀取對(duì)應(yīng)的傳感器的數(shù)據(jù)上傳到上位機(jī);
主程序串口操作模塊:通過(guò)串口下發(fā)命令或者讀取下位機(jī)上傳的數(shù)據(jù)信息;
主程序網(wǎng)絡(luò)通信模塊:接收遠(yuǎn)程服務(wù)器下發(fā)的命令,并將下位機(jī)采集的數(shù)據(jù)上傳到服務(wù)器。
整體看來(lái),這個(gè)相當(dāng)于是一個(gè)小的項(xiàng)目了,內(nèi)容難度都比較大,下面我們會(huì)分為幾篇獨(dú)立的文章來(lái)講解。
本篇只討論如何給下位機(jī)編寫(xiě)一個(gè)簡(jiǎn)單的上位機(jī)。
一、環(huán)境簡(jiǎn)介
1. 軟硬件環(huán)境
下位機(jī):CC2530 OS:vmware + ubuntu
在這里彭老師采用的是CC2530,讀者也可以采用其他的板子,我們只需要該板子有串口,可以和PC通信,同時(shí)板子上有可設(shè)置的led燈、繼電器以及可以采集數(shù)據(jù)的傳感器即可。
2. 硬件連接圖
硬件連接圖如下:

該款CC2530已經(jīng)集成了CH340芯片,usb線連接電腦,即可被識(shí)別。
3. pc下識(shí)別串口
如果該串口被PC獲取,名字為COMn【n為某整數(shù)】。

windows下串口
4. ubuntu下識(shí)別串口
首先需要vmware抓取串口【串口在同一時(shí)刻要么被windows抓取要么被vmware抓取】,按下圖所示,點(diǎn)擊連接即可:

虛擬機(jī)抓取串口
但是往往ubuntu中沒(méi)有ch340的驅(qū)動(dòng),經(jīng)過(guò)實(shí)際測(cè)試,ubuntu14及之前的版本都沒(méi)有這個(gè)驅(qū)動(dòng),ubuntu16以上的版本有這個(gè)驅(qū)動(dòng)。
如果沒(méi)有ch340驅(qū)動(dòng)可以用以下方法安裝對(duì)應(yīng)的驅(qū)動(dòng):
- 1 make
- 2 sudo make load
- 3 ls /dev/ttyUSB0
ubuntu安裝串口驅(qū)動(dòng)
按照上述步驟,會(huì)生成設(shè)備文件**/dev/ttyUSB0**。
- ls /dev/ttyUSB0 -l
- crw-rw---- 1 root dialout 188, 0 Jan 15 05:45 /dev/ttyUSB0
c : 字符設(shè)備 rw-rw---- :文件操作權(quán)限
188, 0 :主次設(shè)備號(hào)
3、4節(jié)提到的usb轉(zhuǎn)串口驅(qū)動(dòng)和linux下驅(qū)動(dòng)源碼后臺(tái)【GH】回復(fù) ch340 即可獲得

【注意】如果是其他開(kāi)發(fā)板,自行安裝其他的串口驅(qū)動(dòng)。
二、模塊設(shè)計(jì)
上位機(jī)和下位機(jī)的通信往往都是通過(guò)串口,linux下往往生成字符設(shè)備ttyUSB0【有的是ttyS0】,操作串口設(shè)備就只需要操作該字符設(shè)備即可。
下面我們?cè)O(shè)計(jì)上下位機(jī)的軟件模塊。
1. 信令
設(shè)計(jì)上位機(jī),首先需要設(shè)計(jì)上位機(jī)下發(fā)給下位機(jī)的指令格式,上位機(jī)按照該指令格式發(fā)送命令給下位機(jī),下位需嚴(yán)格按照該指令格式進(jìn)行解析指令。

含義如下:
- device:要操作的設(shè)備
- data :對(duì)應(yīng)的設(shè)備及其額外的數(shù)據(jù)
- CRC :校驗(yàn)碼
- # :信令終止符
信令格式可以根據(jù)需要擴(kuò)展或者精簡(jiǎn)。
其中device定義如下【可以根據(jù)實(shí)際情況進(jìn)行擴(kuò)展】:
- #define DEV_ID_LED_ON 0X1
- #define DEV_ID_LED_OFF 0X2
- #define DEV_ID_DELAY 0X3
- #define DEV_ID_GAS 0X4
【注意】為便于理解,我們暫不考慮效率問(wèn)題。
2. 上傳數(shù)據(jù)
下位機(jī)需要采集傳感器的數(shù)據(jù)并通過(guò)串口上傳,數(shù)據(jù)結(jié)構(gòu)定義如下:
- struct data{
- unsigned char device;
- unsigned char crc;
- unsigned short data;
- };
- device 設(shè)備
- data 采集的數(shù)據(jù)
- crc 校驗(yàn)碼
3. 功能模塊
現(xiàn)在就可以開(kāi)始設(shè)計(jì)軟件的各個(gè)功能模塊了。
下位機(jī)

下位機(jī)流程圖
下位主要任務(wù)就是循環(huán)接收上位機(jī)通過(guò)串口下發(fā)的數(shù)據(jù),然后解析該指令內(nèi)容,操作對(duì)應(yīng)的硬件。
上位機(jī)

上位機(jī)
上位機(jī)主要任務(wù)是打印菜單,由用戶針對(duì)菜單做出選擇,然后按照指令格式封裝命令,并通過(guò)串口將該命令下發(fā)給下位機(jī)。
三、 下位機(jī)功能函數(shù)
cc2530的操作原理,本文不討論,如果是其他開(kāi)發(fā)板,只需要修改串口操作函數(shù)。
1. LED初始化
- /****************************************************************************
- * 名 稱: InitLed()
- * 功 能: 設(shè)置LED燈相應(yīng)的IO口
- * 入口參數(shù): 無(wú)
- * 出口參數(shù): 無(wú)
- ****************************************************************************/
- void InitLed(void)
- {
- P1DIR |= 0x01; //P1.0定義為輸出口
- LED1 = 0;
- }
2. 初始化UART
- /****************************************************************
- * 名 稱: InitUart()
- * 功 能: 串口初始化函數(shù)
- * 入口參數(shù): 無(wú)
- * 出口參數(shù): 無(wú)
- *****************************************************************/
- void InitUart(void)
- {
- PERCFG = 0x00; //外設(shè)控制寄存器 USART 0的IO位置:0為P0口位置1
- P0SEL = 0x0c; //P0_2,P0_3用作串口(外設(shè)功能)
- P2DIR &= ~0xC0; //P0優(yōu)先作為UART0
- U0CSR |= 0x80; //設(shè)置為UART方式
- U0GCR |= 11;
- U0BAUD |= 216; //波特率設(shè)為115200
- UTX0IF = 0; //UART0 TX中斷標(biāo)志初始置位0
- U0CSR |= 0x40; //允許接收
- IEN0 |= 0x84; //開(kāi)總中斷允許接收中斷
- }
3. 串口發(fā)送函數(shù)
- /**********************************************************************
- * 名 稱: UartSendString()
- * 功 能: 串口發(fā)送函數(shù)
- * 入口參數(shù): Data:發(fā)送緩沖區(qū) len:發(fā)送長(zhǎng)度
- * 出口參數(shù): 無(wú)
- ***********************************************************************/
- void UartSendString(char *Data, int len)
- {
- uint i;
- for(i=0; i<len; i++)
- {
- U0DBUF = *Data++;
- while(UTX0IF == 0);
- UTX0IF = 0;
- }
- }
4. 串口中斷處理函數(shù)
- /**********************************************************************
- * 名 稱: UART0_ISR(void) 串口中斷處理函數(shù)
- * 描 述: 當(dāng)串口0產(chǎn)生接收中斷,將收到的數(shù)據(jù)保存在RxBuf中
- **********************************************************************/
- #pragma vector = URX0_VECTOR
- __interrupt void UART0_ISR(void)
- {
- URX0IF = 0; // 清中斷標(biāo)志
- RxBuf = U0DBUF;
- }
5. 煙霧傳感器數(shù)據(jù)讀取
- /****************************************************************
- * 名 稱: myApp_ReadGasLevel()
- * 功 能: 煙霧傳感器數(shù)據(jù)讀取
- * 入口參數(shù): 無(wú)
- * 出口參數(shù): 無(wú)
- *****************************************************************/
- uint16 myApp_ReadGasLevel( void )
- {
- uint16 reading = 0;
- /* Enable channel */
- ADCCFG |= 0x80;
- /* writing to this register starts the extra conversion */
- ADCCON3 = 0x87;
- /* Wait for the conversion to be done */
- while (!(ADCCON1 & 0x80));
- /* Disable channel after done conversion */
- ADCCFG &= (0x80 ^ 0xFF);
- /* Read the result */
- reading = ADCH;
- reading |= (int16) (ADCH << 8);
- reading >>= 8;
- return (reading);
- }
6. LED燈控制函數(shù)
- /****************************************************************
- * 名 稱: led_opt()
- * 功 能: LED燈控制函數(shù)
- * 入口參數(shù): RxData:接收到的指令 flage:led的操作,點(diǎn)亮或者關(guān)閉
- * 出口參數(shù): 無(wú)
- *****************************************************************/
- void led_opt(char RxData[],unsigned char flage)
- {
- switch(RxData[1])
- {
- case 1:
- LED1 = (flage==DEV_ID_LED_ON)?ON:OFF;
- break;
- /* TBD for led2 led3*/
- default:
- break;
- }
- return;
- }
7. 主程序
- /****************************************************************************
- * 主程序入口函數(shù)
- ****************************************************************************/
- void main(void)
- {
- CLKCONCMD &= ~0x40; //設(shè)置系統(tǒng)時(shí)鐘源為32MHZ晶振
- while(CLKCONSTA & 0x40); //等待晶振穩(wěn)定為32M
- CLKCONCMD &= ~0x47; //設(shè)置系統(tǒng)主時(shí)鐘頻率為32MHZ
- InitLed(); //設(shè)置LED燈相應(yīng)的IO口
- InitUart(); //串口初始化函數(shù)
- UartState = UART0_RX; //串口0默認(rèn)處于接收模式
- memset(RxData, 0, SIZE);
- while(1)
- {
- //接收狀態(tài)
- if(UartState == UART0_RX)
- { //讀取數(shù)據(jù),遇到字符'#'或者緩沖區(qū)字符數(shù)量超過(guò)4就設(shè)置UartState為CONTROL_DEV狀態(tài)
- if(RxBuf != 0)
- {
- //以'#'為結(jié)束符,一次最多接收4個(gè)字符
- if((RxBuf != '#')&&(count < 4))
- {
- RxData[count++] = RxBuf;
- }
- else
- {
- //判斷數(shù)據(jù)合法性,防止溢出
- if(count >= 4)
- {
- //計(jì)數(shù)清0
- count = 0;
- //清空接收緩沖區(qū)
- memset(RxData, 0, SIZE);
- }
- else{
- //進(jìn)入發(fā)送狀態(tài)
- UartState = CONTROL_DEV;
- }
- }
- RxBuf = 0;
- }
- }
- //控制控制外設(shè)狀態(tài)
- if(UartState == CONTROL_DEV)
- {
- //判斷接收的數(shù)據(jù)合法性
- //RxData[]: | device | data |crc | # |
- //check_crc: crc = device ^ data
- //if(RxData[2] == (RxData[0]^RxData[1]))
- {
- switch(RxData[0])
- {
- case DEV_ID_LED_ON :
- led_opt(RxData,DEV_ID_LED_ON);
- break;
- case DEV_ID_LED_OFF:
- led_opt(RxData,DEV_ID_LED_OFF);
- break;
- case DEV_ID_DELAY:
- break;
- case DEV_ID_GAS:
- send_gas();
- break;
- default:
- break;
- }
- }
- UartState = UART0_RX;
- count = 0;
- //清空接收緩沖區(qū)
- memset(RxData, 0, SIZE);
- }
- }
- }
四、 上位機(jī)功能函數(shù)
結(jié)構(gòu)體
- #define DEV_ID_LED_ON 0X1
- #define DEV_ID_LED_OFF 0X2
- #define DEV_ID_DELAY 0X3
- #define DEV_ID_GAS 0X4
- struct data{
- unsigned char device;
- unsigned char crc;
- unsigned short data;
- };
函數(shù)
- void uart_init(void )
- {
- int nset1,nset2;
- serial_fd = open( "/dev/ttyUSB0", O_RDWR);
- if(serial_fd == -1)
- {
- printf("open() error\n");
- exit(1);
- }
- nset1 = set_opt(serial_fd, 115200, 8, 'N', 1);
- if(nset2 == -1)
- {
- printf("set_opt() error\n");
- exit(1);
- }
- }
- int Menu()
- {
- int option;
- system("clear");
- printf("\n\t\t************************************************\n");
- printf("\n\t\t** ALARM SYSTERM **\n");
- printf("\n\t\t** 1----LED **\n");
- printf("\n\t\t** 2----GAS **\n");
- printf("\n\t\t** 0----EXIT **\n");
- printf("\n\t\t************************************************\n");
- while(1)
- {
- printf("Please choose what you want: ");
- scanf("%d",&option);
- if(option<0||option>2)
- printf("\t\t choose error!\n");
- else
- break;
- }
- return option;
- }
- // RxData[]: | device | data |crc | # |
- void led()
- {
- int lednum = 0;
- int onoff;
- char cmd[4];
- //選擇led燈
- while(1)
- {
- printf("input led number :[1 2]\n#");
- scanf("%d",&lednum);
- //check
- if(lednum<1 || lednum >2)
- {
- printf("invalid led number\n");
- system("clear");
- continue;
- }else{
- break;
- }
- }
- printf("operation: 1 on , 0 off\n");
- scanf("%d",&onoff);
- if(onoff == 1)
- {
- cmd[0] = DEV_ID_LED_ON;
- }else if(onoff == 0)
- {
- cmd[0] = DEV_ID_LED_OFF;
- }else{
- printf("invalid led number\n");
- return;
- }
- cmd[1] = lednum;
- //fulfill crc area
- cmd[2] = cmd[0]^cmd[1];
- cmd[3] = '#';//表示結(jié)束符
- tcflush(serial_fd, TCIOFLUSH);
- int i = 0;
- for(i=0;i<4;i++)
- {
- printf("%d ",cmd[i]);
- }
- printf("\n");
- write(serial_fd,&cmd,sizeof(cmd));
- sleep(1);
- }
- // RxData[]: | device | data |crc | # |
- void gas()
- {
- int len ;
- unsigned short GasLevel;
- struct data msg;
- char gas[4]={0};
- char cmd[4];
- cmd[0] = DEV_ID_GAS;
- cmd[3] = '#';//表示結(jié)束符
- write(serial_fd,&cmd,sizeof(cmd));
- sleep(1);
- len = read(serial_fd,&msg,sizeof(struct data));
- //轉(zhuǎn)換讀取的gas數(shù)據(jù)格式
- GasLevel = msg.data;
- gas[0] = GasLevel / 100 + '0';
- gas[1] = GasLevel / 10%10 + '0';
- gas[2] = GasLevel % 10 + '0';
- printf("%s\n",gas);
- getchar();
- }
- void run()
- {
- int x;
- while(1)
- {
- x=Menu();
- switch(x)
- {
- case 1:
- led();
- break;
- case 2:
- gas();
- break;
- case 0:
- printf("\n\t\t exit!\n\n");
- close(serial_fd);
- exit(0);
- default:
- fg=1;
- break;
- }
- if(fg)
- break;
- }
- }
- int main()
- {
- uart_init();
- run();
- return 0;
- }
五、 運(yùn)行結(jié)果
1. 上位機(jī)運(yùn)行界面

主菜單
2. 點(diǎn)亮led燈
點(diǎn)亮led1:

3. 滅燈

熄滅led1
4. 讀取煙霧傳感器數(shù)據(jù)

獲取煙霧數(shù)據(jù)
煙霧的數(shù)據(jù)是079,可以點(diǎn)根華子,你會(huì)發(fā)現(xiàn)每次讀取的值都是在變化。
OK!至此為止,一個(gè)簡(jiǎn)易的CC2530上位機(jī)我們就編寫(xiě)完畢,如果想將從串口獲取的數(shù)據(jù)的值發(fā)送到遠(yuǎn)端服務(wù)器,后續(xù)文章我們將繼續(xù)討論。