2016-6-17 16:41 上傳
點(diǎn)擊文件名下載附件
----------------------------偶是分隔線----------------------------------------------------
Scartch的簡(jiǎn)單編程
如果你代碼下載完畢,串口連接完成,打開S4A之后,就可以看到6路AD模擬量和兩個(gè)開關(guān)的數(shù)字量的值了。圖中的COMx是你實(shí)際使用的串口。在我的電腦上,這個(gè)COM3是使用FT232轉(zhuǎn)換出來的。還要注意圖片中的旗幟的標(biāo)志和那個(gè)紅色的大圓點(diǎn)。旗幟代表運(yùn)行我們使用S4A拖拽的程序,圓點(diǎn)代表停止。
在S4A的左上角可以看到動(dòng)作,控制,外觀,等等控件。里面有各種各樣的工具供我們使用。
- 動(dòng)作一欄含有數(shù)字引腳的輸出,舵機(jī)控制,pwm輸出等。是對(duì)硬件的實(shí)際控制。
- 控制一欄中是常見的循環(huán),if else分支語句,延時(shí)等函數(shù)。
- 數(shù)字和邏輯運(yùn)算里面含有常用的大于小于判斷,加減乘除等語法。
- 變量一欄中可以設(shè)置一個(gè)自己的變量,用于存儲(chǔ)AD的采集值,pwm的輸出值等等。更奇葩的是,還有鏈表這個(gè)東西。一直搞不清楚對(duì)小孩子而言,鏈表可以干什么。
腳本這個(gè)位置就是我們寫程序的地方。只要將程序按照邏輯結(jié)構(gòu)放在這里就可以。一定要注意的是,開始的語句是一個(gè)“當(dāng)旗幟被點(diǎn)擊”的語句。這條語句類似于C語言中的main函數(shù),程序開始執(zhí)行的地方。下面就來簡(jiǎn)單的寫一個(gè)閃燈的程序。
這就是我們平時(shí)在單片機(jī)中常寫的Hello world程序。是不是很簡(jiǎn)單呢?如果想修改延時(shí)的時(shí)間怎么辦?雙擊“等待1秒”中的1,然后填上你想要的時(shí)間并按回車。這個(gè)時(shí)間是小數(shù)的形式。可以是0.1 。
我們平時(shí)在寫程序的時(shí)候,經(jīng)常會(huì)設(shè)置一個(gè)變量,用于存儲(chǔ)延時(shí)的時(shí)間。在S4A中,變量的設(shè)置也是支持的。在變量一欄中點(diǎn)擊“新建一個(gè)變量”,之后填上變量的名字,就出現(xiàn)很多關(guān)于這個(gè)變量操作的控件。比如設(shè)置變量的值,將變量增加一個(gè)數(shù)值等等。可是,為什么沒有減小變量的數(shù)值呢?其實(shí)只要將“將變量delayTime的值增加[]”中的空格處填寫一個(gè)負(fù)數(shù)就可以了。比如-10 。可是小孩子如果不知道負(fù)數(shù)怎么辦呢?
在程序?qū)懲曛螅c(diǎn)擊右上角圓點(diǎn)旁邊的旗幟圖標(biāo)就可以運(yùn)行了。
不知道你家孩子會(huì)不會(huì)喜歡上!
------------偶還是分隔線------------------------
S4A Arduino源代碼解析
準(zhǔn)備工具:
- Arduino IDE(如果沒有,可以使用文本編輯工具代替。比如Notepad++或者記事本)。
- S4A串口通訊協(xié)議。在附件中提供下載。
- S4A Arduino通訊源代碼。在附件中提供下載。
Arduino的函數(shù)
- int main( void )
- {
- setup();
- for (;;){
- loop();
- }
- return 0;
- }
復(fù)制代碼
setup()和loop()是我們需要自己填充的。setup只會(huì)被調(diào)用一次,完成代碼的一些初始化工作。loop是需要一直循環(huán)執(zhí)行的。
Arduino控制IO引腳的幾個(gè)函數(shù)在arduino.h(SAM系列的在wiring_digital.h)中。
- pinMode用于設(shè)置引腳的工作方式,可以是INPUT,OUTPUT。
- digitalWrite是設(shè)置引腳的電平值。類似于STM32庫文件中的GPIO_WriteBit。
- digitalRead是讀取引腳的電平值。類似于STM32庫文件中的GPIO_ReadInputDataBit。
- analogRead讀取模擬引腳的電壓值。因?yàn)锳rduino是10位AD,所以這個(gè)函數(shù)的返回值在0-1023之間。
- analogWrite并不是一個(gè)DA輸出,而是pwm輸出。在Arduino SAM中被改為了pwmWrite。
- void pinMode(uint8_t, uint8_t);
- void digitalWrite(uint8_t, uint8_t);
- int digitalRead(uint8_t);
- int analogRead(uint8_t);
- void analogWrite(uint8_t, int);
復(fù)制代碼
S4A的數(shù)據(jù)結(jié)構(gòu)- typedef enum {
- input, servomotor, pwm, digital }
- pinType;
- typedef struct pin {
- pinType type; //Type of pin
- int state; //State of an output
- //byte value; //Value of an input. Not used by now. TODO
- };
復(fù)制代碼
上面的代碼在Arduino中確實(shí)能夠編譯。但是在C語言中確不能。不知道是不是C++中typedef的一個(gè)新特性,還是一個(gè)bug。我在移植的時(shí)候直接修改了這段:
- typedef struct pin {
- pinType type; //Type of pin
- int state; //State of an output
- //byte value; //Value of an input. Not used by now. TODO
- } pin ;
復(fù)制代碼
struct pin是一個(gè)結(jié)構(gòu)體,里面存儲(chǔ)了引腳的信息,包括引腳的type和引腳的state。type是引腳的模式,可以是input,servomotors,pwm或者digital。state是引腳的值,有效取值從0到1023 。
S4A規(guī)定:
- 如果一個(gè)引腳是input(用于輸入)或者digital(用于輸出),那么state取值只能0或者1023 。0表示低電平,1023表示高電平。
- 如果一個(gè)引腳是servomotor(舵機(jī)接口)或者pwm,state的取值只能從0到255 。表示占空比。0表示占空比為0%,255表示占空比為100% 。
S4A的Arduino源代碼中定義了一個(gè)14個(gè)元素的數(shù)組,用來表示D0~D13的引腳的信息和狀態(tài):
pin arduinoPins[14]; //Array of struct holding 0-13 pins information
S4A對(duì)每一個(gè)引腳的功能都加以區(qū)分:
[原文]
Components have to be connected in a particular way. S4A allows for 6 analog inputs (analog pins), 2 digital inputs (digital pins 2 and 3), 3 analog outputs (digital pins 5, 6 and 9), 3 digital outputs (pins 10, 11 and 13) and 4 special outputs to connect Parallax continuous rotation servomotors (digital pins 4, 7, 8 and 12).
- A0-A5,6路模擬量輸入。引腳信息沒有存放在arduinoPins數(shù)組中。
- D0,D1是通訊串口的RX和TX。保留給串口使用。
- D2,D3是兩路數(shù)字開關(guān)量。可以用來接按鍵。pinType為input。
- D4,D7,D8是舵機(jī)接口。pinType為servomotors。這三個(gè)舵機(jī)接口是通過延時(shí)函數(shù)來實(shí)現(xiàn)的。即使Nucleo板上發(fā)現(xiàn)這3個(gè)接口不是pwm接口也無所謂。
- D5,D6,D9是pwm接口。pinType為pwm。使用邏輯分析儀測(cè)試,這三個(gè)pwm接口的周期都為2ms,頻率為500Hz。
- D10,D11,D12,D13是數(shù)字輸出接口。pinType為digital。可以驅(qū)動(dòng)led燈等設(shè)備。
S4A的通信協(xié)議
根據(jù)S4A的通信協(xié)議(在附件中),每隔一段時(shí)間(
20ms,在下文中會(huì)說明為什么是20ms),Arduino(或者Nucleo)應(yīng)該依次將A0~A5的模擬量,D2,D3的電平值依次上傳。共8組(一組數(shù)據(jù)由兩個(gè)字節(jié)組成)數(shù)據(jù)。每一組數(shù)據(jù)都有相應(yīng)的格式:
| 7 | 6 | 5 | 4 | 3 | 2
| 1 | 0 |
Byte1 | 1 | N | N | N | N | R | R | R |
Byte2 | 0 | R | R | R | R | R | R | R |
- 每一個(gè)字節(jié)的最高位是一個(gè)標(biāo)志位。Byte1的最高位必須為1,Byte2的最高位必須是0.這樣就可以判斷包是否正確。因?yàn)锽yte1一定是大于127的,Byte2一定是小于127的。
- N表示數(shù)據(jù)編號(hào)。Arduino上傳至S4A:A0~A5模擬量的編號(hào)是0~5,D2編號(hào)是6,D3編號(hào)為7.
- N表示數(shù)據(jù)編號(hào)。S4A下傳至Arduino:D4~D13的編號(hào)為4~13 。
- R表示需要上傳的數(shù)據(jù)。由10位組成,可以表示0-1023的最大值。這也就是為什么STM32可以達(dá)到12位AD采集,卻要設(shè)置成10位的原因了。
舉個(gè)例子:比如某一上傳時(shí)刻,采集到了A0~A5的電壓制為1023,D2為高電平,D3位低電平,那么應(yīng)該上傳:
B1_0000_111 B0_1111111 //A0,1023
B1_0001_111 B0_1111111 //A1,1023
B1_0010_111 B0_1111111 //A2,1023
B1_0011_111 B0_1111111 //A3,1023
B1_0100_111 B0_1111111 //A4,1023
B1_0101_111 B0_1111111 //A5,1023
B1_0110_111 B0_1111111 //D2,HIGH
B1_0111_000 B0_0000000 //D3,LOW
Arduino采集AD接口的信息的時(shí)候,一個(gè)AD接口采集了5次,取五次結(jié)果的中間值作為最后上傳的結(jié)果:
- for (byte p = 0; p < 5; p++)
- readings[p] = analogRead(sensorIndex);
- insertionSort(readings, 5); //sort readings
- sensorValues[sensorIndex] = readings[2]; //select median reading
復(fù)制代碼
insertionSort是插入排序,將5次結(jié)果排序,那么五次結(jié)果中的第二個(gè)一定是中間值。
索引
| 0 | 1 | 2
| 3 | 4
|
原始數(shù)據(jù)
| 1023 | 1018 | 1020 | 1019 | 1021 |
索引
| 0 | 1 | 2 | 3 | 4 |
排序之后
| 1018 | 1019 | 1020 | 1021 | 1023 |
在源代碼中,將數(shù)據(jù)打包上傳的代碼:(Serial.write發(fā)送8位無符號(hào)整數(shù))
- void ScratchBoardSensorReport(byte sensor, int value) //PicoBoard protocol, 2 bytes per sensor
- {
- Serial.write( B10000000
- | ((sensor & B1111)<<3)
- | ((value>>7) & B111));
- Serial.write( value & B1111111);
- }
復(fù)制代碼
而每隔大約75ms,S4A會(huì)將D4~D13的值傳給Arduino。因?yàn)檫@個(gè)時(shí)間很短,為了保證數(shù)據(jù)的接受完整,在實(shí)際移植的時(shí)候,應(yīng)該建立RingBuffer緩沖區(qū),采用中斷接收的方式。
[原文]Protocol
S4A interacts with Arduino by sending the actuator states and receiving sensor states every 75 ms, therefore the pulse width needs to be greater than this time period. The data exchange follows the PicoBoard protocol and needs a specific program (firmware) to be installed in the board. Please refer to the Downloads section for further instructions on how to do so.
當(dāng)Arduino接收到S4A傳來的數(shù)據(jù)之后,會(huì)對(duì)數(shù)據(jù)解碼,獲取N編號(hào)和R的值:
- pin = ((actuatorHighByte >> 3) & 0x0F);
- newVal = ((actuatorHighByte & 0x07) << 7) | (actuatorLowByte & 0x7F);
復(fù)制代碼如果R值需要更新:
- if(arduinoPins[pin].state != newVal)
- {
- arduinoPins[pin].state = newVal;
- updateActuator(pin);
- }
復(fù)制代碼而查閱updateActuator的代碼,發(fā)現(xiàn)并沒有更新舵機(jī)的代碼(
具體什么原因,會(huì)和上文提到的20ms一起解釋)。
- void updateActuator(byte pinNumber)
- {
- if (arduinoPins[pinNumber].type==digital) digitalWrite(pinNumber, arduinoPins[pinNumber].state);
- else if (arduinoPins[pinNumber].type==pwm) analogWrite(pinNumber, arduinoPins[pinNumber].state);
- }
復(fù)制代碼 20ms?
loop函數(shù)是整個(gè)調(diào)用過程:
- void loop()
- {
- static unsigned long timerCheckUpdate = millis();
- if (millis()-timerCheckUpdate>=20)
- {
- sendUpdateServomotors();
- sendSensorValues();
- timerCheckUpdate=millis();
- }
- readSerialPort();
- }
復(fù)制代碼
由于C和C++的原因,上述代碼在C中并不能成功編譯。因此我修改為:
- void loop()
- {
- static unsigned long timerCheckUpdate = 0;
- //...
- }
復(fù)制代碼
millis()是Arduino中用于獲取自CPU啟動(dòng)以來經(jīng)歷的ms數(shù)。將timerCheckUpdate改為0,只不過
第一次執(zhí)行的時(shí)候if中的語句也能夠執(zhí)行罷了。在這20ms中,更新了舵機(jī)的數(shù)值,上傳了A0~A5,D2,D3的狀態(tài)。在最后更新了timerCheckUpdate時(shí)間戳,保證下一次執(zhí)行是在20ms之后。如果這時(shí)候串口的接受緩沖區(qū)中存在數(shù)據(jù),讀出來,并解析。
先科普一下舵機(jī)的知識(shí):舵機(jī)的轉(zhuǎn)動(dòng)角度是0~180度,轉(zhuǎn)動(dòng)角度的大小是由pwm的占空比決定的。pwm的周期必須為
20ms。pwm波形中高電平的時(shí)間大約在500us~2500us之間。500us代表0度,2500us代表180度。
- void sendUpdateServomotors()
- {
- for (byte p = 0; p < 10; p++)
- if (arduinoPins[p].type == servomotor) servo(p, arduinoPins[p].state);
- }
- void servo (byte pinNumber, byte angle)
- {
- if (angle != 255)
- pulse(pinNumber, (angle * 10) + 600);
- }
- void pulse (byte pinNumber, unsigned int pulseWidth)
- {
- digitalWrite(pinNumber, HIGH);
- delayMicroseconds(pulseWidth);
- digitalWrite(pinNumber, LOW);
- }
復(fù)制代碼servo這個(gè)函數(shù)將S4A發(fā)送來的舵機(jī)的轉(zhuǎn)動(dòng)角度(0~180)轉(zhuǎn)換成為實(shí)際的高電平時(shí)間。然后通過延時(shí)來產(chǎn)生一定時(shí)間的高電平。為了保證舵機(jī)能夠正常運(yùn)轉(zhuǎn)。在移植的時(shí)候必須提供一個(gè)精度較高的delayMicroseconds函數(shù)。
S4A的Arduino函數(shù)解析就這么多,希望大家都能夠移植到自己的單片機(jī)中。
Have Fun!
S4AFirmware16.rar
(2.61 KB, 下載次數(shù): 16)
2016-6-17 16:47 上傳
點(diǎn)擊文件名下載附件
s4a-protocol.pdf
(74.33 KB, 下載次數(shù): 19)
2016-6-17 16:47 上傳
點(diǎn)擊文件名下載附件