Preamble
Following on from Banging noises…, I attempted to recreate the Noise Chip (U4) depicted in the LDB-1 schematic, from the article Anatomy of a drum machine. The noise chip creates both a white noise signal as well as four tuned square waves at different (unknown) frequencies. I have attempted to contact the author for more details but have not had a reply as yet.
It is using either PIC (which is likely seeing as the author states a preference for the PIC microcontrollers), or an ATtiny. I will attempt to use a ATtiny/ATmega microcontroller (specifically ATmega48P/88P/168P/328P but the code should compile for most Arduinos).
Note that parts of this are a work in progress.
Note: The phase-correct 50% trick is possible with Timer0 on an ATtiny85, but it is not clear if it is possible with Timer1 (although Timer0 does have two channels). Atmel AVR ATtiny comparison chart
See also
Links
- AVR 8/16 Bit Timers/Counters – Tutorial #11 – not so useful
- ATtiny85: Introduction to Pin Change and Timer Interrupts – quite useful
- Secrets of Arduino PWM – the most useful
Very informative AVR tutorials
- AVR forum
Stack exchange related links
- Stack exchange
DR-110 Links
- DR-110 clap triggering – AVR code is mentioned but not supplied. Voicing board schematic also provided
- Related to the above clap triggering is BOSS DR-110 mods, features some PIC code (source and HEX) for a PICAXE for the clap triggering. Also has schematic of the lower part of the voicing board
- An analysis of the DR-110 Cymbal, Hi-Hat and Clap, with suggested modifications and proposed clones – very useful
- Anatomy of a drum machine – original LDR-1 article
- Boss DR-110 and C Y – voicing board schematic and other links
- DR-110 Dr Rhythm Graphic – looks promising but mostly dead links
- DR-110 mods – meh, could be useful to someone, but not particularly relevant in this case
Notes
- We need a ATtiny that has 5 outputs, so that discounts the ATtiny13, ATtiny15 – Is this true? They have five outputs, two of which are PWM.
- The square wave frequencies are probably not much higher than 100 kHz, so a 1 MHz internal clock is probably sufficient – unless precise frequencies are required (which they probably are, to maintain a quality of note – although no notes are produced, just percussion, so maybe not so important after all.
What is being reproduced?
The noise chip IC, U4, in the LDB-1 schematic is undocumented:

This schematic was the original physical circuit which the Noise Chip (U4) replaces

The article states that four tuned square waves are produced (at points 1-4 indicated in the above schematic). No more information other than that. Frequency could be calculated from the values of the resistors and capacitors. However, the value of the capacitors is not shown.
The white noise is produced by a 16 bit LFSR. In hardware versions, the CD4006 (18 stage register) and a CD4070 XOR are used (see LFSR).
Further reading
After a bit of a search, I found this excellent article, An analysis of the DR-110 Cymbal, Hi-Hat and Clap, with suggested modifications and proposed clones that has a slightly different schematic

It also, thankfully, has a table from the original DR-110 documentation:

The four frequencies are:
- 1.15 kHz
- 820 Hz
- 317 Hz
- 465 Hz
At the very end of that article (it runs for three pages), there is even an image of the noise chip, which is a PIC (and is available for purchase), with the respective frequencies!

From Boss DR-110 and C Y, the full schematic of the Voicing board,

Sources
White noise
This white noise, assembler code for ATtiny13 and ATtiny15, Noise Generator with ATTiny15.
Other sources were used, as can be seen at the end of the article.
Square waves
from Program an ATTiny13 as an audio oscillator with variable frequency and pulse-width
Note: The choice of 0b100
for WGM0[2,1,0]
won’t set CTC mode. (In fact it’ll set a mode that’s reserved by Atmel.) The ATtiny13 datasheet says CTC mode needs value 2; you’ve accidentally given it _bit_number_ 2 instead (i.e. value 4). Because of that it’s also necessary not only to change (i.e. clear) WGM02
in TCCR0B
but also to set bits WGM01
and WGM00
to 1 and 0 respectively. Those bits are in TCCR0A
, so it’s not sufficient to set TCCR0A
with only the COM0A
bits.
Attempt#1 – Using CTC hardware
The main setup code from Newbie’s Guide to AVR Timers – Part Six – Pure Hardware CTC. Uses bit 5 of port D, pin D5 (Digital pin 5)
#include #include int main (void) { DDRD |= (1 << 5); // Set LED as output TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode OCR1A = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64 TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 for (;;) { } }
The Interrupt Service routine, from Newbie’s Guide to AVR Timers – Part Five – CTC Mode using Interrupts:
ISR (TIMER1_COMPA_vect) { PORTB ^= (1 << 0); // Toggle the output }
Note: The ISR routine is not required for the hardware only version above.
Using the #include
lines from ATtiny85: Introduction to Pin Change and Timer Interrupts
#define F_CPU 1000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h>
Putting it together gives
#define F_CPU 1000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> int main (void) { DDRD |= (1 << 5); // Set digital pin 5 as output TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TCCR1A |= (1 << COM1A0); // Enable timer 1 Compare Output channel A in toggle mode OCR1A = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64 TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 for (;;) { } } ISR (TIMER1_COMPA_vect) { PORTB ^= (1 << 0); // Toggle the output }
This compiles. But is it correct?
NOTE: As already stated, the ISR routine is not required for the hardware only version above.
More outputs
The Compare output ports are
PB3 = OC0 PD4 = OC1B PD5 = OC1A
See Port Registers for port to pin mappings;
The chips used on the Arduino board (the ATmega8 and ATmega168) have three ports:
- B (digital pin 8 to 13)
- C (analog input pins)
- D (digital pins 0 to 7)
Each port is controlled by three registers, which are also defined variables in the Arduino language. The DDR register, determines whether the pin is an INPUT or OUTPUT. The PORT register controls whether the pin is HIGH or LOW, and the PIN register reads the state of INPUT pins set to input with
pinMode()
.
To add extra timers, need to enable more outputs. Currently we have
DDRD |= (1 << 5); // Set digital pin 5 as output
so we add
DDRD |= (1 << 4); // Set digital pin 4 as output
However, it is not clear, yet unlikely that timer1 can handle two different frequencies (i.e. to different compare values), so we could just enable both and use the second output through a divide by two circuit (using a D type flip-flop).
So presumably we just add
TCCR1A |= (1 << COM1B0); // Enable timer 1 Compare Output channel B in toggle mode
I am not sure if that works correctly, as expected, with both outputs (digital pin 4 and 5 toggling).
Note
From Configuring Timer1 for OCR1A and OCR1B interrupts
- It is NOT possible to use two different compare values for A and B on Timer1, as the timer resets when it encounters the lower value compare – thus the higher compare value is never reached.
- Just use one Timer and the rest in software:
Generally, that is what most would do — have one base timer tick and accumulate into “soft timers” for other intervals.
However, this thread, Two timers, three frequencies–ideas? suggests that two different values, on Timer2 are possible.
From Secrets of Arduino PWM – Using the ATmega PWM registers directly. Actually use Ken Schirriff’s original page, as the Arduino page is ridiculously buggy as next to totally useless
The two outputs for each timer will normally have the same frequency, but can have different duty cycles (depending on the respective output compare register).
It is not clear if this applies to the CTC counters though. It may only apply to PWM timers. I haven’t fully understood, and will revisit later.
NOTE: I have given up using this method for the moment as it is not clear if enough different frequencies are possible, without using soft timers. It maybe that only three frequencies are possible using this hardware method.
Attempt #2 – Using PWM timers
From Secrets of Arduino PWM – Varying the timer top limit: fast PWM and Varying the timer top limit: phase-correct PWM, it seems relatively easy to get two square waves with different frequencies (albeit with 50% difference).
Fast PWM Mode with OCRA top
Using fast PWM Mode with OCRA top.
Where
- We are using Timer2 and the associated pins 3 and 11
- The waveform generation mode bits
WGM
are set to to111
for fast PWM with OCRA controlling the top limit
int main() { pinMode(3, OUTPUT); // Set as output pinMode(11, OUTPUT); // Set as output TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22); // prescale 64 OCR2A = 180; // top limit OCR2B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) }
Giving
- Output A frequency: 16 MHz / 64 / (180+1) / 2 = 690.6Hz
- Output A duty cycle: 50%
- Output B frequency: 16 MHz / 64 / (180+1) = 1381.2Hz
- Output B duty cycle: (50+1) / (180+1) = 28.2%
Phase-correct PWM with OCRA top
Using phase-correct PWM with OCRA top,
Where
- The waveform generation mode bits
WGM
are set to to101
for phase-correct PWM with OCRA controlling the top limit
int main() { pinMode(3, OUTPUT); // Set as output pinMode(11, OUTPUT); // Set as output TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); // Toggle OC2A on Compare Match and, // Clear OC2B on Compare Match when up-counting. Set OC2B on Compare Match when down-counting. // and phase-correct TCCR2B = _BV(WGM22) | _BV(CS22); // phase-correct and prescale 64 OCR2A = 180; // top limit OCR2B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) }
Giving
- Output A frequency: 16 MHz / 64 / 180 / 2 / 2 = 347.2Hz
- Output A duty cycle: 50%
- Output B frequency: 16 MHz / 64 / 180 / 2 = 694.4Hz
- Output B duty cycle: 50 / 180 = 27.8%
As phase-correct gives the most symmetrical waveform, we will continue with this code. However, Fast PWM, as the name suggests is faster.
Expansion
The Timers and their associated pins are as follows (from Timer Interrupts and PWM Pins):
– Pins 5 and 6: controlled by Timer 0
– Pins 9 and 10: controlled by timer 1
– Pins 11 and 3: controlled by timer 2
Note that the prescalar for Timer0 (and Timer1) differs from that of Timer2 and CS02
gives a prescale of 256 en lieu of 64.
So, expanding the phase-correct version for Timer0, and its pins and registers
int main() { pinMode(3, OUTPUT); // Set as output - Timer2 pinMode(11, OUTPUT); // Set as output - Timer2 TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); // Toggle OC2A on Compare Match and, // Clear OC2B on Compare Match when up-counting. Set OC2B on Compare Match when down-counting. // and phase-correct TCCR2B = _BV(WGM22) | _BV(CS22); // phase-correct and prescale 64 OCR2A = 180; // top limit OCR2B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) pinMode(5, OUTPUT); // Set as output - Timer0 pinMode(6, OUTPUT); // Set as output - Timer0 TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(WGM00); TCCR0B = _BV(WGM02) | _BV(CS02); // prescale 256 OCR0A = 180; // top limit OCR0B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) }
Similarly, expanding for Timer1, and its pins and registers
int main() { pinMode(3, OUTPUT); // Set as output - Timer2 pinMode(11, OUTPUT); // Set as output - Timer2 TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22); // prescale 64 OCR2A = 180; // top limit OCR2B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) pinMode(5, OUTPUT); // Set as output - Timer0 pinMode(6, OUTPUT); // Set as output - Timer0 TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(WGM00); TCCR0B = _BV(WGM02) | _BV(CS02); // prescale 256 OCR0A = 180; // top limit OCR0B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) pinMode(9, OUTPUT); // Set as output - Timer1 pinMode(10, OUTPUT); // Set as output - Timer1 TCCR1A = _BV(COM1A0) | _BV(COM1B1) | _BV(WGM10); TCCR1B = _BV(WGM12) | _BV(CS12); // prescale 256 OCR1A = 180; // top limit OCR1B = 50; //arbitrary duty cycle for output B (output A is fixed at 50%) }
So this gives us six different frequencies… however, we only need four and we have to add a white noise generator. Also, we actually have three pairs the same frequencies (347.2Hz and 694.4Hz). Actually… that is not quite true, as we have already noted, the prescale bits for Timer2 differ from those of Timer0 and Timer1. Setting CS02
and CS12
give a prescale of 256, not 64. So, Timer0 and Timer1 are producing 86.8 Hz and 173.6 Hz.
If we discount one of the timers, lets say Timer1, as we make Timer2 four times as fast as Timer0, by first:
- adjusting (reducing from 180) the
OCR
for both Timer0 and Timer2 to give us a higher base frequency, and then; - changing the prescalar value of Timer0,
CS02:0
, from 64 (011
) to 256 (100
) – As it has turned out, we already are using a prescalar of 256 for Timer0, so that part of the code remains unchanged
then we should have 4 different frequencies, each with a frequency 50% of next, i.e., 8 kHz, 4 kHz, 2 kHz, and 1 kHz.
Note that it is not yet clear what frequencies, or range of frequencies, are required for the square waves. Nor is it set, what frequency the ATmega (or ATtiny) is running at – 16 MHz or 1 MHz. Lets assume that we are using a 16 MHz clock and the required base frequency (the highest) is 8 kHz (it will probably be nearer 16-20 kHz).
The prescalar values for:
- Timer0 (1, 8, 64, 256, 1024) are given in TABLE 12-9 of the AVR ATmega328P datasheet – page 110
- Timer1 (1, 8, 64, 256, 1024) are given in TABLE 13-5 of the AVR ATmega328P datasheet – page 137
- Timer2 (1, 8, 32, 64, 128, 256, 1024) are given in TABLE 15-9 of the AVR ATmega328P datasheet – page 162
The relevant sections of the AVR ATmega328P datasheet are:
- 12. 8-bit Timer/Counter0 with PWM
- 13. 16-bit Timer/Counter1 with PWM
- 14. Timer/Counter0 and Timer/Counter1 Prescalers
- 15. 8-bit Timer/Counter2 with PWM and Asynchronous Operation
Most Arduinos run at 16 MHz. Thus, for a required frequency (Freq) of 8 kHz and an assumed clock frequency (Fclock) of 16 MHz, then calculating:
OCR
= Fclock/Freq/prescalar/2 = 16M/8K/64/2 = 15.625
Correction: Need to divide by a further two, so
OCR
= Fclock/Freq/prescalar/2/2 = 16M/8K/64/2/2 = 7.8125
So, ignoring the exact OCR
value but using the approximate value of 16 [no, 8] for a 16 MHz clock gives 8 kHz base (and OCRxB
set to 8 [no, 4] for a 50% duty cycle), the code now becomes
int main() { pinMode(3, OUTPUT); // Set as output - Timer2 pinMode(11, OUTPUT); // Set as output - Timer2 TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22); // prescale 64 OCR2A = 8; // top limit for 8 kHz OCR2B = 4; // 50% duty cycle for output B (output A is fixed at 50%) pinMode(5, OUTPUT); // Set as output - Timer0 pinMode(6, OUTPUT); // Set as output - Timer0 TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(WGM00); TCCR0B = _BV(WGM02) | _BV(CS02); // prescale 256 OCR0A = 8; // top limit for 8 kHz OCR0B = 4; // 50% duty cycle for output B (output A is fixed at 50%) }
Prescalars for other AT microcontrollers
NOTE: for the ATtiny 85, the prescale values are given in Table 12-5 of the Atmel ATtiny25, ATtiny45, ATtiny85 Datasheet – Microchip Technology – page 89, and for Timer1 0111
gives 64. Table 11-6 (page 80) for Timer 0 011
gives 64. See also Table 13-3 page 99 (1011
) for ATtiny15 mode.
NOTE: for the ATtiny13, Table 11-9 of the ATtiny13 datasheet – page 73, and for Timer0 011
gives 64
White Noise Generator
Implementing a Linear Feedback Shift Register (LFSR) in Arduino isn’t too hard. From Linear-feedback shift register (LFSR) dengan arduino, there are two sketches:
- Fibonacci
- Galois
Fibonacci:
uint16_t LFSRBuffer; void setup() { Serial.begin(9600); Serial.println("LFSR metode Fibonacci"); Serial.println("http://www.semesin.com/project"); uint16_t nilaiAwal = analogRead(0); LFSRFibonacci(nilaiAwal); //LFSRFibonacci(100); } void loop() { uint16_t bilanganLFSR = LFSRFibonacci(); Serial.println(bilanganLFSR); delay(1000); } void LFSRFibonacci(uint16_t nilaiAwal) { LFSRBuffer = nilaiAwal; } uint16_t LFSRFibonacci() { uint16_t bit; bit = ((LFSRBuffer >> 0) ^ (LFSRBuffer >> 2) ^ (LFSRBuffer >> 3) ^ (LFSRBuffer >> 5)) & 1; LFSRBuffer = (LFSRBuffer >> 1) | (bit << 15); return LFSRBuffer; }
Galois
uint16_t LFSRBuffer; void setup() { Serial.begin(9600); Serial.println("LFSR metode Galois"); Serial.println("http://www.semesin.com/project"); uint16_t nilaiAwal = analogRead(0); LFSRGalois(nilaiAwal); //LFSRGalois(100); } void loop() { uint16_t bilanganLFSR = LFSRGalois(); Serial.println(bilanganLFSR); delay(1000); } void LFSRGalois(uint16_t nilaiAwal) { LFSRBuffer = nilaiAwal; } uint16_t LFSRGalois() { unsigned lsb = LFSRBuffer & 1; LFSRBuffer >>= 1; if (lsb) { LFSRBuffer ^= 0xB400; } return LFSRBuffer; }
However, these two sketches return 16 bit values, and they need to be 8 bit to output to a pin.
Or use a Random Number Generator library, such as Iniesta8/yapRNG.
Or from Audio Frequency White Noise generation using Arduino Mini Pro
Fibonacci
void generateNoise(){ unsigned long int reg, newr; unsigned char lobit; unsigned char b31, b29, b25, b24; const int speakerPin = 8; b31 = (reg & (1L <> 31; b29 = (reg & (1L <> 29; b25 = (reg & (1L <> 25; b24 = (reg & (1L <> 24; lobit = b31 ^ b29 ^ b25 ^ b24; newr = (reg << 1) | lobit; reg = newr; digitalWrite (speakerPin, reg & 1); delayMicroseconds (50); // Changing this value changes the frequency. }
Galois – possibly faster than the fibonacci
#define speakerPin 8 unsigned long lastClick; void setup() { // put your setup code here, to run once: pinMode(speakerPin,OUTPUT); lastClick = micros(); } /* initialize with any 32 bit non-zero unsigned long value. */ #define LFSR_INIT 0xfeedfaceUL /* Choose bits 32, 30, 26, 24 from http://arduino.stackexchange.com/a/6725/6628 * or 32, 22, 2, 1 from * http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf * or bits 32, 16, 3,2 or 0x80010006UL per http://users.ece.cmu.edu/~koopman/lfsr/index.html * and http://users.ece.cmu.edu/~koopman/lfsr/32.dat.gz */ #define LFSR_MASK ((unsigned long)( 1UL<<31 | 1UL <<15 | 1UL <<2 | 1UL <<1 )) unsigned int generateNoise(){ // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs static unsigned long int lfsr = LFSR_INIT; /* 32 bit init, nonzero */ /* If the output bit is 1, apply toggle mask. * The value has 1 at bits corresponding * to taps, 0 elsewhere. */ if(lfsr & 1) { lfsr = (lfsr >>1) ^ LFSR_MASK ; return(1);} else { lfsr >>= 1; return(0);} } void loop() { /* ... */ if ((micros() - lastClick) > 50 ) { // Changing this value changes the frequency. lastClick = micros(); digitalWrite (speakerPin, generateNoise()); } }
Putting it all together
Now, we can join the square wave timer PWM code and LFSR sketches
const int speakerPin = 8; unsigned long lastClick; void setup() { // put your setup code here, to run once: pinMode(speakerPin,OUTPUT); lastClick = micros(); pinMode(3, OUTPUT); // Set as output - Timer2 pinMode(11, OUTPUT); // Set as output - Timer2 TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22); // prescale 64 OCR2A = 8; // top limit for 8 kHz OCR2B = 4; // 50% duty cycle for output B (output A is fixed at 50%) pinMode(5, OUTPUT); // Set as output - Timer0 pinMode(6, OUTPUT); // Set as output - Timer0 TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(WGM00); TCCR0B = _BV(WGM02) | _BV(CS02); // prescale 256 OCR0A = 8; // top limit for 8 kHz OCR0B = 4; // 50% duty cycle for output B (output A is fixed at 50%) } /* initialize with any 32 bit non-zero unsigned long value. */ #define LFSR_INIT 0xfeedfaceUL /* Choose bits 32, 30, 26, 24 from http://arduino.stackexchange.com/a/6725/6628 * or 32, 22, 2, 1 from * http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf * or bits 32, 16, 3, 2 or 0x80010006UL per http://users.ece.cmu.edu/~koopman/lfsr/index.html * and http://users.ece.cmu.edu/~koopman/lfsr/32.dat.gz */ #define LFSR_MASK ((unsigned long)( 1UL << 31 | 1UL << 15 | 1UL << 2 | 1UL << 1 )) // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs unsigned int generateNoise(){ static unsigned long int lfsr = LFSR_INIT; /* 32 bit init, nonzero */ /* If the output bit is 1, apply toggle mask. * The value has 1 at bits corresponding * to taps, 0 elsewhere. */ if(lfsr & 1) { lfsr = (lfsr >>1) ^ LFSR_MASK ; return(1);} else { lfsr >>= 1; return(0);} } void loop() { /* ... */ if ((micros() - lastClick) > 50 ) { // Changing this value changes the frequency. lastClick = micros(); digitalWrite (speakerPin, generateNoise()); } }
UPDATE: However, since writing the above code, I have discovered that the frequencies are not double of each other (see Further reading at the top), and so this technique will not work..!
However, if one was prepared to use approximations to the required frequencies (don’t forget that the original frequencies were probably approximations themselves, based on available capacitor/resistor values), then one could use the following frequency pairs:
- 1.15 kHz and 575 Hz
- 820 Hz and 410 Hz
Each of the lower frequencies is about 100 Hz too high.
OCR2A
= Fclock/Freq/prescalar/2/2 = 16M/1.15K/64/2/2 = 54.34 ≈ 53
OCR2B
= 27
OCR0A
= Fclock/Freq/prescalar/2/2 = 16M/820/64/2/2 = 76.219 ≈ 76
OCR0B
= 38
Note that the prescaler in this case for Timer0 is now 64 (011
) and not 256 (100
) as before.
setup()
now becomes
void setup() { // put your setup code here, to run once: pinMode(speakerPin,OUTPUT); lastClick = micros(); pinMode(3, OUTPUT); // Set as output - Timer2 pinMode(11, OUTPUT); // Set as output - Timer2 TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20); TCCR2B = _BV(WGM22) | _BV(CS22); // prescale 64 OCR2A = 53; // top limit for 8 kHz OCR2B = 27; // 50% duty cycle for output B (output A is fixed at 50%) pinMode(5, OUTPUT); // Set as output - Timer0 pinMode(6, OUTPUT); // Set as output - Timer0 TCCR0A = _BV(COM0A0) | _BV(COM0B1) | _BV(WGM00); TCCR0B = _BV(WGM02) | _BV(CS01) | _BV(CS00); // prescale 64 OCR0A = 76; // top limit for 8 kHz OCR0B = 38; // 50% duty cycle for output B (output A is fixed at 50%) }
UPDATE: There is an issue with using Timer0
, as it will conflict with the use of micros()
and lastClick()
, which is used in the LSFR. See Does millis() conflict with the PWM pins associated with timer 0? and Arduino uno PWM CTC mode – strange behaviour on interrupt. From Re: Timer Interrupts and PWM Pins:
Arduino uses TIMER0 for the timing. You should not change that. You can do whatever you want with TIMER1 and TIMER2. Some functions or libraries use those timers (pwm, tone(), and many more), so you have to be careful how they are used.
So while the square waves will operate as expected the LSFR will not. It would be necessary to switch to using Timer1
(as is done in Attempt#3).
Attempt#3 – Soft timers
Attempting to use a combination of the CTC hardware and soft timers, where the highest frequency is used as a base clock rate and then use software to divide the frequency for the other three signals.
Knowing the frequencies
- 1.15 kHz
- 820 Hz
- 317 Hz
- 465 Hz
and trying to determine a relationship between them and there doesn’t seem to be one. So, simple division of the highest clock rate will not work.
Taking an idea from Generating 2 different frequencies only on timer 1, it is necessary to find the least common multiple (LCM) of 1.15k, 820, 465, 317, and use a frequency equal to the LCM as the base clock rate. Then from that, in software divide that base clock rate to obtain all four square waves.
Finding the LCM could be done by hand, or using an online LCM calculator.
Using the online calculator:
For the values: 1150, 820, 465, 317
https://www.calculatorsoup.com/calculators/math/lcm.php?input=1150%2C+820%2C+465%2C+317&action=solve
LCM = 2780058300
LCM = 2,780,058,300
which is clearly not that much use, as the clock would need to be more than 5 GHz. Adjusting the figures to find a lower LCM
For the values: 1150, 820, 465, 316
https://www.calculatorsoup.com/calculators/math/lcm.php?input=1150%2C+820%2C+465%2C+316&action=solve
LCM = 692822100
LCM = 692,822,100
Still too high.
For the values:
1150, 820, 465, 320
https://www.calculatorsoup.com/calculators/math/lcm.php?input=1150%2C+820%2C+465%2C+320&action=solve
LCM = 140318400
LCM = 140,318,400
Still too high but getting lower
For the values:
1150, 820, 460, 320
https://www.calculatorsoup.com/calculators/math/lcm.php?input=1150%2C+820%2C+460%2C+320&action=solve
LCM = 1508800
LCM = 1,508,800
This value is just about usable with a 8/16 MHz clock. It would be of no use for a 1 MHz clock frequency though.
For the values:
1150, 820, 464, 320
https://www.calculatorsoup.com/calculators/math/lcm.php?input=1150%2C+820%2C+464%2C+320&action=solve
LCM = 43755200
With this last set of values we have gone off track again.
Taking the penultimate LCM of 1508800, we would need to set a hardware timer to obtain this and then in the ISR
create four counters that toggle their outputs at the following counts: 1312, 1840, 3280, and 4715
Using the example from Newbie’s Guide to AVR Timers – Part Five – CTC Mode using Interrupts:
#include #include int main (void) { DDRB |= (1 << 0); // Set LED as output TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt sei(); // Enable global interrupts OCR1A = 15624; // Set CTC compare value to 1Hz at 1MHz AVR clock, with a prescaler of 64 TCCR1B |= ((1 << CS10) | (1 << CS11)); // Start timer at Fcpu/64 for (;;) { } } ISR(TIMER1_COMPA_vect) { PORTB ^= (1 << 0); // Toggle the LED }
Note that the example in the link uses a 1 MHz clock, whereas we are using a 16 MHz
16M/1508800 = 10.604, so even further approximations with be made. Taking 11 as the nearest value, then that gives a base frequency of 1454545.45 (1.454545 MHz). Taking 10, gives 1.6 MHz (obviously).
To get 1.15 KHz, that base frequency would need dividing by 1264.822. Dividing 1454545.45 by 1264 gives a frequency of, 1150.747 Hz. Dividing 1454545.45 by 1265 gives a frequency of, 1149.838 Hz.
To get 820 Hz, that base frequency would need dividing by 1773.835. Dividing 1454545.45 by 1773 gives a frequency of, 820.386 Hz. Dividing 1454545.45 by 1774 gives a frequency of, 819.92 Hz.
To get 460 Hz, that base frequency would need dividing by 3162.055. Dividing 1454545.45 by 3162 gives a frequency of, 460.008 Hz. Dividing 1454545.45 by 3163 gives a frequency of, 459.862 Hz.
To get 320 Hz, that base frequency would need dividing by 4545.45. Dividing 1454545.45 by 4545 gives a frequency of, 320.03 Hz. Dividing 1454545.45 by 4546 gives a frequency of, 319.96 Hz.
All of these are pretty reasonable approximations. Of course, using the base frequency of 1454545.45 Hz, we can strive to obtain closer to the original lower two values (465 Hz and 316 Hz), which we had approximated to 460 Hz and 320 Hz, in order to determine the lowest feasible LCM.
To get to original 465 Hz, that base frequency would need dividing by 3128.0547. Dividing 1454545.45 by 3128 gives a frequency of, 465.008 Hz. Dividing 3129 by 3163 gives a frequency of, 464.859 Hz.
To get 316 Hz, that base frequency would need dividing by 4602.99. Dividing 1454545.45 by 4602 gives a frequency of, 316.068 Hz. Dividing 1454545.45 by 4603 gives a frequency of, 315.999 Hz.
So to recap: For a 16 MHz clock frequency, using Timer1
with a clock divider of 11 (without a prescalar, or rather a prescalar of 1 (001
) and then software dividers (base frequency divider values) of 1265, 1774, 3128, and 4603, we get pretty damn close to the original frequencies.
Setting up the CTC interrupt appropriately:
#include #include int main (void) { DDRB |= (1 << 0); // Set LED as output TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt sei(); // Enable global interrupts OCR1A = 15624; // Set CTC compare value to 1454545.45 Hz at 16 MHz AVR clock, with a prescaler of 1 TCCR1B |= (1 << CS10); // Start timer at Fcpu/1 for (;;) { } } ISR(TIMER1_COMPA_vect) { PORTB ^= (1 << 0); // Toggle the LED }
Note that in this example with have to:
- Enable global interrupts (
sei
), which we didn’t have to do in the previous Attempts (#1 and #2) - Enable the CTC interrupt
- Have an
ISR
(Interrupt Service Routine) - We are also setting the output differently
However, this, as it stands, only gives us the base frequency on digital pin 8. We have to add the software timers.
Again referring to Port Registers for port to pin mappings;
The chips used on the Arduino board (the ATmega8 and ATmega168) have three ports:
- B (digital pin 8 to 13)
we will use digital pins 8, 9, 10 and 11 for the square wave. These are set by setting bits 0-3 of DDRB
, the Port B Data Direction Register:
DDRB |= (1 << 0 | 1 << 1 | 1 << 2 | 1 << 3); // Set digital pins 8-11 as outputs
or
DDRB = DDRB | B01111; // Set digital pins 8-11 as outputs
Now for the software timers themselves, they are global integer variables, as they are used in the ISR:
int squarewave_1150 = 0; int squarewave_820 = 0; int squarewave_465 = 0; int squarewave_316 = 0;
and the ISR:
ISR(TIMER1_COMPA_vect) { squarewave_1150++; squarewave_820++; squarewave_465++; squarewave_316++; if (squarewave_1150 == 1265) { PORTB ^= (1 << 0); // Toggle the 1.15 KHz output squarewave_1150 = 0; } if (squarewave_820 == 1774) { PORTB ^= (1 << 1); // Toggle the 820 Hz output squarewave_820 = 0; } if (squarewave_465 == 3128) { PORTB ^= (1 << 2); // Toggle the 465 Hz output squarewave_465 = 0; } if (squarewave_316 == 4603) { PORTB ^= (1 << 3); // Toggle the 316 Hz output squarewave_316 = 0; } }
Now, there is an immediate problem. The compare values used (the base frequency divider values) will actually result in frequencies which are half of the expected values. Why? Because the toggling of the square wave output, when these values are reached, means that the period of the square wave is twice the length of that which we require.
So the compare values used for toggling the wave output are actually half of software base frequency divider values.
This will cause further inaccuracies in the frequencies, where the base frequency divider values are not exactly divisible by two, and means that we should probably consider decreasing or increasing the base frequency divider values, for the odd values, i.e. the base frequency divider values for 1.15 kHz and 316 Hz. Using 1264 for the 1.15 kHz and 4602 for 316 Hz (see the previous calculations above for the errors induced +0.474 Hz and +0.068 Hz, respectively).
So, dividing the base frequency divider values by two:
ISR(TIMER1_COMPA_vect) { squarewave_1150++; squarewave_820++; squarewave_465++; squarewave_316++; if (squarewave_1150 == 1264/2) { PORTB ^= (1 << 0); // Toggle the 1.15 KHz output squarewave_1150 = 0; } if (squarewave_820 == 1774/2) { PORTB ^= (1 << 1); // Toggle the 820 Hz output squarewave_820 = 0; } if (squarewave_465 == 3128/2) { PORTB ^= (1 << 2); // Toggle the 465 Hz output squarewave_465 = 0; } if (squarewave_316 == 4602/2) { PORTB ^= (1 << 3); // Toggle the 316 Hz output squarewave_316 = 0; } }
Putting it together, and defining some const int
constants for readability gives
const int BFDV_1150 = 1264; // base frequency divider value for 1.15 KHz const int BFDV_820 = 1774; // base frequency divider value for 820 Hz const int BFDV_465 = 3128; // base frequency divider value for 465 Hz const int BFDV_316 = 4602; // base frequency divider value for 316 Hz const int HalfPeriodCompare_1150 = BFDV_1150/2; // Half period toggle compare value for 1.15 kHz const int HalfPeriodCompare_820 = BFDV_820/2; // Half period toggle compare value for 820 Hz const int HalfPeriodCompare_465 = BFDV_465/2; // Half period toggle compare value for 465 Hz const int HalfPeriodCompare_316 = BFDV_316/2; // Half period toggle compare value for 316 Hz const int DigitalPin8 = 0; const int DigitalPin9 = 1; const int DigitalPin10 = 2; const int DigitalPin11 = 3; int squarewave_1150 = 0; int squarewave_820 = 0; int squarewave_465 = 0; int squarewave_316 = 0; int main (void) { DDRB |= (1 << DigitalPin8 | 1 << DigitalPin9 | 1 << DigitalPin10 | 1 << DigitalPin11); // Set digital pins 8-11 as outputs TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt sei(); // Enable global interrupts OCR1A = 15624; // Set CTC compare value to 1454545.45 Hz at 16 MHz AVR clock, with a prescaler of 1 TCCR1B |= (1 << CS10); // Start timer at Fcpu/1 for (;;) { } } ISR(TIMER1_COMPA_vect) { squarewave_1150++; squarewave_820++; squarewave_465++; squarewave_316++; if (squarewave_1150 == HalfPeriodCompare_1150) { PORTB ^= (1 << DigitalPin8); // Toggle the 1.15 KHz output squarewave_1150 = 0; } if (squarewave_820 == HalfPeriodCompare_820) { PORTB ^= (1 << DigitalPin9); // Toggle the 820 Hz output squarewave_820 = 0; } if (squarewave_465 == HalfPeriodCompare_465) { PORTB ^= (1 << DigitalPin10); // Toggle the 465 Hz output squarewave_465 = 0; } if (squarewave_316 == HalfPeriodCompare_316) { PORTB ^= (1 << DigitalPin11); // Toggle the 316 Hz output squarewave_316 = 0; } }
Adding in the noise generator
// Noise chip for LDB-1 using ATmega 328 const int BFDV_1150 = 1264; // base frequency divider value for 1.15 KHz const int BFDV_820 = 1774; // base frequency divider value for 820 Hz const int BFDV_465 = 3128; // base frequency divider value for 465 Hz const int BFDV_316 = 4602; // base frequency divider value for 316 Hz const int HalfPeriodCompare_1150 = BFDV_1150/2; // Half period toggle compare value for 1.15 kHz const int HalfPeriodCompare_820 = BFDV_820/2; // Half period toggle compare value for 820 Hz const int HalfPeriodCompare_465 = BFDV_465/2; // Half period toggle compare value for 465 Hz const int HalfPeriodCompare_316 = BFDV_316/2; // Half period toggle compare value for 316 Hz const int DigitalPin8 = 0; const int DigitalPin9 = 1; const int DigitalPin10 = 2; const int DigitalPin11 = 3; int squarewave_1150 = 0; int squarewave_820 = 0; int squarewave_465 = 0; int squarewave_316 = 0; const int speakerPin = 8; unsigned long lastClick; /* initialize with any 32 bit non-zero unsigned long value. */ #define LFSR_INIT 0xfeedfaceUL /* Choose bits 32, 30, 26, 24 from http://arduino.stackexchange.com/a/6725/6628 * or 32, 22, 2, 1 from * http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf * or bits 32, 16, 3, 2 or 0x80010006UL per http://users.ece.cmu.edu/~koopman/lfsr/index.html * and http://users.ece.cmu.edu/~koopman/lfsr/32.dat.gz */ #define LFSR_MASK ((unsigned long)( 1UL << 31 | 1UL << 15 | 1UL << 2 | 1UL << 1 )) // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs unsigned int generateNoise(){ static unsigned long int lfsr = LFSR_INIT; /* 32 bit init, nonzero */ /* If the output bit is 1, apply toggle mask. * The value has 1 at bits corresponding * to taps, 0 elsewhere. */ if(lfsr & 1) { lfsr = (lfsr >>1) ^ LFSR_MASK ; return(1);} else { lfsr >>= 1; return(0);} } int main (void) { pinMode(speakerPin,OUTPUT); lastClick = micros(); DDRB |= (1 << DigitalPin8 | 1 << DigitalPin9 | 1 << DigitalPin10 | 1 << DigitalPin11); // Set digital pins 8-11 as outputs TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt sei(); // Enable global interrupts OCR1A = 15624; // Set CTC compare value to 1454545.45 Hz at 16 MHz AVR clock, with a prescaler of 1 TCCR1B |= (1 << CS10); // Start timer at Fcpu/1 for (;;) { if ((micros() - lastClick) > 50 ) { // Changing this value changes the frequency. lastClick = micros(); digitalWrite (speakerPin, generateNoise()); } } } ISR(TIMER1_COMPA_vect) { squarewave_1150++; squarewave_820++; squarewave_465++; squarewave_316++; if (squarewave_1150 == HalfPeriodCompare_1150) { PORTB ^= (1 << DigitalPin8); // Toggle the 1.15 KHz output squarewave_1150 = 0; } if (squarewave_820 == HalfPeriodCompare_820) { PORTB ^= (1 << DigitalPin9); // Toggle the 820 Hz output squarewave_820 = 0; } if (squarewave_465 == HalfPeriodCompare_465) { PORTB ^= (1 << DigitalPin10); // Toggle the 465 Hz output squarewave_465 = 0; } if (squarewave_316 == HalfPeriodCompare_316) { PORTB ^= (1 << DigitalPin11); // Toggle the 316 Hz output squarewave_316 = 0; } }
That’s it in main()
format. Using setup()
and loop()
gives us:
// Noise chip for LDB-1 using ATmega 328 const int BFDV_1150 = 1264; // base frequency divider value for 1.15 KHz const int BFDV_820 = 1774; // base frequency divider value for 820 Hz const int BFDV_465 = 3128; // base frequency divider value for 465 Hz const int BFDV_316 = 4602; // base frequency divider value for 316 Hz const int HalfPeriodCompare_1150 = BFDV_1150/2; // Half period toggle compare value for 1.15 kHz const int HalfPeriodCompare_820 = BFDV_820/2; // Half period toggle compare value for 820 Hz const int HalfPeriodCompare_465 = BFDV_465/2; // Half period toggle compare value for 465 Hz const int HalfPeriodCompare_316 = BFDV_316/2; // Half period toggle compare value for 316 Hz const int DigitalPin8 = 0; const int DigitalPin9 = 1; const int DigitalPin10 = 2; const int DigitalPin11 = 3; int squarewave_1150 = 0; int squarewave_820 = 0; int squarewave_465 = 0; int squarewave_316 = 0; const int speakerPin = 8; unsigned long lastClick; const int kNoiseFrequency = 50; /* initialize with any 32 bit non-zero unsigned long value. */ #define LFSR_INIT 0xfeedfaceUL /* Choose bits 32, 30, 26, 24 from http://arduino.stackexchange.com/a/6725/6628 * or 32, 22, 2, 1 from * http://www.xilinx.com/support/documentation/application_notes/xapp052.pdf * or bits 32, 16, 3, 2 or 0x80010006UL per http://users.ece.cmu.edu/~koopman/lfsr/index.html * and http://users.ece.cmu.edu/~koopman/lfsr/32.dat.gz */ #define LFSR_MASK ((unsigned long)( 1UL << 31 | 1UL << 15 | 1UL << 2 | 1UL << 1 )) // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs unsigned int generateNoise(){ static unsigned long int lfsr = LFSR_INIT; /* 32 bit init, nonzero */ /* If the output bit is 1, apply toggle mask. * The value has 1 at bits corresponding * to taps, 0 elsewhere. */ if(lfsr & 1) { lfsr = (lfsr >>1) ^ LFSR_MASK ; return(1);} else { lfsr >>= 1; return(0);} } void setup (void) { pinMode(speakerPin,OUTPUT); lastClick = micros(); DDRB |= (1 << DigitalPin8 | 1 << DigitalPin9 | 1 << DigitalPin10 | 1 << DigitalPin11); // Set digital pins 8-11 as outputs TCCR1B |= (1 << WGM12); // Configure timer 1 for CTC mode TIMSK1 |= (1 << OCIE1A); // Enable CTC interrupt sei(); // Enable global interrupts OCR1A = 15624; // Set CTC compare value to 1454545.45 Hz at 16 MHz AVR clock, with a prescaler of 1 TCCR1B |= (1 << CS10); // Start timer at Fcpu/1 } void loop() { if ((micros() - lastClick) > kNoiseFrequency ) { // Changing this value changes the frequency. lastClick = micros(); digitalWrite (speakerPin, generateNoise()); } } ISR(TIMER1_COMPA_vect) { squarewave_1150++; squarewave_820++; squarewave_465++; squarewave_316++; if (squarewave_1150 == HalfPeriodCompare_1150) { PORTB ^= (1 << DigitalPin8); // Toggle the 1.15 KHz output squarewave_1150 = 0; } if (squarewave_820 == HalfPeriodCompare_820) { PORTB ^= (1 << DigitalPin9); // Toggle the 820 Hz output squarewave_820 = 0; } if (squarewave_465 == HalfPeriodCompare_465) { PORTB ^= (1 << DigitalPin10); // Toggle the 465 Hz output squarewave_465 = 0; } if (squarewave_316 == HalfPeriodCompare_316) { PORTB ^= (1 << DigitalPin11); // Toggle the 316 Hz output squarewave_316 = 0; } }
Note that the #includes
from Attempt#1 are not actually required.
That’s all folks! Happy noise making…