共計 7503 個字符,預計需要花費 19 分鐘才能閱讀完成。
丸趣 TV 小編給大家分享一下怎么用 C 語言在 Linux 下實現 CC2530 上位機,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
一、環境簡介
1. 軟硬件環境
下位機:CC2530 OS:vmware + ubuntu
在這里彭老師采用的是 CC2530, 讀者也可以采用其他的板子,我們只需要該板子有串口,可以和 PC 通信,同時板子上有可設置的 led 燈、繼電器以及可以采集數據的傳感器即可。
2. 硬件連接圖
硬件連接圖如下:
該款 CC2530 已經集成了 CH340 芯片,usb 線連接電腦,即可被識別。
3. pc 下識別串口
如果該串口被 PC 獲取,名字為 COMn【n 為某整數】。
windows 下串口
4. ubuntu 下識別串口
首先需要 vmware 抓取串口【串口在同一時刻要么被 windows 抓取要么被 vmware 抓取】,按下圖所示,點擊連接即可:
虛擬機抓取串口
但是往往 ubuntu 中沒有 ch440 的驅動,經過實際測試,ubuntu14 及之前的版本都沒有這個驅動,ubuntu16 以上的版本有這個驅動。
如果沒有 ch440 驅動可以用以下方法安裝對應的驅動:
1 make 2 sudo make load 3 ls /dev/ttyUSB0
ubuntu 安裝串口驅動
按照上述步驟,會生成設備文件 **/dev/ttyUSB0**。
ls /dev/ttyUSB0 -l crw-rw---- 1 root dialout 188, 0 Jan 15 05:45 /dev/ttyUSB0
c : 字符設備 rw-rw—-:文件操作權限
188, 0:主次設備號
3、4 節提到的 usb 轉串口驅動和 linux 下驅動源碼后臺【GH】回復 ch440 即可獲得
【注意】如果是其他開發板,自行安裝其他的串口驅動。
二、模塊設計
上位機和下位機的通信往往都是通過串口,linux 下往往生成字符設備 ttyUSB0【有的是 ttyS0】,操作串口設備就只需要操作該字符設備即可。
下面我們設計上下位機的軟件模塊。
1. 信令
設計上位機,首先需要設計上位機下發給下位機的指令格式,上位機按照該指令格式發送命令給下位機,下位需嚴格按照該指令格式進行解析指令。
含義如下:
device:要操作的設備
data:對應的設備及其額外的數據
CRC:校驗碼
# : 信令終止符
信令格式可以根據需要擴展或者精簡。
其中 device 定義如下【可以根據實際情況進行擴展】:
#define DEV_ID_LED_ON 0X1 #define DEV_ID_LED_OFF 0X2 #define DEV_ID_DELAY 0X3 #define DEV_ID_GAS 0X4
【注意】為便于理解,我們暫不考慮效率問題。
2. 上傳數據
下位機需要采集傳感器的數據并通過串口上傳,數據結構定義如下:
struct data{ unsigned char device; unsigned char crc; unsigned short data; };
device 設備
data 采集的數據
crc 校驗碼
3. 功能模塊
現在就可以開始設計軟件的各個功能模塊了。
下位機
下位機流程圖
下位主要任務就是循環接收上位機通過串口下發的數據,然后解析該指令內容,操作對應的硬件。
上位機
上位機
上位機主要任務是打印菜單,由用戶針對菜單做出選擇,然后按照指令格式封裝命令,并通過串口將該命令下發給下位機。
三、下位機功能函數
cc2530 的操作原理,本文不討論,如果是其他開發板,只需要修改串口操作函數。
1. LED 初始化
/**************************************************************************** * 名 稱: InitLed() * 功 能: 設置 LED 燈相應的 IO 口 * 入口參數: 無 * 出口參數: 無 ****************************************************************************/ void InitLed(void) { P1DIR |= 0x01; //P1.0 定義為輸出口 LED1 = 0; }
2. 初始化 UART
/**************************************************************** * 名 稱: InitUart() * 功 能: 串口初始化函數 * 入口參數: 無 * 出口參數: 無 *****************************************************************/ void InitUart(void) { PERCFG = 0x00; // 外設控制寄存器 USART 0 的 IO 位置:0 為 P0 口位置 1 P0SEL = 0x0c; //P0_2,P0_3 用作串口(外設功能) P2DIR = ~0xC0; //P0 優先作為 UART0 U0CSR |= 0x80; // 設置為 UART 方式 U0GCR |= 11; U0BAUD |= 216; // 波特率設為 115200 UTX0IF = 0; //UART0 TX 中斷標志初始置位 0 U0CSR |= 0x40; // 允許接收 IEN0 |= 0x84; // 開總中斷允許接收中斷 }
3. 串口發送函數
/********************************************************************** * 名 稱: UartSendString() * 功 能: 串口發送函數 * 入口參數: Data: 發送緩沖區 len: 發送長度 * 出口參數: 無 ***********************************************************************/ void UartSendString(char *Data, int len) { uint i; for(i=0; i i++) { U0DBUF = *Data++; while(UTX0IF == 0); UTX0IF = 0; } }
4. 串口中斷處理函數
/********************************************************************** * 名 稱: UART0_ISR(void) 串口中斷處理函數 * 描 述: 當串口 0 產生接收中斷,將收到的數據保存在 RxBuf 中 **********************************************************************/ #pragma vector = URX0_VECTOR __interrupt void UART0_ISR(void) { URX0IF = 0; // 清中斷標志 RxBuf = U0DBUF; }
5. 煙霧傳感器數據讀取
/**************************************************************** * 名 稱: myApp_ReadGasLevel() * 功 能: 煙霧傳感器數據讀取 * 入口參數: 無 * 出口參數: 無 *****************************************************************/ 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 燈控制函數
/**************************************************************** * 名 稱: led_opt() * 功 能: LED 燈控制函數 * 入口參數: RxData:接收到的指令 flage:led 的操作,點亮或者關閉 * 出口參數: 無 *****************************************************************/ 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. 主程序
/**************************************************************************** * 主程序入口函數 ****************************************************************************/ void main(void) { CLKCONCMD = ~0x40; // 設置系統時鐘源為 32MHZ 晶振 while(CLKCONSTA 0x40); // 等待晶振穩定為 32M CLKCONCMD = ~0x47; // 設置系統主時鐘頻率為 32MHZ InitLed(); // 設置 LED 燈相應的 IO 口 InitUart(); // 串口初始化函數 UartState = UART0_RX; // 串口 0 默認處于接收模式 memset(RxData, 0, SIZE); while(1) { // 接收狀態 if(UartState == UART0_RX) { // 讀取數據,遇到字符 # 或者緩沖區字符數量超過 4 就設置 UartState 為 CONTROL_DEV 狀態 if(RxBuf != 0) { // 以 # 為結束符, 一次最多接收 4 個字符 if((RxBuf != #) (count 4)) { RxData[count++] = RxBuf; } else { // 判斷數據合法性,防止溢出 if(count = 4) { // 計數清 0 count = 0; // 清空接收緩沖區 memset(RxData, 0, SIZE); } else{ // 進入發送狀態 UartState = CONTROL_DEV; } } RxBuf = 0; } } // 控制控制外設狀態 if(UartState == CONTROL_DEV) { // 判斷接收的數據合法性 //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; // 清空接收緩沖區 memset(RxData, 0, SIZE); } } }
四、上位機功能函數
結構體
#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; };
函數
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] = # // 表示結束符 tcflush(serial_fd, TCIOFLUSH); int i = 0; for(i=0;i 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] = # // 表示結束符 write(serial_fd, cmd,sizeof(cmd)); sleep(1); len = read(serial_fd, msg,sizeof(struct data)); // 轉換讀取的 gas 數據格式 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; }
五、運行結果
1. 上位機運行界面
主菜單
2. 點亮 led 燈
點亮 led1:
3. 滅燈
熄滅 led1
4. 讀取煙霧傳感器數據
獲取煙霧數據
煙霧的數據是 079,可以點根華子,你會發現每次讀取的值都是在變化。
以上是“怎么用 C 語言在 Linux 下實現 CC2530 上位機”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注丸趣 TV 行業資訊頻道!