|
不知道大家有沒(méi)有聽(tīng)說(shuō)的Scratch: https://scratch.mit.edu/ 是由麻省理工開(kāi)發(fā)的一個(gè)軟件,幫助孩子們從零開(kāi)始學(xué)習(xí)編程。圖形化的程序設(shè)計(jì),萌萌噠的界面,想必能夠吸引住眾多孩子。不過(guò)這么好的軟件貌似在國(guó)內(nèi)知名度不高。
后來(lái),一個(gè)團(tuán)隊(duì)開(kāi)發(fā)了一個(gè)名為S4A(Scartch for Arduino)的軟件:http://s4a.cat/ 將Scartch與Arduino連接起來(lái)。結(jié)合了Scartch的編程優(yōu)點(diǎn),外加Arduino的眾多傳感器,一時(shí)間孩子們也能夠玩轉(zhuǎn)舵機(jī),開(kāi)關(guān),以及模擬量的傳感器了。S4A使用了Arduino的6路AD采集接口,3路PWM接口和3路舵機(jī)接口,方便小孩子連接傳感器,實(shí)現(xiàn)自己的設(shè)計(jì)。
后來(lái),我覺(jué)得S4A這個(gè)玩意挺好玩的,看了一下桌子上的Nucleo,決定來(lái)移植一下。經(jīng)過(guò)大約3天的緊張工作,終于成功完成了移植。期間出了一個(gè)小小的問(wèn)題耗費(fèi)了大約一天的時(shí)間。也就是Nucleo和S4A通訊的時(shí)候,S4A總是崩潰,一直未響應(yīng)。不知道是什么原因,期間一直在改動(dòng)自己的代碼。一天無(wú)果。第二天無(wú)意間換了FT232作為串口通訊的工具。竟然奇跡般的好了。事后發(fā)現(xiàn),只要是Nucleo的串口,不出幾分鐘,S4A必然崩潰。不知道是Nucleo串口的原因還是S4A的原因。
在移植完之后,我簡(jiǎn)單的用S4A寫了一個(gè)呼吸燈的程序,突然發(fā)現(xiàn),用C很容易實(shí)現(xiàn)的東西,讓我來(lái)拖拽的時(shí)候竟然不知道如何去做了。。。
下面是程序截圖:
最后,給大家上傳一小段視頻看一下(由于社區(qū)限制,只能將視頻分成兩個(gè)壓縮包上傳),感興趣點(diǎn)個(gè)贊,不感興趣路過(guò)好了。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
Nucleo334的軟件包以上傳到社區(qū)。歡迎大家下載試用。
發(fā)帖的時(shí)候占用了3層樓。對(duì)這幾層的規(guī)劃:
- 簡(jiǎn)單的介紹一下S4A軟件如何使用。帶領(lǐng)大家拖拽一個(gè)小程序。
- 全面介紹一下如何將S4A在Arduino上的軟件移植到STM的Nucleo板上。
- 暫時(shí)還沒(méi)想好能干點(diǎn)什么。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
軟件包已經(jīng)上傳,名為S4Nu.rar(Scartch for Nucleo)歡迎大家提前下載使用。另外需要特別注意的是:雖然Nucleo自帶了串口,但是在與S4A連接的時(shí)候不要使用這個(gè)串口。也不要使用USB連線將Nucleo與電腦相連。
應(yīng)該對(duì)Nucleo使用外部供電。并使用USB轉(zhuǎn)串口工具,例如CP2102,F(xiàn)T232等連接電腦與Nucleo的串口。具體是什么原因暫時(shí)還未知?赡茉诮酉聛(lái)的幾天內(nèi)能夠解決這個(gè)問(wèn)題,也可能解決不掉。下面就要說(shuō)說(shuō)如何設(shè)置Nucleo的硬件部分:
- 將Nucleo復(fù)位按鍵(板子上面的那個(gè)黑色開(kāi)關(guān))下面的短路帽由U5V改成E5V。這樣能切換到外部電源供電。
- 將Nucleo背面的焊橋SB62和SB63短接。這樣可以使PA2和PA3引腳連到Arduino接口的D0(Rx)和D1(Tx)。如果你懶得焊接,也可以使用CN3排針(STLINK)部分。CN3標(biāo)記的RX接的是Nucleo的TX,而CN3的TX接的是Nucleo的RX。
- 使用外部電源給Nucleo供電。外部電源5V接到Morpho左邊排針的E5V,將外部電源GND接到Nucleo的GND。
- 將你使用的串口工具的TX接D0或者CN3的TX,串口工具的RX接D1或者CN3的RX,串口工具的GND接Nucleo的GND,并將串口工具連接到電腦。
- 打開(kāi)S4A軟件,S4A會(huì)自動(dòng)搜索電腦上的串口,判斷包格式。如果包正確,那么可以順利連接。
- 順利連接之后可以看到S4A軟件上顯示的6路AD量和兩路數(shù)字量輸入。
- 2015.6.9 更新2樓,介紹S4A如何使用。
- 2015.6.10 更新3樓,介紹S4A Arduino源代碼。
因通訊的問(wèn)題,給老外的技術(shù)支持發(fā)了一封郵件,幾天后,收到了一個(gè)回復(fù),大概意思說(shuō)是他們的S4A只是為Arduino Uno和Duemienova定做的,并且在今后的升級(jí)中,會(huì)考慮兼容更多的板子。本以為這件事就這樣算了。
沒(méi)想到,另一個(gè)人,猜測(cè)大概是S4A的開(kāi)發(fā)人員,給回復(fù)了一封郵件。意思是他正在考慮讓S4A在LPC的ARM處理器上工作。讓我發(fā)送一下我的代碼,說(shuō)不定他可以檢查到什么錯(cuò)誤。好消息,給你吧。
在發(fā)送完STM32的代碼之后,靜等了幾天,昨晚收到了郵件回復(fù):也沒(méi)看出是什么軟件的問(wèn)題,大概是USB轉(zhuǎn)串口的問(wèn)題吧。
S4Nu.rar
(2.49 MB, 下載次數(shù): 21)
2016-6-17 16:41 上傳
點(diǎn)擊文件名下載附件
視頻.rar
(14.65 MB, 下載次數(shù): 13)
2016-6-17 16:41 上傳
點(diǎn)擊文件名下載附件
----------------------------偶是分隔線----------------------------------------------------
Scartch的簡(jiǎn)單編程
如果你代碼下載完畢,串口連接完成,打開(kāi)S4A之后,就可以看到6路AD模擬量和兩個(gè)開(kāi)關(guān)的數(shù)字量的值了。圖中的COMx是你實(shí)際使用的串口。在我的電腦上,這個(gè)COM3是使用FT232轉(zhuǎn)換出來(lái)的。還要注意圖片中的旗幟的標(biāo)志和那個(gè)紅色的大圓點(diǎn)。旗幟代表運(yùn)行我們使用S4A拖拽的程序,圓點(diǎn)代表停止。
在S4A的左上角可以看到動(dòng)作,控制,外觀,等等控件。里面有各種各樣的工具供我們使用。
- 動(dòng)作一欄含有數(shù)字引腳的輸出,舵機(jī)控制,pwm輸出等。是對(duì)硬件的實(shí)際控制。
- 控制一欄中是常見(jiàn)的循環(huán),if else分支語(yǔ)句,延時(shí)等函數(shù)。
- 數(shù)字和邏輯運(yùn)算里面含有常用的大于小于判斷,加減乘除等語(yǔ)法。
- 變量一欄中可以設(shè)置一個(gè)自己的變量,用于存儲(chǔ)AD的采集值,pwm的輸出值等等。更奇葩的是,還有鏈表這個(gè)東西。一直搞不清楚對(duì)小孩子而言,鏈表可以干什么。
腳本這個(gè)位置就是我們寫程序的地方。只要將程序按照邏輯結(jié)構(gòu)放在這里就可以。一定要注意的是,開(kāi)始的語(yǔ)句是一個(gè)“當(dāng)旗幟被點(diǎn)擊”的語(yǔ)句。這條語(yǔ)句類似于C語(yǔ)言中的main函數(shù),程序開(kāi)始執(zhí)行的地方。下面就來(lái)簡(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ù)值等等?墒,為什么沒(méi)有減小變量的數(shù)值呢?其實(shí)只要將“將變量delayTime的值增加[]”中的空格處填寫一個(gè)負(fù)數(shù)就可以了。比如-10 ?墒切『⒆尤绻恢镭(fù)數(shù)怎么辦呢?
在程序?qū)懲曛,點(diǎn)擊右上角圓點(diǎn)旁邊的旗幟圖標(biāo)就可以運(yùn)行了。
不知道你家孩子會(huì)不會(huì)喜歡上!
------------偶還是分隔線------------------------
S4A Arduino源代碼解析
準(zhǔn)備工具:
- Arduino IDE(如果沒(méi)有,可以使用文本編輯工具代替。比如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庫(kù)文件中的GPIO_WriteBit。
- digitalRead是讀取引腳的電平值。類似于STM32庫(kù)文件中的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語(yǔ)言中確不能。不知道是不是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ù)組,用來(lái)表示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路模擬量輸入。引腳信息沒(méi)有存放在arduinoPins數(shù)組中。
- D0,D1是通訊串口的RX和TX。保留給串口使用。
- D2,D3是兩路數(shù)字開(kāi)關(guān)量。可以用來(lái)接按鍵。pinType為input。
- D4,D7,D8是舵機(jī)接口。pinType為servomotors。這三個(gè)舵機(jī)接口是通過(guò)延時(shí)函數(shù)來(lái)實(shí)現(xiàn)的。即使Nucleo板上發(fā)現(xiàn)這3個(gè)接口不是pwm接口也無(wú)所謂。
- 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ì)說(shuō)明為什么是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位無(wú)符號(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傳來(lái)的數(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)并沒(méi)有更新舵機(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)用過(guò)程:
- 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)以來(lái)經(jīng)歷的ms數(shù)。將timerCheckUpdate改為0,只不過(guò)第一次執(zhí)行的時(shí)候if中的語(yǔ)句也能夠執(zhí)行罷了。在這20ms中,更新了舵機(jī)的數(shù)值,上傳了A0~A5,D2,D3的狀態(tài)。在最后更新了timerCheckUpdate時(shí)間戳,保證下一次執(zhí)行是在20ms之后。如果這時(shí)候串口的接受緩沖區(qū)中存在數(shù)據(jù),讀出來(lái),并解析。
先科普一下舵機(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ā)送來(lái)的舵機(jī)的轉(zhuǎn)動(dòng)角度(0~180)轉(zhuǎn)換成為實(shí)際的高電平時(shí)間。然后通過(guò)延時(shí)來(lái)產(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)擊文件名下載附件
|
|