Righto, so someone on an electric skateboard forum asked for some help controlling an LED controller from and RC receiver channel. The LED controller wants a 1KHz PWM signal to set the brightness of the LED. I offered to help, but not having an Attiny85 myself I had to do a bit of googling and guessing. Eventually by going back and forth a few times (I send some new code, he tells me what error he gets) we got it compiling and working to some extent. It does successfully control the brightness of the LED, but for some reason it goes in 4 distinct steps instead of smoothly. We're not sure why but we just decided to ignore that for the time being.
Then the guy asked if we could implement a way read in the throttle and control a brake light. I thought that was a great idea, so I modified the code to support two channels. Obviously it will need some more code to check if the throttle is at less than half to enable the brake light, but we're just starting with getting it working on two channels first. The guy tested the 2-channel code and found that the signal is now only working on about 5% of the range of his servo tester, so when he changes the "map" from 1000=0, 2000=100 to 1500=0, 1600=100 the LED changes brightness smoothly and across its whole brightness range, though the servo tester still only moves over about 5% of its range. It's strange that a) it no longer changes brightness in 4 distinct steps, and b) only 5% of the servo tester's range is usable. I'm fairly confused at this point. Here's the 2-channel code:
#include "avr/interrupt.h"
volatile unsigned long rc_timeStart1;
volatile unsigned long rc_timeStart2;
volatile int rc_value1;
volatile int rc_value2;
volatile int rcPin1LastState = 0;
volatile int rcPin2LastState = 0;
int percentage1 = 0;
int percentage2 = 0;
unsigned long lastPwmStart1 = 0;
unsigned long lastPwmStart2 = 0;
const byte pwmPin1 = 0;
const byte pwmPin2 = 1;
const byte rcPin1 = 2;
const byte rcPin2 = 3;
void setup() {
pinMode(rcPin1, INPUT);
pinMode(rcPin2, INPUT);
pinMode(pwmPin1, OUTPUT);
pinMode(pwmPin2, OUTPUT);
GIMSK = 0b00100000; //turn on pin change interrupts
PCMSK = 0b00001100; //turn on interrupts on pin PB2 and PB3 (physical pins 2 & 3)
sei();
}
void loop() {
percentage1 = map(rc_value1, 1000, 2000, 0, 100); //assume the receiver is outputting exactly in spec between 1000 and 2000; it's probably not but should be close enough
percentage2 = map(rc_value2, 1000, 2000, 0, 100);
percentage1 = constrain(percentage1, 0, 100);
percentage2 = constrain(percentage2, 0, 100);
doPWM1();
doPWM2();
}
ISR(PCINT0_vect)
{
if(digitalRead(rcPin1) == HIGH) {
if(rcPin1LastState == 0) {
rc_timeStart1 = micros();
rcPin1LastState = 1;
}
} else {
if(rcPin1LastState == 1) {
rc_value1 = micros() - rc_timeStart1;
rcPin1LastState = 0;
}
}
if(digitalRead(rcPin2) == HIGH) {
if(rcPin2LastState == 0) {
rc_timeStart2 = micros();
rcPin2LastState = 1;
}
} else {
if(rcPin2LastState == 1) {
rc_value2 = micros() - rc_timeStart2;
rcPin2LastState = 0;
}
}
}
void doPWM1() {
//1KHz PWM signal means beginning a new pulse every 1000 microseconds or 1 millisecond
//then we just vary the length of the pulse between 0 and 1000 microseconds
unsigned long microseconds = micros();
if(microseconds - lastPwmStart1 >= 1000) { //begin new pulse
lastPwmStart1 = microseconds;
digitalWrite(pwmPin1, HIGH);
} else {
int targetLength = percentage1 * 10; //0 - 100 times 10 will be 0 - 1000
if(microseconds - lastPwmStart1 >= targetLength) { //reached the target pulse length, time to turn off the pin
digitalWrite(pwmPin1, LOW);
}
}
}
void doPWM2() {
unsigned long microseconds = micros();
if(microseconds - lastPwmStart2 >= 1000) { //begin new pulse
lastPwmStart2 = microseconds;
digitalWrite(pwmPin2, HIGH);
} else {
int targetLength = percentage2 * 10; //0 - 100 times 10 will be 0 - 1000
if(microseconds - lastPwmStart2 >= targetLength) { //reached the target pulse length, time to turn off the pin
digitalWrite(pwmPin2, LOW);
}
}
}
So basically I would just like someone who knows a bit more about interrupts and generating PWM signals (and has some free time) to give my code a quick once-over and see if there's any obvious flaws. It's pretty simple code but I can't see anything wrong with it. I got it working on a couple of Unos last night (one had the above code and was hooked up to a regular old LED and the other was generating a servo signal) and it seemed to work perfectly. I'm just not sure how I can go about debugging it when I'm in Australia and the guy with the RC receiver, servo tester, Attiny85 and oscilloscope is in the US.
I realise a Trinket isn't an Arduino, but given that the register names and pinouts are really the only differences I'm hoping no one minds