mbedでADCで取得した値をSPIでArduinoに送る



SPI Slave
mbed
LPC1114FN28

#include "mbed.h"

SPISlave device(dp2,dp1,dp6,dp25); // mosi, miso, sclk, ssel
Serial pc(USBTX, USBRX);
AnalogIn ain(dp4);

int main()
{
    device.format(8,0);
    device.frequency(1000000);
//    uint8_t hbyte = 7;
//    uint8_t lbyte = 208;
    float fval;
    int ival;
    int counter = 0;

    while(1) {        
        if(device.receive()) {
            fval = ain.read(); // Converts and read the analog input value (value from 0.0 to 1.0)
            ival = (int) (fval * 5000); // Change the value to be in the 0 to 3300 range                       
            uint8_t hbyte = ival / 256;
            uint8_t lbyte = ival % 256;

            int val = device.read();
            pc.printf("received1: %d, ",val);
            device.reply(0xFF);

            int val2 = device.read();
            pc.printf("received2: %d, ",val2);
            device.reply(hbyte);         // Make this the next reply

            int val3 = device.read();
            pc.printf("receive3: %d, ",val3);
            pc.printf("count: %d\n",counter);
            device.reply(lbyte);         // Make this the next reply

            counter = counter+1;
        }
    }
}

master
Arduion Mega2560

#include <SPI.h>
const byte btn = 12;              // Master button digital I/O pin.
const byte led = 13;              // Master LED digital I/O pin.
const byte cmdADC = 3;
unsigned long nextMillis = 0;    // Next millis() for master LED update.
#define DTIME 1
#define PERIOD 100

void setup() {
  Serial.begin(9600);
  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(MISO, INPUT);
  pinMode(SS, OUTPUT);
  pinMode(btn, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  nextMillis = millis();
}

void loop() {
  if (millis() >= nextMillis) {
    nextMillis = millis() + PERIOD;

    digitalWrite(SS, LOW);
    delay(DTIME);
    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
    byte rx = SPI.transfer(cmdADC);
    digitalWrite(SS, HIGH);

    digitalWrite(SS, LOW);
    delay(DTIME);
    byte hbyte = SPI.transfer(255);
    digitalWrite(SS, HIGH);

    digitalWrite(SS, LOW);
    delay(DTIME);
    byte lbyte = SPI.transfer(255);
    SPI.endTransaction();
    digitalWrite(SS, HIGH);
    
    int val = (hbyte<<8) + lbyte;
    Serial.println(val);
  } 
}

データ取得用Pythonコード

import matplotlib.pyplot as plt
import re
import serial

# pattern = re.compile(r'\d+\.\d')

fout = open("out.csv","w")

ser = serial.Serial("/dev/tty.usbmodem1411",9600)
i = 0
ii = []
res = []
while (i<1001):
    snw = ser.readline().rstrip()
    # snw = pattern.findall(line)
    if snw:
        snw = float(snw)
        # plt.plot(ii,res)
        # plt.show()
        print("{},{}\n".format(i,snw))
        fout.write("{},{}\n".format(i,snw))
        i = i+1 

ser.close()

fout.close()

解析用Rコード

zerov <- read.table("0.csv",sep=",")
names(zerov) <- c("count","voltage")
threev <- read.table("3_3.csv",sep=",")
names(threev) <- c("count","voltage")
fivev <- read.table("5.csv",sep=",")
names(fivev)<-c("count","voltage","D")
D <- rep("0V",1001)
zerov <- cbind(zerov,D)
D <- rep("3.3V",1001)
threev <- cbind(threev,D)
D <- rep("5V",1001)
fivev <- cbind(fivev,D)

library(ggplot2)

df <- merge(zerov,threev,fivev,all=T)
df <- merge(df,fivev,all=T)

ggplot(df,aes(factor(D),voltage))
ggplot(df,aes(factor(D),voltage))+geom_boxplot(aes(fill=D))
ggplot(df,aes(factor(D),voltage))+geom_boxplot(aes(fill=factor(D)))

summary(zerov$voltage)
summary(threev$voltage)
summary(fivev$voltage)

ggplot(df,aes(x=voltage,fill=D))+geom_density()

10Hzで1000サンプル計測したときの結果

[0V計測時]
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
   0.00   63.00   73.00   96.13   87.00 4858.00
[3.3V計測時]
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
     84    3225    3264    3247    3279    3372
[5V計測時]
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max.
   4696    4824    4848    4838    4863    4951

測定値に誤差があるのでカルマンフィルタを実装

#include <SPI.h>
#include <math.h>
const byte btn = 12;              // Master button digital I/O pin.
const byte led = 13;              // Master LED digital I/O pin.
const byte cmdADC = 3;
unsigned long nextMillis = 0;    // Next millis() for master LED update.
#define DTIME 1
#define PERIOD 100

float x_hat;
float x_hat_bar;
float sigmax_hat;
float sigmax_hat_bar;
float sigmax0 = 37.0;//from experiment
float sigmaz = 50.0;//given parameter
bool isFirst = true;
float k;
float kalmanFilter(float z){
	//initialize
	if(isFirst){
	  x_hat = float(z);
	  sigmax_hat = sigmax0;
	  isFirst = false;    
    }
    //estimate step
    x_hat_bar = x_hat;
    sigmax_hat_bar = sqrt(sigmax_hat*sigmax_hat + sigmax0*sigmax0);
    //filter step
    k = (sigmax_hat_bar*sigmax_hat_bar) / ((sigmax_hat_bar*sigmax_hat_bar) + (sigmaz*sigmaz));
    x_hat = x_hat_bar + k * (z - x_hat_bar);
    sigmax_hat = (1-k) * sigmax_hat_bar;
    return x_hat;
}

void setup() {
  Serial.begin(9600);
  pinMode(SCK, OUTPUT);
  pinMode(MOSI, OUTPUT);
  pinMode(MISO, INPUT);
  pinMode(SS, OUTPUT);
  pinMode(btn, INPUT_PULLUP);
  pinMode(led, OUTPUT);
  nextMillis = millis();
}

void loop() {
  if (millis() >= nextMillis) {
    nextMillis = millis() + PERIOD;

    digitalWrite(SS, LOW);
    delay(DTIME);
    SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
    byte rx = SPI.transfer(cmdADC);
    digitalWrite(SS, HIGH);

    digitalWrite(SS, LOW);
    delay(DTIME);
    byte hbyte = SPI.transfer(255);
    digitalWrite(SS, HIGH);

    digitalWrite(SS, LOW);
    delay(DTIME);
    byte lbyte = SPI.transfer(255);
    SPI.endTransaction();
    digitalWrite(SS, HIGH);
    
    int val = (hbyte<<8) + lbyte;
    Serial.print(val);
    Serial.print(",");
    
    float filtered = kalmanFilter(float(val));
    Serial.println(filtered);
  } 
}