control relays using 74hc595(shiftout) - arduino

I want to use 7h4c595(8 IOs) to control 8 relays.
I tried using 0b00000000, It's working fine.
but I don't know how to concat switches values into this kind of binary.
almost 0 knowledge about this. sorry
I know the code below is not right, but it seems working.
problem: 74hc595's q0 is controling the 2nd relay, not the 1st.
and q1 is controling 3rd relay.
It should be like q0->1st, q1-> 2nd, and so on.
sorry for bothering you.
[code]
uint8_t switch0=0;// 0 = off
uint8_t switch1=1;// 1 = on
etc...
uint8_t switch7=1;//1-7 on
setup(){
etc...
}
loop(){
if(digitalWrite(btn1)==HIGH){
switch0=1;//on
switch1=0;//off
etc...//1-7 off
}
//unit8_t sw=0b10000000; //turn 1st relay on when btn1 pressed
uint8_t sw={switch0,switch1,....,switch7};
for(int i=0;i<8;i++){
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, i);
digitalWrite(latchPin, HIGH);
}
}

I'd go in similar way how are the pins defined in avr/io.h
constexpr uint8_t RELAY0 = 0;
constexpr uint8_t RELAY1 = 1;
// ...
constexpr uint8_t RELAY7 = 7;
loop() {
uint8_t data = (switch0 << RELAY0) | (switch1 << RELAY1) | /* ... |*/ (switch7 << RELAY7);
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, data);
digitalWrite(latchPin, HIGH);
delay(200); // or more
}
Or you can use single byte (uint8_t) for storing all switches at once. As a bonus you can send it over shiftOut directly.
uint8_t allSwitches = 0; // all relays disabled
// turning relay x on (somewhere inside of function):
allSwitches |= _BV(x); // where x is number between 0 and 7 including
// turning relay x off:
allSwitches &= ~_BV(x);
// but you can set some of them and reset others in single step:
allSwitches = _BV(0) | _BV(5) | _BV(7); // turns on relay 0, 5 and 7, rest will be turned off

Related

Received data are correct on scope not in program

I use Arduino shiftIn instruction to receive data from a MLX90316.
It uses one wire for MOSI an MISO.
Data consists of 4 bytes (2+2)
First 2 bytes are an angle value. Second 2 bytes ares the reversed value of angle (1st complement)
First 2 bytes are correct on scope and program (0x00 and 0x22)
Second 2 bytes are correct on scope (0xFF and 0XDD), but are frequently (not always) wrong in program (0xFF and 0xDC or 0xFF and 0xCC...)
In fact only last byte is sometimes wrong when I print it.
I have tried to change Serial speed to 9600 bps (same result)
I have tried to change delayMicroseconds value before last shiftIn (same result)
/* MLX90316 Rotary Position Sensor */
int readAngle();
int pinSS = 10; // Green Wire
int pinDATA = 11; // Yellow Wire
int pinSCK = 13; // Grey Wire
unsigned int r1=0;
unsigned int r2=0;
unsigned int r3=0;
unsigned int r4=0;
int angle;
void setup(){
delayMicroseconds(16000); // 16ms slave start-up
pinMode(pinSS,OUTPUT); // Pin Slave Select
pinMode(pinSCK, OUTPUT); // Pin Clock
digitalWrite(pinSS, HIGH); // de-select chip
Serial.begin(115200);
Serial.println("MLX90316 Rotary Position Sensor");
}
void loop() {
angle = readAngle();
Serial.println("");
Serial.print("r1=");Serial.println(r1,HEX);
Serial.print("r2=");Serial.println(r2,HEX);
Serial.print("r3=");Serial.println(r3,HEX);
Serial.print("r4=");Serial.println(r4,HEX);
delay(10000);
}
int readAngle() {
// Start with clock LOW
digitalWrite(pinSCK, LOW);
// Data pin in write mode
pinMode(pinDATA, OUTPUT);
// take the SS pin low to select the chip:
digitalWrite(pinSS, LOW);
delayMicroseconds(30);
// Send START bytes
shiftOut(pinDATA, pinSCK, MSBFIRST, 0xAA);
delayMicroseconds(25);
shiftOut(pinDATA, pinSCK, MSBFIRST, 0xFF);
delayMicroseconds(30); // 30 us between START bytes and DATA
// Data pin in read mode
pinMode(pinDATA, INPUT_PULLUP);
// Receive data
r1 = shiftIn(pinDATA, pinSCK, MSBFIRST);
delayMicroseconds(25);
r2 = shiftIn(pinDATA, pinSCK, MSBFIRST);
delayMicroseconds(25);
r3 = shiftIn(pinDATA, pinSCK, MSBFIRST);
delayMicroseconds(30);
r4 = shiftIn(pinDATA, pinSCK, MSBFIRST);
// take the SS pin high to de-select the chip:
delayMicroseconds(5);
digitalWrite(pinSS, HIGH);
}
I expect the output of 0x00 0x22 0xFF 0xDD
I don't know which Arduino you are using but the shift in and out functions are very simple software implementations.
uint8_t shiftIn(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
if (bitOrder == LSBFIRST)
value |= digitalRead(dataPin) << i;
else
value |= digitalRead(dataPin) << (7 - i);
digitalWrite(clockPin, LOW);
}
return value;
}
The MLX90316 allows only a clock speed of 145kHz, so this direct port access could be too fast and the data is not ready when you read it.
But since you have a scope available you could simply check that timing and write your own shiftIn if necessary.
I think this "version" should work for your chip (untested)
uint8_t shiftIn2(uint8_t dataPin, uint8_t clockPin) {
uint8_t value = 0;
uint8_t i;
for (i = 0; i < 8; ++i) {
digitalWrite(clockPin, HIGH);
delayMicroseconds(4);
digitalWrite(clockPin, LOW);
value |= digitalRead(dataPin) << (7 - i);
delayMicroseconds(4);
}
return value;
}

How do I have this audio and LED activate ONLY when a button is held using an Arduino?

I have this code that I am using to play a sound effect where I used a program called wav2c to convert a .wav file to number values that I put into a header file that I use in the code to generate the sound. I currently have it programmed to play the audio upon uploading it to the Arduino with an LED being activated along with it and staying lit for just the duration of the sound effect. I am trying to program it so that the sound and LED only activate when I am pressing a button. I have the pin the button is plugged into already programmed in but I'm not sure how to have it control the audio and LED as stated above. I don't have much experience with programming or Arduino so any help is much appreciated! I am using an Arduino Mega 2560.
The code
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define SAMPLE_RATE 20000
#include "Test.h"
int ledPin = 2;
int speakerPin = 9; // Can be either 3 or 11, two PWM outputs connected to Timer 2
const byte pinSwitch1 = 3;
volatile uint16_t sample;
byte lastSample;
void stopPlayback()
{
digitalWrite(ledPin, LOW);
// Disable playback per-sample interrupt.
TIMSK1 &= ~_BV(OCIE1A);
// Disable the per-sample timer completely.
TCCR1B &= ~_BV(CS10);
// Disable the PWM timer.
TCCR2B &= ~_BV(CS10);
digitalWrite(speakerPin, LOW);
}
// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
if (sample >= sounddata_length) {
if (sample == sounddata_length + lastSample) {
stopPlayback();
}
else {
if(speakerPin==11){
// Ramp down to zero to reduce the click at the end of playback.
OCR2A = sounddata_length + lastSample - sample;
} else {
OCR2B = sounddata_length + lastSample - sample;
}
}
}
else {
if(speakerPin==11){
OCR2A = pgm_read_byte(&sounddata_data[sample]);
} else {
OCR2B = pgm_read_byte(&sounddata_data[sample]);
}
}
++sample;
}
void startPlayback()
{
pinMode(speakerPin, OUTPUT);
// Set up Timer 2 to do pulse width modulation on the speaker
// pin.
// Use internal clock (datasheet p.160)
ASSR &= ~(_BV(EXCLK) | _BV(AS2));
// Set fast PWM mode (p.157)
TCCR2A |= _BV(WGM21) | _BV(WGM20);
TCCR2B &= ~_BV(WGM22);
if(speakerPin==11){
// Do non-inverting PWM on pin OC2A (p.155)
// On the Arduino this is pin 11.
TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
OCR2A = pgm_read_byte(&sounddata_data[0]);
} else {
// Do non-inverting PWM on pin OC2B (p.155)
// On the Arduino this is pin 3.
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0);
TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
OCR2B = pgm_read_byte(&sounddata_data[0]);
}
// Set up Timer 1 to send a sample every interrupt.
cli();
// Set CTC mode (Clear Timer on Compare Match) (p.133)
// Have to set OCR1A *after*, otherwise it gets reset to 0!
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
// No prescaler (p.134)
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set the compare register (OCR1A).
// OCR1A is a 16-bit register, so we have to do this with
// interrupts disabled to be safe.
OCR1A = F_CPU / SAMPLE_RATE; // 16e6 / 8000 = 2000
// Enable interrupt when TCNT1 == OCR1A (p.136)
TIMSK1 |= _BV(OCIE1A);
lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
sample = 0;
sei();
}
void setup()
{
pinMode( pinSwitch1, INPUT );
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
startPlayback();
}
void loop()
{
while (true);
}
The header file referenced in the code with the numeric values to create the audio.
#ifndef _HEADERFILE_H // Put these two lines at the top of your file.
#define _HEADERFILE_H // (Use a suitable name, usually based on the file name.)
const int sounddata_length=32000;
//const int sounddata_sampleRate=20000;
const unsigned char sounddata_data[] PROGMEM = {
15,1,49,0,150,0,138,0,219,255,133,0,176,0,15,1,210,
//There are many lines of more numbers in between that I cut out to save space
};
#endif // _HEADERFILE_H // Put this line at the end of your file.
The following changes will allow you to start playback whenever there is a falling edge on your switch pin. You may need to tweak to avoid switch 'bouncing'.
Firstly, add a global variable to record the last switch state:
int lastSwitchState;
Change your setup() to
void setup() {
pinMode(pinSwitch1, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
lastSwitchState = digitalRead(pinSwitch1);
}
and your loop() function to
void loop() {
delay(50);
int switchState = digitalRead(pinSwitch1);
if (switchState != lastSwitchState) {
lastSwitchState = switchState;
if (switchState == LOW) {
startPlayback();
}
}
}
Interrupts vs polling
Instead of polling the switch pin inside the main loop(), you could use interrupts. You would use attachInterrupt() to do this. Interrupts are only available on certain pins, however, and the above approach is conceptually simpler I think.

microphone sph0645 with I2S less sensitive after watchdog sleep with Adafruit Feather M0

I am using the Adafruit Feather M0 RFM69 with the Adafruit I2S MEMS Microphone Breakout SPH0645. Every second I take a reading (sampleRate = 16000, bits per sample = 32) using the I2S library and send it over the radio. This all works fine.
My problem is that, when I want to save power, I am getting weird readings after I wake the board from sleep (using Adafruit_SleepyDog library). The microphone somewhat still works, although it is much less sensitive, only picks up loud sounds and also returns 60dB in a quiet room. When I don't put it to sleep, in the same sound setting, I get 40dB. However, if I put a delay of 250ms after waking up, the microphone works fine again, like before, but this is obviously not saving energy then.
I wonder why this is happening. Is there something I can do to get the microphone to work quicker? I checked the datasheet, but it only says: "When Vdd is applied the microphone senses the
CLOCK line, if the frequency is greater than 900KHz, the microphone enters the normal mode of operation." This should not even take a few ms though?
Thanks in advance
#include <I2S.h>
#include <Adafruit_SleepyDog.h>
#include <SPI.h>
#include <RH_RF69.h>
/************ Radio Setup ***************/
#define RF69_FREQ 433.0
#define SLEEP
//#if defined(ARDUINO_SAMD_FEATHER_M0) // Feather M0 w/Radio
#define RFM69_CS 8
#define RFM69_INT 3
#define RFM69_RST 4
#define LED 13
//#endif
// radio
// Singleton instance of the radio driver
RH_RF69 rf69(RFM69_CS, RFM69_INT);
int transmit_interval = 1000;
int time_counter = 0;
int packetnum = 0;
// MIC
#define SAMPLES 1024//2048 // make it a power of two for best DMA performance
int samples[SAMPLES];
int measurementsdB = 0;
int current_measure;
#define ADC_SOUND_REF 65
#define DB_SOUND_REF 41
int sampleRate1 = 16000;
int bitsPerSample1 = 32;
typedef struct
{
uint8_t measurementdB = 123;
uint8_t battery = 111;
uint8_t test = 222;
} RadioMessage;
RadioMessage struct_message;
void setup()
{
delay(2000); // Wait so its easier to program
Serial.begin(115200);
//while (!Serial) { delay(1); } // wait until serial console is open, remove if not tethered to computer
// Init Mic
if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate1, bitsPerSample1)) {
while (1); // do nothing
}
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
pinMode(RFM69_RST, OUTPUT);
digitalWrite(RFM69_RST, LOW);
Serial.println("Feather RFM69 TX Test!");
Serial.println();
// manual reset
digitalWrite(RFM69_RST, HIGH);
delay(10);
digitalWrite(RFM69_RST, LOW);
delay(10);
if (!rf69.init()) {
Serial.println("RFM69 radio init failed");
while (1);
}
Serial.println("RFM69 radio init OK!");
// Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module)
// No encryption
if (!rf69.setFrequency(RF69_FREQ)) {
Serial.println("setFrequency failed");
}
// If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the
// ishighpowermodule flag set like this:
rf69.setTxPower(20, true); // range from 14-20 for power, 2nd arg must be true for 69HCW
// The encryption key has to be the same as the one in the server
uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
rf69.setEncryptionKey(key);
Serial.print("RFM69 radio #"); Serial.print((int)RF69_FREQ); Serial.println(" MHz");
//GCLK->GENCTRL.bit.RUNSTDBY=1; // !! can go
}
void loop() {
Serial.println("START");
///// MIC
//PM->APBCMASK.reg |= PM_APBCMASK_I2S;
int a = 0;
while (a == 0) a = I2S.available();
uint8_t current_measure = sample_audio_signal(samples);
///// RADIO
if (true)//((time_counter + transmit_interval) < millis())
{
struct_message.measurementdB = current_measure;
//struct_message.battery = measuredvbat;
// Send a message!
/*
Serial.print("Array content: ");
uint8_t* bla = (uint8_t*) &struct_message;
for (int i = 0; i < 3; i++)
{
Serial.println(bla[i]);
}*/
rf69.send((const uint8_t*) &struct_message, sizeof(struct_message));
rf69.waitPacketSent();
Serial.print("Wait for reply");
// Now wait for a reply
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];
uint8_t len = sizeof(buf);
if (rf69.waitAvailableTimeout(100)) {
// Should be a reply message for us now
if (rf69.recv(buf, &len)) {
Serial.print("Got a reply: ");
Serial.println((char*)buf);
} else {
Serial.println("Receive failed");
}
} else {
Serial.println("No reply, is another RFM69 listening?");
}
Serial.println("Radio sleeping");
rf69.sleep();
time_counter = millis();
}
// sleep time
#ifdef SLEEP
int sleepMS = Watchdog.sleep(10);
delay(250);
#else
delay(1000);
#endif
Serial.println("loop ended");
}
void Blink(byte PIN, byte DELAY_MS, byte loops) {
for (byte i=0; i<loops; i++) {
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
delay(DELAY_MS);
}
}
float sample_audio_signal(int samples[])
{
for (int i=0; i<SAMPLES; i++) {
int sample = 0;
while ((sample == 0) || (sample == -1) ) {
sample = I2S.read();
}
// convert to 18 bit signed
sample >>= 14;
samples[i] = sample;
}
// ok we have the samples, get the mean (avg)
float meanval = 0;
for (int i=0; i<SAMPLES; i++) {
meanval += samples[i];
}
meanval /= SAMPLES;
// subtract it from all samples to get a 'normalized' output
for (int i=0; i<SAMPLES; i++) {
samples[i] -= meanval;
}
// find the 'peak to peak' max
float maxsample, minsample;
minsample = 100000;
maxsample = -100000;
for (int i=0; i<SAMPLES; i++) {
minsample = min(minsample, samples[i]);
maxsample = max(maxsample, samples[i]);
}
int newdB = 20 * log10((float)maxsample / (float)ADC_SOUND_REF) + DB_SOUND_REF;
return newdB;
Ok, the best I got it down to is 3.8mA. I only got so far by leaving the voltage regulator and the internal oscillator (DFLL) on during sleeping.
After adding the following code to my setup routine, when board goes to sleep, the microphone still works after waking up:
SYSCTRL->DFLLCTRL.bit.RUNSTDBY=1;
SYSCTRL->VREG.bit.RUNSTDBY=1;
However, ideally I would like to get much less than that, but then the mic doesn't work...

Controlling DC motor using encoder

I'm trying to control the speed of two DC motors using an Arduino Uno and encoders that are connected to the motors.
I've written a code to check whether there's a change in the position of the encoder and according to that calculate the velocity of the motors.
Ive used this website for the code:
I'm having problems when calculating the difference between the new position of the encoder and the old position of the encoder. For some reason that difference keeps going up even though the speed stays the same.
This is my code so far:
#define pwmLeft 10
#define pwmRight 5
#define in1 9
#define in2 8
#define in3 7
#define in4 6
//MOTOR A
int motorSpeedA = 100;
static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile long encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile long oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile long reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent
//MOTOR B
static int pinC = 12; // Our first hardware interrupt pin is digital pin 2
static int pinD = 33; // Our second hardware interrupt pin is digital pin 3
volatile byte cFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte dFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile long encoderPosB = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile long oldEncPosB = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile long readingB = 0;
int tempPos;
long vel;
unsigned long newtime;
unsigned long oldtime = 0;
void setup() {
//MOTOR A
pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0, PinA, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1, PinB, RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
//MOTOR B
pinMode(pinC, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
pinMode(pinD, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
attachInterrupt(0, PinC, RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
attachInterrupt(1, PinD, RISING);
Serial.begin(9600); // start the serial monitor link
pinMode (in1, OUTPUT);
pinMode (in2, OUTPUT);
pinMode (in3, OUTPUT);
pinMode (in4, OUTPUT);
digitalWrite (8, HIGH);
digitalWrite (9, LOW); //LOW
digitalWrite (7, LOW); //LOW
digitalWrite (6, HIGH);
pinMode (pwmLeft, OUTPUT);
pinMode (pwmRight, OUTPUT);
}
void PinA(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos --; //decrement the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
} else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinB(){
cli(); //stop interrupts happening before we read pin values
reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPos ++; //increment the encoder's position count
bFlag = 0; //reset flags for the next turn
aFlag = 0; //reset flags for the next turn
} else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinC(){
cli(); //stop interrupts happening before we read pin values
readingB = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
if(readingB == B00001100 && cFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPosB --; //decrement the encoder's position count
dFlag = 0; //reset flags for the next turn
cFlag = 0; //reset flags for the next turn
} else if (readingB == B00000100) dFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void PinD(){
cli(); //stop interrupts happening before we read pin values
readingB = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
if (readingB == B00001100 && dFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
encoderPosB ++; //increment the encoder's position count
dFlag = 0; //reset flags for the next turn
cFlag = 0; //reset flags for the next turn
} else if (readingB == B00001000) cFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
sei(); //restart interrupts
}
void loop(){
analogWrite(pwmLeft, motorSpeedA);
analogWrite(pwmRight, motorSpeedA);
if(oldEncPos != encoderPos) {
newtime = millis();
tempPos = encoderPos - oldEncPos;
vel = tempPos / (newtime - oldtime);
Serial.println(tempPos);
oldEncPos = encoderPos;
oldtime = newtime;
delay(250);
}
if(oldEncPosB != encoderPosB) {
Serial.println(encoderPosB);
oldEncPosB = encoderPosB;
}
}
The two if statements are just made to check that the encoders are working properly. In the first if statement I'm trying to do the calculations of the velocity.
I would appreciate any feedback.
Thank you.
EDIT:
I found out theres an encoder library which makes everything a lot easier.
so now my code looks like this:
#include <Encoder.h>
#define pwmLeft 10
#define pwmRight 5
Encoder myEncA(3, 2);
Encoder myEncB(13, 12);
unsigned long oldtimeA = 0;
unsigned long oldtimeB = 0;
int speedA = 100;
int speedB = 130;
void setup() {
Serial.begin(9600);
digitalWrite (8, HIGH);
digitalWrite (9, LOW); //LOW
digitalWrite (7, LOW); //LOW
digitalWrite (6, HIGH);
pinMode (pwmLeft, OUTPUT);
pinMode (pwmRight, OUTPUT);
}
long oldPositionA = -999;
long oldPositionB = -999;
void loop() {
analogWrite(pwmLeft, speedA);
analogWrite(pwmRight, speedB);
long newPositionA = myEncA.read();
long newPositionB = myEncB.read();
if ((newPositionA != oldPositionA) || (newPositionB != oldPositionB)) {
unsigned long newtimeA = millis ();
long positionA = newPositionA - oldPositionA;
long positionB = newPositionB - oldPositionB;
long velB = (positionB) / (newtimeA - oldtimeA);
long velA = (positionA) / (newtimeA - oldtimeA);
oldtimeA = newtimeA;
oldPositionA = newPositionA;
oldPositionB = newPositionB;
Serial.println(velB);
}
}
I am still having problems with my "B" motor, the calculation is still way off for some reason.
Motor "A" works fine
A couple of issues, including a divide by zero error in loop(). This scan cause a reset of your controller. Always check the value of the divisor when doing a division!
Using only positive transitions unnecessarily reduces the resolution of your readings by 2.
The Arduino is an 8bit controller... Reading an int requires multiple instruction, which means you should disable interrupts before reading an int that's modified by an interrupt routine. Failure to do so will cause odd jumps in the vakue read. This is usually done like this:
//...
NoInterrupts();
int copyOfValue = value; // use the copy to work with.
interrupts();
//...
In your case, a single byte value is likely enough to store movement, with a reset every 30 ms, this should give you a top speed of 255 pulses/30ms = 8500 pulses/second or 1275000 rpm for a 24 ticks/turn encoder. :) in that case, no need to disable interrupts for a reading.
with one reading per 30ms, 1 tick /30ms = 33 tick/seconds, or 85 RPM. It's a bit high for motion. You may need to average readings, depending on your application.
Also, the algorithm you are using will definitely not work. The main reason is that the delay between reads and adjustments is too small. Most readings will be of zero. You will run into the problem when removing the println() calls. I suggest a pacing of at least 30 ms between readings. 100 ms may work a bit better, depending on your application. Using a float variable for speed average will definitely help.
void loop()
{
//...
if(oldEncPos != encoderPos) {
newtime = millis();
tempPos = encoderPos - oldEncPos;
vel = tempPos / (newtime - oldtime); // <-- if newtime == oltime => divide by zero.
//...
}
//...
}
The encoder reading code seems awfully complex...
#define PIN_A 2 // encoder bit 0
#define PIN_B 3 // encoder bit 1
volatile char encVal1;
volatile unsigned char encPos1; // using char
void OnEncoder1Change()
{
char c = (digitalRead(pinA) ? 0b01 : 0)
+ (digitalRead(pinB) ? 0b10 : 0); // read
char delta = (c - encVal1) & 0b11; // get difference, mask
if (delta == 1) // delta is either 1 or 3
++encPos1;
else
--encPos1;
encVal1 = c; // keep reading for next time.
encPos1 += delta; // get position.
// no need to call sei()
}
setup()
{
pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP);
// get an initial value
encValA = digitalRead(pinA) ? 0b01 : 0;
encValA += digitalRead(pinB) ? 0b10 : 0;
// use digitalPinToInterrupt() to map interrupts to a pin #
// ask for interrupt on change, this doubles .
attachInterrupt(digitalPinToInterrupt(PIN_A), OnEncoder1Change, CHANGE);
attachInterrupt(digitalPinToInterrupt(PIN_B), OnEncoder1Change, CHANGE);
//...
}
unsigned char oldTime;
unsigned char oldPos;
int speed;
void loop()
{
unsigned char time = millis();
if (time - oldTime > 30) // pace readings so you have a reasonable value.
{
unsigned char pos = encPos1;
signed char delta = pos - oldPos;
speed = 1000 * delta) / (time - oldTime); // signed ticks/s
encPos1 -= pos; // reset using subtraction, do you don't miss out
// on any encoder pulses.
oldTime = time;
}
}

Arduino Interrupt misbehaving

I'm having some problems with some C code I'm writing for an arduino project. The goal is to digitize a large quantity of analog signals with external multiplexed ADCs, then load these digital values into an external shift register and shift them into the Arduino using SPI.To test my code I only have one ADC multiplexing 4 signals.
The interrupt pin (20) is connected to a comparator circuit which looks at the raw analog signal and pulls the pin high when the voltage is 1V or higher. When the ISR is called it will disable global interrupts "noInterrupts()" set an event flag, detach pin 20 from the interrupt handler, enable global interrupts "interrupts()" and finally return to where it left off.
I'm facing a couple issues, first the ISR is called once fine, a second time fine but after the second ISR call it is not called again untill, which is my seconds issue, the interrupt pin goes low. As per the AttachInterupt() function the ISR should only be called when pin 20 is high. This can be seen in the first and second picture I have attached. Another thing I notice is that the duration that the interrupt pin is high has no effect on whether a 3rd ISR is called.
I'm not sure if this is an issue with my understanding of the interrupt-handling of the Arduino, or a code screw up resulting in a stack overflow or something like that.
// the sensor communicates using SPI, so include the library:
#include <SPI.h>
//Constants
#define RD 41 //pin 41 conneced to read pin
#define INT1 37 //pin 37 connecte to interrupt 1
#define CLK_INH 53 //pin 53 connected to clk inhibit
#define LD 40 //pin 40 connected to load pin
#define INPUT_MAX 3 //input selector limit (Zero Indexed)
#define SENSORS 3 //how many sensors are used (Zero Indexed)
#define DATA_DUMP 38 //pin 29 controlls the data dump deature
#define BYTE_LEN 1 //number of ADC used
#define DEBUG1 17
#define DEBUG2 16
//Controls
unsigned char selector = 0; //ACD input selector
volatile byte eventFlag = LOW; //Control Flag, set to True when event occurs
bool lastButtonState = true;
//Counters
unsigned int i = 0; //eventLog[i]: event counter
unsigned int j = 0; //eventLog[i].data[j]: data counter
//Function Delcarations
unsigned char inputSelector (unsigned char my_selector);
void lockAndPop ();
void dataDump ();
void debug (int pin);
bool fallingEdge (bool); //check for a falling edge of a digital read
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
// put your setup code here, to run once:
pinMode(RD, OUTPUT); //Read pin, 0 = begin analog conversion (ADC)
pinMode(INT1, INPUT); //Interrupt pin, 0 = conversion complete (ADC)
pinMode(CLK_INH, OUTPUT); //Clock inhibit pin, 1= no change on output (ShiftRegister)
pinMode(LD, OUTPUT); //Shift/Load pin, 1 = data is shifted (ShiftRegister)
pinMode(DATA_DUMP, INPUT);
pinMode(DEBUG1, OUTPUT);
pinMode(DEBUG2, OUTPUT);
DDRA = 0xFF; //Set port A to ouput
SPI.begin();
//SPI.mode1 Clock idel low CLOP = 0, Data sampled on falling edge CPHA = 1
SPI.beginTransaction(SPISettings(5000000, MSBFIRST, SPI_MODE1));
digitalWrite(RD, HIGH); //Stop conversion
digitalWrite(CLK_INH, HIGH); //No change on the output
digitalWrite(LD, LOW); //Load the shift register
digitalWrite(DEBUG1, LOW);
digitalWrite(DEBUG2, LOW);
attachInterrupt(digitalPinToInterrupt(20), pin_ISR, HIGH); //Call pin_ISR when pin20 goes high
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef struct //event structure, containts a timestamp element and an array of 18 data points
{
unsigned long int timeStamp;
unsigned char data[SENSORS];
} Event;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Event eventLog[200]; //an array of structures representing 200 events, once the 200 events have been filled the data will be printed
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop() {
if(fallingEdge(digitalRead(DATA_DUMP))){ //If there is falling edge on the data dump button, call the dataDump function
dataDump();
}
debug(DEBUG2);
if(eventFlag) //if the Event flag is set to true by ISR begin the conversion steps
{
debug(DEBUG1);
digitalWrite(RD,LOW); //Start conversion
while(digitalRead(INT1)){} //Wait for conversion to complete
eventLog[i].timeStamp = micros();
for (j=0; j<=SENSORS; j++) {
lockAndPop(); //lock digital value and reset conversion
PORTA = inputSelector(selector); //increment the selector pin
digitalWrite(RD, LOW); //Start new conversion
digitalWrite(CLK_INH, LOW); //Start the data transfer
eventLog[i].data[j] = SPI.transfer(0); //read a single byte from the SPI line
digitalWrite(CLK_INH, HIGH); //Inhibit clock
digitalWrite(LD, LOW);
while(digitalRead(INT1)){} //wait for previous conversion to end
}
i++;
digitalWrite(RD, HIGH);
selector = 0;
if(i>=200){
dataDump(); //if the event log hits 200 before a data dump is request, dump the data
}
eventFlag = LOW;
attachInterrupt(digitalPinToInterrupt(20), pin_ISR, HIGH);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void pin_ISR() {
noInterrupts();
detachInterrupt(digitalPinToInterrupt(20));
eventFlag = HIGH;
interrupts();
return;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char inputSelector (unsigned char my_selector){
if(my_selector==INPUT_MAX){ //if the current selector is at the highest value reset to 0
return 0;
}
return my_selector++; //increment the input selector by 1
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void lockAndPop (){
digitalWrite(LD, HIGH); //Lock in digital value
digitalWrite(RD, HIGH); //Reset conversion
return;
}
void dataDump (){
detachInterrupt(digitalPinToInterrupt(20));
char buf[100], *pos = buf; //create a buffer of 100 charaters, anda pointer to the begining of that buffer
char *base = buf; //create a base address to reset the buffer
unsigned int eventCount = i; //how many events occured before dump command was called
unsigned int localCount;
unsigned int localData;
Serial.begin(115200);
Serial.println(i);
for (localCount = 0; localCount<=eventCount; localCount++){
pos += sprintf(pos, "%lu", eventLog[localCount].timeStamp); //sprintf will append the data to the pointer "pos", and return the number of byte append.
for (localData = 0; localData<=SENSORS; localData++){
pos += sprintf(pos, " %d", (unsigned int)(eventLog[localCount].data[localData]));
}
Serial.println(buf);
pos = base;
}
i=0;
j=0;
Serial.end();
attachInterrupt(digitalPinToInterrupt(20), pin_ISR, HIGH);
return;
}
void debug(int pin){
digitalWrite(pin, HIGH);
digitalWrite(pin, LOW);
return;
}
bool fallingEdge (bool currentButtonState){
if(!currentButtonState&&lastButtonState){
lastButtonState = currentButtonState;
return 1;
}
lastButtonState = currentButtonState;
return 0;
}
There is a bit of noise happening on the ISR pin, but this shouldn't matter as I'm disabled that particular pin within the service routine so I wouldn't think this is an issue

Resources