Per il mio pregetto ho necessità di un posizionatore di precisione, lo sto realizzando con un motore DC accoppiato ad un encoder in quadratura da 150LPI.
Grazie al codice nel playg ho potuto abilitare gli interrupt sui pin 4 e 5 di arduino. Per il momento muovo l'encoder manualmente il programma computa una variabile che quando l'encoder compie un giro di 360° contiene il valore di 2880 che diviso 360° mi da una risoluzione di 0.125°.
Il codice lo potete vedere alla fine, ora sto cercando un driver per motore DC piccolo e flessibile. Ho scartato 293 ecc insomma quelli conosciuti per vari motivi.
Avete link?
Sono graditi commenti e collaborazione a 360°, in particolare per sistemare il codice in librerie, io sono inesperto ma imparo presto.
Il codice:
#include "pins_arduino.h"
/*
* an extension to the interrupt support for arduino.
* add pin change interrupts to the external interrupts, giving a way
* for users to have interrupts drive off of any pin.
* Refer to avr-gcc header files, arduino source and atmega datasheet.
*/
/*
* Theory: all IO pins on Atmega168 are covered by Pin Change Interrupts.
* The PCINT corresponding to the pin must be enabled and masked, and
* an ISR routine provided. Since PCINTs are per port, not per pin, the ISR
* must use some logic to actually implement a per-pin interrupt service.
*/
/* Pin to interrupt map:
* D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
* D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
* A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
*/
volatile uint8_t *port_to_pcmask[] = {
&PCMSK0,
&PCMSK1,
&PCMSK2
};
static int PCintMode[24];
typedef void (*voidFuncPtr)(void);
volatile static voidFuncPtr PCintFunc[24] = {
NULL };
volatile static uint8_t PCintLast[3];
/*
* attach an interrupt to a specific pin using pin change interrupts.
*/
void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) {
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
uint8_t slot;
volatile uint8_t *pcmask;
// map pin to PCIR register
if (port == NOT_A_PORT) {
return;
}
else {
port -= 2;
pcmask = port_to_pcmask[port];
}
// -- Fix by Baziki. In the original sources it was a little bug, which cause analog ports to work incorrectly.
if (port == 1) {
slot = port * 8 + (pin - 14);
}
else {
slot = port * 8 + (pin % 8);
}
// --Fix end
PCintMode[slot] = mode;
PCintFunc[slot] = userFunc;
// set the mask
*pcmask |= bit;
// enable the interrupt
PCICR |= 0x01 << port;
}
void PCdetachInterrupt(uint8_t pin) {
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *pcmask;
// map pin to PCIR register
if (port == NOT_A_PORT) {
return;
}
else {
port -= 2;
pcmask = port_to_pcmask[port];
}
// disable the mask.
*pcmask &= ~bit;
// if that's the last one, disable the interrupt.
if (*pcmask == 0) {
PCICR &= ~(0x01 << port);
}
}
// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.
static void PCint(uint8_t port) {
uint8_t bit;
uint8_t curr;
uint8_t mask;
uint8_t pin;
// get the pin states for the indicated port.
curr = *portInputRegister(port+2);
mask = curr ^ PCintLast[port];
PCintLast[port] = curr;
// mask is pins that have changed. screen out non pcint pins.
if ((mask &= *port_to_pcmask[port]) == 0) {
return;
}
// mask is pcint pins that have changed.
for (uint8_t i=0; i < 8; i++) {
bit = 0x01 << i;
if (bit & mask) {
pin = port * 8 + i;
// Trigger interrupt if mode is CHANGE, or if mode is RISING and
// the bit is currently high, or if mode is FALLING and bit is low.
if ((PCintMode[pin] == CHANGE
|| ((PCintMode[pin] == RISING) && (curr & bit))
|| ((PCintMode[pin] == FALLING) && !(curr & bit)))
&& (PCintFunc[pin] != NULL)) {
PCintFunc[pin]();
}
}
}
}
SIGNAL(PCINT0_vect) {
PCint(0);
}
SIGNAL(PCINT1_vect) {
PCint(1);
}
SIGNAL(PCINT2_vect) {
PCint(2);
}
volatile long ticktocks = 0;
long i = 0;
void tick(void) {
ticktocks++;
}
void tock(void) {
ticktocks--;
}
const int encoder0PinA = 4;
const int encoder0PinB = 5;
volatile long encoder0Pos = 0;
void encoder0ChA()
{
if (digitalRead(encoder0PinA) == HIGH) {
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == LOW) {
encoder0Pos += 1; // CW
}
else
{
encoder0Pos -= 1; // CCW
}
}
else
{
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinB) == HIGH) {
encoder0Pos += 1; // CW
}
else {
encoder0Pos -= 1; // CCW
}
}
}
void encoder0ChB()
{
// look for a low-to-high on channel B
if (digitalRead(encoder0PinB) == HIGH) {
// check channel A to see which way encoder is turning
if (digitalRead(encoder0PinA) == HIGH) {
encoder0Pos += 1; // CW
}
else {
encoder0Pos -= 1; // CCW
}
}
else
{
// check channel B to see which way encoder is turning
if (digitalRead(encoder0PinA) == LOW) {
encoder0Pos += 1; // CW
}
else {
encoder0Pos -= 1; // CCW
}
}
}
int ledPin = 13;
void setup()
{
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
pinMode(encoder0PinA, INPUT);
pinMode(encoder0PinB, INPUT);
delay(3000);
PCattachInterrupt(encoder0PinA, encoder0ChA, CHANGE);
PCattachInterrupt(encoder0PinB, encoder0ChB, CHANGE);
}
void loop() {
if ((encoder0Pos == 0) || (encoder0Pos == 2880)) {
digitalWrite(ledPin, HIGH);
}
else
{
digitalWrite(ledPin, LOW);
}
i++;
delay(1000);
Serial.print(i, DEC);
Serial.print(" ");
Serial.println(encoder0Pos);
if (i > 256) {
PCdetachInterrupt(encoder0PinA);
PCdetachInterrupt(encoder0PinB);
}
}