你了解什麼是Arduino嗎?你知道Arduino能做些什麼嗎?今天就讓我們來認識一下Arduino!
Arduino是一款便捷靈活、方便上手的開源電子原型平臺。
包含硬體(各種型號的Arduino板)和軟體(Arduino IDE)。由一個歐洲開發團隊於2005年冬季開發。其成員包括Massimo Banzi、David CuarTIelles、Tom Igoe、Gianluca MarTIno、David Mellis和Nicholas ZambetTI等。
本分享一個通過arduino點亮LED的案例。
需要的工具
Arduino Uno開發板
臺式機
Arduino到臺式機連接線
這個小項目主要是帶大家入門嵌入式開發領域。
我們的核心是Arduino Uno開發板,下面這張圖是Arduino的電路圖。我把它的幾個大塊標記出來,下面我一一來說明下。
Voltage Regulator: 它的任務是為系統提供穩定的3.3V和5V的電壓。在藍色區域有兩個voltage regulator,一個是LP2985,輸入5V,輸出3.3V;一個是NCP1117,輸入最高20V,輸出5V。Arduino的供電有兩種,一種是USB供電,這時候只從藍色區域左下角的USBVCC為板子提供5V電壓,然後通過一個regulator為板子提供3.3V電壓。另一種供電是通過供電插口(在板子上USB插口的下方有個圓形的黑色電源插口),這個供電插口是藍色區域中靠中間的長方形區域,它的電壓可以最高到20V,然後通過NCP1117變成5V電壓,然後再通過LP2985變成3.3V電壓。這裡面有個值得注意的地方是藍色區域的USBVCC出來後連接了一個三極體,三極體上面有個比較器,比較器的正向輸入端連接了一個分壓電路,反向輸入端連接著3.3V。它的目的是如果從供電插口輸入的電壓不足5V,那就用USB的5V電壓,否則就用供電插口的5V電壓。
USB Control chip: USB的控制晶片,買回來的Arduino中這個晶片的固件都是已經在裡面的,它的作用是把USB接口的東西轉成串行通信數據(在電路圖紅色的Serial Comm部分)發送給CPU,還用把CPU從串行通信發出來的東西,傳換成USB信號發送給PC機。
Main CPU: 主CPU是Atmel328P。8-bit CPU, 因為Arduino沒有外接的serial flash 或者外接的SDRAM,所以根據晶片手冊,一共有32KB 晶片上的programming flash,編譯的代碼可以放在這個flash裡面。有2KB的SRAM,一些寄存器的信息,stack和heap,全局變量等都放在RAM裡。
Crystal: 16MHz的晶振
LED: LED的輸入標記是SCK,對應連接的是atmel328P上的B5管腳。LED連接了一個放大器,目的是電流不通過放大器,只是通過電壓來控制LED,這樣的話B5管腳可以做其他用途。
Serial Comm: 串口通信埠,在CPU上通過usart給PC端發送數據。
DDRB |= (1 << PB5); //配置PB5的data direction registerPORTB |= (1 << PB5); //使PB5輸出高電平PORTB &= ~(1 << PB5); //使PB5輸出低電平
當能夠控制LED的開關,這時候可以說明編譯器和Avrdude的代碼下載也沒問題。這時候為了我們更好地debug程序,我們需要讓串口通信正常工作,這樣可以把信息列印到PC端。
根據Arduino電路圖,我們需要讓紅色區域的serial comm正常工作。USB controller chip可以把數據從USB埠輸出到PC端。
在atmel328P的data sheet, section 24。有詳細的USART的描述,對於USART來說,首先肯定是要配置波特率了,然後需要配置USART的一些傳輸模式,比如一次發8 bit 或者一次發7 bit,有沒有stop bit等等。
在傳輸過程,就是不斷的把想要發送的數據寫到寄存器裡,然後Atmel328P會通過兩個pin發送到USB controller chip,然後USB controller chip再發送給PC端。
//配置USARTUBRR0H = (uint8_t)(BAUDRATE_9600_UBRR >> 8); // 配置波特率UBRR0L = (uint8_t)BAUDRATE_9600_UBRR;UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1 << RXCIE0); //enable接收和發送數據 UCSR0C = (3<<UCSZ00); //配置發送模式,8 bit 數據 1 bit stop bit
//發送數據void USART_Transmit(uint8_t * Data, uint16_t Length){ uint16_t i; for (i = 0; i < Length; i++) { /* Wait for empty transmit buffer */ while (!( UCSR0A & (1<<UDRE0))); /* Put data into buffer, sends the data */ UDR0 = Data[i]; }}
//接收數據, 使用中斷接收數據ISR(USART_RX_vect){ uint8_t ReceivedData; ReceivedData = UDR0;}
當確定Arduino和PC端可以正常通信,我們就可以開始寫command line interface。顧名思義,是通過PC端輸入指令,Arduino做相應的動作。一般大部分的電子產品都有自己的cli用來和產品通信,很多情況如果要開發新的功能,就增加一條新的command,然後PC端的driver可以發送這個新的command給嵌入式設備,這樣它就可以執行新的功能了。
command line interface源碼, 裡面可以輸入指令使Arduino的LED開啟或者關閉。
在command line interface的實現過程中,有些蠻有趣的地方。
我用了一個circular buffer來實現數據的接收和處理,有一個read index和一個write index,使用buffer的目的就在於用戶輸入命令的速度要和計算機處理的速度不同,所以我們需要一個buffer來平衡它們。比如計算機要處理某個命令需要很久,而用戶在這個命令後又連續輸入了好幾個其他的命令,所有其他的命令都會放到這個circular buffer然後依次處理。
這個小project使用了這個volatile來定義一個變量, USART_StartCmdProcess
,用來記錄當前在receive buffer中有多少個命令。原因是我們是在中斷中把這個變量自加1,當編譯器編譯這段代碼的時候,如果沒有volatile的話,編譯器並不知道什麼時候這個變量什麼時候會加1,因為中斷在任何時候都可能發生。因此在主函數裡面有if (變量 > 0),這個判斷會被編譯器認為永遠不會發生(編譯器將這個判斷為永遠false)。所以加了volatile就強制編譯器在編譯去真正判斷地判斷變量的值,簡單地說是不會優化主函數裡面地if (變量 > 0)。
輸入GetLedStatus, Arduino返回LED OFF
輸入SetLed ON,Arduino點亮LED
輸入GetLedStatus, Arduino返回LED ON
輸入SetLed OFF, Arduino關閉LED