Arduino выполняет все вычисления, но код написан таким образом, что требуется подключение к компьютеру. Левитация начнет работать, когда Arduino получает 32 байта с компьютера. Эти 32 байт должены содержать 16 целых и записываются в массив AP.
Процедура левитации прекращается, когда байт 0 получен от Arduino.
Скетч для arduino
Для достижения высокой производительности автор использует вставки на ассемблере
PHP код:
// clear bit and set bit #ifndef cbi #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) #endif #ifndef sbi #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) #endif
// intRes = intIn1 * intIn2 >> 16 + rounding // rounding adds 1 to the result if the MSB of the byte 1 is set // uses: // r26 to store 0 // r27 to store the byte 1 of the 32bit result // 21 cycles #define MultiU16X16toH16Round(intRes, intIn1, intIn2) \ asm volatile ( \ "clr r26 \n\t" \ "mul %A1, %A2 \n\t" \ "mov r27, r1 \n\t" \ "mul %B1, %B2 \n\t" \ "movw %A0, r0 \n\t" \ "mul %B2, %A1 \n\t" \ "add r27, r0 \n\t" \ "adc %A0, r1 \n\t" \ "adc %B0, r26 \n\t" \ "mul %B1, %A2 \n\t" \ "add r27, r0 \n\t" \ "adc %A0, r1 \n\t" \ "adc %B0, r26 \n\t" \ "lsl r27 \n\t" \ "adc %A0, r26 \n\t" \ "adc %B0, r26 \n\t" \ "clr r1 \n\t" \ : \ "=&r" (intRes) \ : \ "a" (intIn1), \ "a" (intIn2) \ : \ "r26" , "r27" \ )
// waits until another conversion is finished by waiting for // the interrupt flag to be set // reads the 10bit result and resets the interrupt flag unsigned int analogNext() { while (!(ADCSRA & _BV(ADIF))) ;
#define SAMPLES 4096 unsigned int baseline = 0; unsigned int coilPower = 0;
void setup() {
// open serial connection, 1,000,000 baud beginSerial(1000000L);
// setup 19230.8Hz sampling rate // setup Vcc as ref, right aligned output analogSetup(6, 1, 0);
// free running mode ADCSRB = ADCSRB & 0xf8;
// setup pin 3 as output pinMode(3,OUTPUT);
// enable autotrigger sbi(ADCSRA, ADATE);
// start conversion from pin 0 analogStart(0);
// calibration unsigned long gather;
// turn the coil off for baseline digitalWriteD(3, 0); // wait for some time delay(200); // gather info gather = 0; for (int i = 0; i < SAMPLES; i ++) { // wait for the next value and read it gather += analogNext(); } baseline = gather / SAMPLES;
// turn the coil on for coilPower digitalWriteD(3, 1); // wait for some time delay(200); // gather info gather = 0; for (int i = 0; i < SAMPLES; i ++) { // wait for the next value and read it gather += analogNext(); } coilPower = gather / SAMPLES - baseline + 1; coilPower *= 2; // this is to compensate for the filter resolution 0 ... 32767
// turn the coil off digitalWriteD(3, 0);
// disable Timer0 for function millis() to // avoid interference disableMillis();
unsigned char signal = 0; unsigned char action = 0; unsigned int filter = 0; unsigned int oldfilter = 0;
while (true) { // read commands if (!action) { if (serialAvailable() >= HEADERSIZE) { char* a = (char*) ap; for (char i = 0; i < HEADERSIZE; i ++) { char c = serialRead(); *a = c; a++; } action = true;
filter = 0; oldfilter = 0; signal = 0;
for (char i = 0; i < HEADERSIZE; i ++) { serialWrite(i); } }
} else { if (serialAvailable() > 0) { unsigned char c = serialRead(); switch (c) { case 0: action = false; digitalWriteD(3,0); break; case 1: ap[4] ++; break; case 2: ap[4] --; break; case 3: ap[5] ++; break; case 4: ap[5] --; break; } } }
if (action) { // wait for the next value and read it unsigned int field = analogNext(); // write previously computed signal digitalWriteD(3, signal);
// do the computations
unsigned int result; // mag = field - coilPower * oldFilter >> 16 - baseline; MultiU16X16toH16Round(result, coilPower, oldfilter); int mag = field - result - baseline;
if (power < 0) power = 0; if (power > 255) power = 255; // compare with the actual coil field if (power > (filter >> 7)) { // need to energize signal = 1; } else { // need to deenergize signal = 0; }
// emergency shutdown // magnet is too far, turn off the coil if (mag < 8) signal = 0;
// test if (ap[15] !=0) { counter += ap[15]; if (counter > 0) { signal = 0; } else { signal = 1; } }