You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
*/
-using namespace std;
-#include <vector>
+#include "Adc.h"
#include "libs/nuts_bolts.h"
-#include "libs/Module.h"
#include "libs/Kernel.h"
-#include "Adc.h"
+#include "libs/Pin.h"
#include "libs/ADC/adc.h"
#include "libs/Pin.h"
+#include "libs/Median.h"
+
+#include <cstring>
+#include <algorithm>
+
+#include "mbed.h"
// This is an interface to the mbed.org ADC library you can find in libs/ADC/adc.h
// TODO : Having the same name is confusing, should change that
-Adc::Adc(){
- this->adc = new ADC(1000, 1);
+Adc *Adc::instance;
+
+static void sample_isr(int chan, uint32_t value)
+{
+ Adc::instance->new_sample(chan, value);
}
+Adc::Adc()
+{
+ instance = this;
+ // ADC sample rate need to be fast enough to be able to read the enabled channels within the thermistor poll time
+ // even though ther maybe 32 samples we only need one new one within the polling time
+ const uint32_t sample_rate= 1000; // 1KHz sample rate
+ this->adc = new mbed::ADC(sample_rate, 8);
+ this->adc->append(sample_isr);
+}
+
+/*
+LPC176x ADC channels and pins
+
+Adc Channel Port Pin Pin Functions Associated PINSEL Register
+AD0 P0.23 0-GPIO, 1-AD0[0], 2-I2SRX_CLK, 3-CAP3[0] 14,15 bits of PINSEL1
+AD1 P0.24 0-GPIO, 1-AD0[1], 2-I2SRX_WS, 3-CAP3[1] 16,17 bits of PINSEL1
+AD2 P0.25 0-GPIO, 1-AD0[2], 2-I2SRX_SDA, 3-TXD3 18,19 bits of PINSEL1
+AD3 P0.26 0-GPIO, 1-AD0[3], 2-AOUT, 3-RXD3 20,21 bits of PINSEL1
+AD4 P1.30 0-GPIO, 1-VBUS, 2- , 3-AD0[4] 28,29 bits of PINSEL3
+AD5 P1.31 0-GPIO, 1-SCK1, 2- , 3-AD0[5] 30,31 bits of PINSEL3
+AD6 P0.3 0-GPIO, 1-RXD0, 2-AD0[6], 3- 6,7 bits of PINSEL0
+AD7 P0.2 0-GPIO, 1-TXD0, 2-AD0[7], 3- 4,5 bits of PINSEL0
+*/
+
// Enables ADC on a given pin
-void Adc::enable_pin(Pin* pin){
+void Adc::enable_pin(Pin *pin)
+{
PinName pin_name = this->_pin_to_pinname(pin);
+ int channel = adc->_pin_to_channel(pin_name);
+ memset(sample_buffers[channel], 0, sizeof(sample_buffers[0]));
+
this->adc->burst(1);
- this->adc->setup(pin_name,1);
- this->adc->interrupt_state(pin_name,1);
+ this->adc->setup(pin_name, 1);
+ this->adc->interrupt_state(pin_name, 1);
+}
+
+// Keeps the last 8 values for each channel
+// This is called in an ISR, so sample_buffers needs to be accessed atomically
+void Adc::new_sample(int chan, uint32_t value)
+{
+ // Shuffle down and add new value to the end
+ if(chan < num_channels) {
+ memmove(&sample_buffers[chan][0], &sample_buffers[chan][1], sizeof(sample_buffers[0]) - sizeof(sample_buffers[0][0]));
+ sample_buffers[chan][num_samples - 1] = (value >> 4) & 0xFFF; // the 12 bit ADC reading
+ }
}
-// Read the last value ( burst mode ) on a given pin
-unsigned int Adc::read(Pin* pin){
- return this->adc->read(this->_pin_to_pinname(pin));
+//#define USE_MEDIAN_FILTER
+// Read the filtered value ( burst mode ) on a given pin
+unsigned int Adc::read(Pin *pin)
+{
+ PinName p = this->_pin_to_pinname(pin);
+ int channel = adc->_pin_to_channel(p);
+
+ uint16_t median_buffer[num_samples];
+ // needs atomic access TODO maybe be able to use std::atomic here or some lockless mutex
+ __disable_irq();
+ memcpy(median_buffer, sample_buffers[channel], sizeof(median_buffer));
+ __enable_irq();
+
+#ifdef USE_MEDIAN_FILTER
+ // returns the median value of the last 8 samples
+ return median_buffer[quick_median(median_buffer, num_samples)];
+
+#elif defined(OVERSAMPLE)
+ // Oversample to get 2 extra bits of resolution
+ // weed out top and bottom worst values then oversample the rest
+ // put into a 4 element moving average and return the average of the last 4 oversampled readings
+ static uint16_t ave_buf[num_channels][4] = { {0} };
+ std::sort(median_buffer, median_buffer + num_samples);
+ uint32_t sum = 0;
+ for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) {
+ sum += median_buffer[i];
+ }
+ // this slows down the rate of change a little bit
+ ave_buf[channel][3]= ave_buf[channel][2];
+ ave_buf[channel][2]= ave_buf[channel][1];
+ ave_buf[channel][1]= ave_buf[channel][0];
+ ave_buf[channel][0]= sum >> OVERSAMPLE;
+ return roundf((ave_buf[channel][0]+ave_buf[channel][1]+ave_buf[channel][2]+ave_buf[channel][3])/4.0F);
+
+#else
+ // sort the 8 readings and return the average of the middle 4
+ std::sort(median_buffer, median_buffer + num_samples);
+ int sum = 0;
+ for (int i = num_samples / 4; i < (num_samples - (num_samples / 4)); ++i) {
+ sum += median_buffer[i];
+ }
+ return sum / (num_samples / 2);
+
+#endif
}
// Convert a smoothie Pin into a mBed Pin
-PinName Adc::_pin_to_pinname(Pin* pin){
- if( pin->port == LPC_GPIO0 && pin->pin == 23 ){
+PinName Adc::_pin_to_pinname(Pin *pin)
+{
+ if( pin->port == LPC_GPIO0 && pin->pin == 23 ) {
return p15;
- }else if( pin->port == LPC_GPIO0 && pin->pin == 24 ){
+ } else if( pin->port == LPC_GPIO0 && pin->pin == 24 ) {
return p16;
- }else if( pin->port == LPC_GPIO0 && pin->pin == 25 ){
+ } else if( pin->port == LPC_GPIO0 && pin->pin == 25 ) {
return p17;
- }else if( pin->port == LPC_GPIO0 && pin->pin == 26 ){
+ } else if( pin->port == LPC_GPIO0 && pin->pin == 26 ) {
return p18;
- }else if( pin->port == LPC_GPIO1 && pin->pin == 30 ){
+ } else if( pin->port == LPC_GPIO1 && pin->pin == 30 ) {
return p19;
- }else if( pin->port == LPC_GPIO1 && pin->pin == 31 ){
+ } else if( pin->port == LPC_GPIO1 && pin->pin == 31 ) {
return p20;
- }else{
+ } else {
//TODO: Error
return NC;
}