Hello,
I am starting with arduino nano and am trying to get an arduino to read value from an encoder and print it on a screen. Ultimately, I want to have 2 encoders, one on each axis of my telescope and do something with it. But first I need to be able to read data from the encoder.
I have a sketch which works, but it misses steps. I tested it by puting the encoder in place on the telescope, loking through it, tturning and coming back to the same spot...the value azTripodCount should be the same...but it's not. If I turn in one direction only, it'll be too high, if I turn in the other direction, it'll be too low, if I turn both ways, it varies.
I think I am missing step because my arduino board is doing too much with the screen.
#include <avr/interrupt.h> // Needed to use interrupts
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#define TFT_CS 10
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
volatile long altTripodCount = 0; //This variable will increase or decrease depending on the rotation of encoder
volatile long azTripodCount = 0; //For now, I use only azimuth, I will see later to get altitude and azimuth
volatile unsigned int printNumber = 0;
static unsigned int printInterval = 5000;
const unsigned int gearRatio = 144;
const float pi = 3.14159265;
const unsigned int stepsPerRevEncoder = 1200;
const unsigned int maxCount = gearRatio * stepsPerRevEncoder;
void setup() {
//Setup screen
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_RED);
//Setup interupts
DDRD &= B11000011; //Configure PORTD pin 2 to 5 as inputs // 2:green alt; 3:green az; 4: white alt; ,5: white az
PORTD |= B00111100; //Activate pull-ups in PORTD pin 2 to 5
EICRA |= (1 << ISC01 | 1 << ISC00); // set INT0 to trigger on rising edge
EIMSK |= (1 << INT0); // Turns on INT0 interruption
}
void loop() {
// Write out to serial monitor the values
printAltAz();
// Keep track of changes
previousAzTripodCount = azTripodCount;
++printNumber;
delay(5000);
}
ISR(INT0_vect)
{
// Activated if channel A changing state
// Check channel B to determine the direction
// if(((PIND & (1<<PD2))<<2) ^ (PIND & ((1<<PD4)))) {
// --azTripodCount;
// }else{
// ++azTripodCount;
// }
// Or same thing faster
switch (PIND & 20) {//20 = b0010100
case 20:
++azTripodCount;
break;
case 0:
++azTripodCount;
break;
default:
--azTripodCount;
}
}
void printAltAz() {
if (azTripodCount > maxCount){
azTripodCount = azTripodCount - maxCount;
}
else if (azTripodCount < 0){
azTripodCount = azTripodCount + maxCount;
}
tft.setCursor(0, 30);
tft.fillRect(0,30,72,80,ST77XX_BLACK); //5 wide, 7 high, 1 in between
tft.setCursor(0, 30);
tft.setTextColor(ST77XX_RED);
tft.setTextSize(2);
tft.println(altTripodCount);
tft.println(azTripodCount);
tft.setTextSize(2);
tft.println(toRad(altTripodCount));
tft.println(toRad(azTripodCount));
tft.println(printNumber);
}
float toRad(long count){
return count*2*pi/maxCount;
}
As you can see, i tried a few tricks to make it faster, but it still misses steps. Also those tricks might not be as clever as I thought, i'm only a beginner :-\
So I tried to hook up 2 arduinos: the master would deal with the screen and anything else I will add later, even if it's slow it's ok. And when it needs to use the value of the encoder, it would get it from the slave.
The master code:
#include <SPI.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#define TFT_CS 12
#define TFT_RST 8
#define TFT_DC 9
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
volatile long altTripodCount = 0; //This variable will increase or decrease depending on the rotation of encoder
volatile long azTripodCount = 0;
long previousAzTripodCount = 1;
volatile unsigned int printNumber = 0;
static unsigned int printInterval = 500;
const unsigned int gearRatio = 144;
const float pi = 3.14159265;
const unsigned int stepsPerRevEncoder = 1200;
const unsigned int maxCount = gearRatio * stepsPerRevEncoder;
void setup() {
Serial.begin(115200);
//Setup screen
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_RED);
//Set SlaveSelection pin as output.
pinMode(SS, OUTPUT);
//and Make it HIGH to prevent to start communication right away
digitalWrite(SS, HIGH);
//Start the SPI communication.
SPI.begin();
}
void loop() {
//Receive azValue from the slave (I actually just try to send a 0 or 1 atm, I'll see later to send a long)
//Enable slave arduino with setting the SlaveSelection pin to 0Volt
digitalWrite(SS, LOW);
// Wait for a moment
delay(10);
//We sent the data here and wait for the response from device
char receivedValue = SPI.transfer(1); // Sending 1, but it could be anything else
//And then write the answer to the serial port
Serial.println("received_value");
Serial.println(receivedValue);
//Disable slave arduino with setting the SlaveSelection pin to 5Volt
digitalWrite(SS, HIGH);
// Write out to serial monitor the values
printAltAz();
// Keep track of changes
previousAzTripodCount = azTripodCount;
++printNumber;
delay(printInterval);
}
void printAltAz() {
if (azTripodCount > maxCount){
azTripodCount = azTripodCount - maxCount;
}
else if (azTripodCount < 0){
azTripodCount = azTripodCount + maxCount;
}
tft.setCursor(0, 30);
tft.fillRect(0,30,72,80,ST77XX_BLACK); //5 wide, 7 high, 1 in between
tft.setCursor(0, 30);
tft.setTextColor(ST77XX_RED);
tft.setTextSize(2);
tft.println(altTripodCount);
tft.println(azTripodCount);
tft.setTextSize(2);
tft.println(toRad(altTripodCount));
tft.println(toRad(azTripodCount));
tft.println(printNumber);
Serial.println(printNumber);
Serial.println(toRad(azTripodCount));
}
float toRad(long count){
return count*2*pi/maxCount;
}
The slave code:
#include <SPI.h>
volatile long altTripodCount = 0; //This variable will increase or decrease depending on the rotation of encoder
volatile long azTripodCount = 0;
volatile unsigned int printNumber = 0;
char i = 0;
void setup() {
Serial.begin(115200);
DDRD &= B11000011; //Configure PORTD pin 2 to 5 as inputs // 2:green alt; 3:green az; 4: white alt; ,5: white az
PORTD |= B00111100; //Activate pull-ups in PORTD pin 2 to 5
EICRA |= (1 << ISC01 | 1 << ISC00); // set INT0 to trigger on rising edge
EIMSK |= (1 << INT0); // Turns on INT0 interruption
pinMode(SS,INPUT); // Set SS as input
pinMode(SCK, OUTPUT);
pinMode(MOSI,OUTPUT); // Set MOSI as output
pinMode(MISO,INPUT); // Set MISO as input
// SPCR - SPI Control Register
// According to struct table we enable the SPI and Interface
SPCR |= 0b11000000;
// SPSR - SPI Status Register
SPSR |= 0x00;
}
void loop(){
delay(1000);
}
ISR (SPI_STC_vect) //Inerrrput routine function
{
Serial.println("slave interupted");
//Here we read the SPI lines
//This line will check data for every ASCII codes for 8-bit received data
//SPDR -> SPI Data Read bit
SPDR = i;
i ++;
if ( i > 255)
i = 0;
while(!(SPSR & (1 << SPIF)));
//Load the received data to the variable
char received = SPDR;
//And send it to the serial communication bus
Serial.println(received);
}
ISR(INT0_vect)
{
Serial.println("slave interupted encoder");
switch (PIND & 20) {//20 = b0010100
case 20:
++azTripodCount;
break;
case 0://20 = b0000000
++azTripodCount;
break;
default:
--azTripodCount;
}
}
I moved the CS pin from my screen to the pin 12, because pin 10 is the SlaveSelect pin. The screen doesn't work anymore. I used Serial debuging to see what's going on, and there are a few more porblems.The encoder interupt still works on the slave but the master receives only ⸮ .
Cheers,
Gregoire