Hi guys,
I am writing some code for the Digispark. Actually it is a modified version of Phil's digispark_3ch_propo_sc_v2, intended for conversion of a WebraPicco Tx , see viewtopic.php?f=25&t=1168
Actually it was working very well, just as I intended it to be. Then I decided I wanted to add a buzzer and to have it sound two pips just before it enters the loop. I have been spending about 4 hours now to get it to work, without success. All it wants to do is show a single short flash (I have connected a led instead of the beeper for now, I am doing this in the living room and don't want to drive my wife nuts.....).
It's probably something simple, but I cannot work out what I am doing wrong, hence this cry for help.
Here is that part of the code:
Cheers,
Max.
Edit: and yes, buzzer is defined as OUTPUT, on P2. And the Digispark has been de-fluffed.
Help needed
- Mike_K
- Posts: 676
- Joined: 16 Feb 2018, 06:35
- Location: Hertfordshire
Re: Help needed
Hi Max
You'll probably need to post the complete modified sketch (as a .ino) to get help. I've only briefly looked at Phil's Digispark encoder, mainly as I'm an ATtiny85 fan, but I thought the buzzer was on P1?? Or have you done away with the elevator pot?
Cheers Mike
You'll probably need to post the complete modified sketch (as a .ino) to get help. I've only briefly looked at Phil's Digispark encoder, mainly as I'm an ATtiny85 fan, but I thought the buzzer was on P1?? Or have you done away with the elevator pot?
Cheers Mike
-
- Posts: 330
- Joined: 31 Jan 2019, 11:48
- Location: Boskoop, Netherlands
Re: Help needed
Hi Mike,Phil_G wrote: ↑04 Jan 2021, 18:08 Your modification prevents any ppm generation for four seconds Max
You need to do it on a frame-gated basis which is how the inactivity warning works.
It's further complicated by the multiplexed buzzer and button input...
I have posted 2, 3 and 4 channel versions, including rudder/throttle... all work perfectly
Cheers
Phil
I revised the entire sketch for my specific purpose, one of the things I did was to split the combined buzzer and s/c button, P1 is now the button and P2 the buzzer.
I did test the whole thing without a buzzer installed (or its led replacement), and all was well. I did not test the inactivity timer, so I don't know if that works or not. But that part is exactly the same as Phil's original (yes Phil, I will add a source reference.....), except maybe for the channel assignment.
This is my code. I commented the part about the reversing check out since I did revise that to add an audible signal (the Webra has a non-centering lever for rudder, so you could be reversing inadvertently), and I wanted to be sure that it is not the culprit of my current trauma..
Cheers,
Max.
Code: Select all
// Simple 2 channel propo encoder for DigiSpark ATTiny85, for Rudder&Throttle channels in a converted Webra Picco transmitter
// A2=throttle, A3=rudder
// Calibrate button is push-to-make in this version, alternate use is to set/reset throttle timeout.
// REMOVE THE SCHOTTKY DIODES ON D3 & D4, the pullup on P3 and either LED or LED Resistor (they flick off easily with a watchmakers screwdriver)
// Connections:
// Pots wired between ground and regulated 5v from the Digispark
// A2(P4)=throttle wiper, A3(P3)=rudder wiper, P2=buzzer, P1=calibrate button, P0=PPM out.
// Rudder pot calibration, hold button in, switch on, still holding button move pot to extremes, hold button.
// Centralise spot including throttle, release button.
// Servo reversing by moving Rudder pot to any end on power up (saved to flash).
// Adapted for Gerard's Webra Picco Tx conversion. Positive mark pulses ppm for Frsky DHT module.
static int ppm = 0, button = 1, buzzer = 2; // D1 is calibrate & timeout set/reset button
int ch, ppmPulse = 300, neutralPulse, raw, calibrated = 1, RudHi = 0, RudLo = 1023, RudMid = 512;
int chtemp, ch0val, channel[] = {0, 0, 0, 0, 0, 0, 8000}; // last is used for sync
byte tlock = 1, RudReverse = 0;
unsigned int inact = 0;
#define INACTFRAMES 30000 // inactivity alarm, 30,000 x 20ms frames is 10 minutes
#define full_n -500 // full negative deflection of sticks
#define full_p +500 // full positive deflection of sticks
#include <EEPROM.h>
void setup() {
OSCCAL = 96; // Digisparks can vary in speed, make larger if timings too long.
noInterrupts();
pinMode(button, INPUT_PULLUP);
pinMode(buzzer, OUTPUT);
pinMode(ppm, OUTPUT);
while (digitalRead(button) == 0) { // calibrate sticks by holding button, (make-contact to GND)
calibrated = 0;
raw = analogRead(3); // read rudder (A3) input
if (raw > RudHi) RudHi = raw;
if (raw < RudLo) RudLo = raw;
RudMid = raw; // save neutral of rudder stick as button is released
} // calibrate button released
if (calibrated == 0) {
// save the calibration values
EEPROMWriteInt(0, RudLo); // eeprom location 0 (decimal)
EEPROMWriteInt(2, RudMid); // eeprom location 2 (decimal)
EEPROMWriteInt(4, RudHi); // eeprom location 4 (decimal)
calibrated = 1; // flag as calibrated
}
// load the saved calibration values
RudLo = EEPROMReadInt(0); // eeprom location 0 (decimal)
RudMid = EEPROMReadInt(2); // eeprom location 2 (decimal)
RudHi = EEPROMReadInt(4); // eeprom location 4 (decimal)
// load saved rudder channel reversal from eeprom location 6 (decimal), only last bit is considered
RudReverse = EEPROM.read(6) & 1; // (bitwise AND with B00000001: set bits 1-7 to 0, leave bit0 as is)
// check for rudder reversing, stick over on power-up.
/* channel[1] = map(analogRead(3), RudLo, RudHi, full_n, full_p);
if (channel[1] > full_p - 50 || channel[1] < full_n + 50) {
while (digitalRead (button) == 1) digitalWrite (buzzer,HIGH); // buzzer sounds to avoid inadvertent reversal,
digitalWrite (buzzer,LOW); // continue by pressing button, switch Tx off to abort
RudReverse ^= B0000001; // flip bit0 of byte
EEPROM.write(6, RudReverse);
}
*/
neutralPulse = 1500 - ppmPulse;
for (int x = 0; x<2; x++) {
delay (1000);
digitalWrite (buzzer,HIGH);
delay (1000);
digitalWrite (buzzer,LOW);
}
}
void loop() {
raw = analogRead(2); // read throttle pot wiper to A2
channel[0] = map (raw, 0, 1023, full_n, full_p);
raw = analogRead(3); // read rudder pot wiper to A3
if (raw > RudMid) channel[1] = map (raw, RudMid, RudHi, 0, full_p);
else channel[1] = map (raw, RudLo, RudMid, full_n, 0);
ch0val = channel[1]; // used by inactivity timer
channel[2] = 0; // channel 2 neutral
channel[3] = 0; // channel 3 neutral
channel[4] = 0; // channel 5 neutral
channel[5] = 0; // channel 6 neutral
channel[6] = 0; // 7th element is sync
// soft throttle lock, throttle can't be opened until it has been closed first
if (channel[0] < full_n + 50) tlock = 0;
if (tlock) channel[0] = full_n;
// rudder channel reversal
if (RudReverse == 1) channel[1] = - channel[1];
// frame formatting
for (int ch = 0; ch < 6; ch++) {
channel[ch] = constrain(channel[ch], full_n, full_p);
channel[ch] += neutralPulse;
channel[6] += channel[ch];
}
channel[6] = 15000 - channel[6]; // CHECK, SHOULD BE 20000 - 7*PPMPULSE - CHANNEL [6]
//send ppm frame, last channel holds sync value
for (int ch = 0; ch < 7; ch++) {
digitalWrite(ppm, HIGH);
delayMicroseconds(ppmPulse);
digitalWrite(ppm, LOW);
delayMicroseconds(channel[ch]);
}
// inactivity timer. 10 mins is approx 30000 frames.
if (ch0val < -100 || ch0val > 100) { // moving rudder resets timer start
inact = 0;
}
if (++inact > INACTFRAMES) {
if (bitRead(inact, 3) == 1 && bitRead(inact, 5) == 1) {
digitalWrite(buzzer, HIGH);
}
else {
digitalWrite(buzzer, LOW);
if (digitalRead(button) == 0) inact = 0; // Push button to stop alarm
}
}
}
// This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, int p_value)
{
byte lowByte = p_value % 256;
byte highByte = p_value / 256;
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return lowByte + highByte * 256;
}
-
- Posts: 330
- Joined: 31 Jan 2019, 11:48
- Location: Boskoop, Netherlands
Re: Help needed
I know Phil, but it happens during Setup, not in the loop. And the millisecs shown are just for testing, I will change them to more appropriate values later.
-
- Posts: 745
- Joined: 16 Feb 2018, 14:11
- Location: Warwickshire
Re: Help needed
You've got a noInterrupts(); statement at line 28.
That stops all interrupts, including the timer0 interrupt which the Arduino uses to keep track of milliseconds. So delay() won't work.
In general, you don't want to leave interrupts disabled for very long in a normal Arduino sketch. I don't see a good reason for disabling them in your setup() routine.
That stops all interrupts, including the timer0 interrupt which the Arduino uses to keep track of milliseconds. So delay() won't work.
In general, you don't want to leave interrupts disabled for very long in a normal Arduino sketch. I don't see a good reason for disabling them in your setup() routine.
-
- Posts: 330
- Joined: 31 Jan 2019, 11:48
- Location: Boskoop, Netherlands
Re: Help needed
Ah, thanks Martin. But it does not seem to affect the delayMicroseconds() used to construct the ppm stream.
-
- Posts: 745
- Joined: 16 Feb 2018, 14:11
- Location: Warwickshire
Re: Help needed
Yeah delayMicroseconds() just uses the timer0 hardware. There is a hardware register, TCNT0, that is set to count once every 4 microseconds. It counts up to 255 and then resets back to zero. It does this regardless of the interrupts. delayMicroseconds() just sits looking at this changing register to keep track of time, so it works even when interrupts are switched off. Note that delayMicroseconds() and micros() only have a resolution of 4. If you write a loop to just Serial.println(micros()); you'll see that all the numbers printed are multiples of 4.
Technical details follow (most sane people will skip the rest of this).
Every time that timer/counter wraps round, and interrupts aren't turned off, then the milliseconds count is increased by at least one. Note that the timer wraps every 256 x 4 microseconds which is 1.024 milliseconds, so there is a fudge factor built in to millis() that keeps track of the fractional error and increases milliseconds by 2 instead of 1 roughly every 43 milliseconds: internally, it keeps track of the 'fractional parts' of a millisecond so it's not exactly every 43 counts that this happens. If you have a crystal and not a resonator (and don't have interrupts disabled), then it's very accurate long term, but it stutters/jitters roughly every 43 counts.
Note that it would be perfectly possible to set Timer0 to reset back to 0 at a count of 249 instead of 255 - and this would make millis() dead accurate without needing the fudge - but the Arduino also uses Timer0 for micros() and for driving some PWM pins - resetting it every 250 counts would make those functions more complex and the PWM would have lower resolution.
If you're not using Timer2 for anything else then you can use that to get more accurate timing of your pulses (one microsecond resolution instead of four). I posted a thread somewhere on here that shows you how to get accurateMicros() - it works just like micros() except that it has four times the resolution.
And by careful manipulation of the timers, you can get timings accurate to one-sixteenth of a microsecond. You'll need to read and understand the datasheet if you want to exploit the hardware fully.
Technical details follow (most sane people will skip the rest of this).
Every time that timer/counter wraps round, and interrupts aren't turned off, then the milliseconds count is increased by at least one. Note that the timer wraps every 256 x 4 microseconds which is 1.024 milliseconds, so there is a fudge factor built in to millis() that keeps track of the fractional error and increases milliseconds by 2 instead of 1 roughly every 43 milliseconds: internally, it keeps track of the 'fractional parts' of a millisecond so it's not exactly every 43 counts that this happens. If you have a crystal and not a resonator (and don't have interrupts disabled), then it's very accurate long term, but it stutters/jitters roughly every 43 counts.
Note that it would be perfectly possible to set Timer0 to reset back to 0 at a count of 249 instead of 255 - and this would make millis() dead accurate without needing the fudge - but the Arduino also uses Timer0 for micros() and for driving some PWM pins - resetting it every 250 counts would make those functions more complex and the PWM would have lower resolution.
If you're not using Timer2 for anything else then you can use that to get more accurate timing of your pulses (one microsecond resolution instead of four). I posted a thread somewhere on here that shows you how to get accurateMicros() - it works just like micros() except that it has four times the resolution.
And by careful manipulation of the timers, you can get timings accurate to one-sixteenth of a microsecond. You'll need to read and understand the datasheet if you want to exploit the hardware fully.
-
- Posts: 330
- Joined: 31 Jan 2019, 11:48
- Location: Boskoop, Netherlands
Re: Help needed
Thanks again Martin. I can't say I can follow everything you wrote (in the bit where sane people shouldn't go ) but I appreciate your efforts nevertheless.
I've had enough for today, I will continue testing/developing tomorrow.
Cheers,
Max.
I've had enough for today, I will continue testing/developing tomorrow.
Cheers,
Max.
- Wayne_H
- Posts: 811
- Joined: 17 Feb 2018, 05:26
- Location: Temora, NSW. Australia
- Contact:
Re: Help needed
I confess I too read it, ergo I'm not "most people" and/or not sane - in reality, it's probably both, but hey, we're happy
Cheers,
Wayne
Once a Retrobate, always a Retrobate............
Wayne
Once a Retrobate, always a Retrobate............
- Mike_K
- Posts: 676
- Joined: 16 Feb 2018, 06:35
- Location: Hertfordshire
Re: Help needed
Hi Martin
I was always under the impression that delayMicroseconds() used TCNT0 as well, until I had to read the source code for another issue. it doesn't use TCNT0, in fact, it doesn't use any timers at all and the pause is based on looping avrlib's delay_us() function. If you want to see this for yourself, look in wiring.c where you'll see the assembler calls, basically just loops of 1uS assembler delay. So its resolution is actually 1uS, not 4uS and the maximum value that works reliably is 16383. Your description is how micros() work.
If you want to prove it doesn't use interrupts or TCNT0 just try the following sketch which disables interrupts and TCNT0 and then flashes the built-in LED every second.
Cheers Mike
I was always under the impression that delayMicroseconds() used TCNT0 as well, until I had to read the source code for another issue. it doesn't use TCNT0, in fact, it doesn't use any timers at all and the pause is based on looping avrlib's delay_us() function. If you want to see this for yourself, look in wiring.c where you'll see the assembler calls, basically just loops of 1uS assembler delay. So its resolution is actually 1uS, not 4uS and the maximum value that works reliably is 16383. Your description is how micros() work.
If you want to prove it doesn't use interrupts or TCNT0 just try the following sketch which disables interrupts and TCNT0 and then flashes the built-in LED every second.
Cheers Mike
- Attachments
-
- delayMicroseconds.ino
- (742 Bytes) Downloaded 113 times