Intro: Arduino Tone Generator With No Library or Serial Functions (With Interrupts)
This isn't something I'd normally make an instructable on, I prefer my metalwork, but as I am an electrical engineering student and have to take a class on microcontrollers (Embedded Systems Design), I thought I'd make an instructable on one of my projects. When I originally made the project and others for this class, I found that there are very few to no tutorials that don't use the arduino library functions or serial functions, which is another reason I thought this would be a good instructable.
This code is designed for the Atmega 2560 microcontroller, so if you want to implement it on another board you'll need to change the address registers in the code based on your controllers user manual. The basic idea behind the code is that whenever you enter a key on the keyboard into the serial monitor, the arduino mega will output a certain frequency based on what key you press, with "q" resetting it. I made it so that "a" will output the A flat frequency and "A" will output the A sharp frequency, "b" outputting B flat, "c" for C flat, "C" for C sharp, and so on. The full code is uploaded at the end, but each step will break down the code into pieces so it's easier to explain.
Step 1: Defining Register Addresses
This step is easy, if you're using the atmega 2560, you just need to use the addresses I used, although if you use a board with a different chip, you'll need to find the addresses for each of these registers on your chips' user manual. The definitions at the top are just constants that will be used for our functions later on. We specify the addresses as volatile unsigned because we don't want the compiler to mess with them.
Step 2: Arrays and Global Variables
Here we want to define the Frequency array which will contain all of the frequencies that each key should output. These values are calculated from the actual note frequencies, and honestly I forgot how I got them, but they are the right values as I tested them on an oscilloscope to make sure. We are also defining the notes array which contains all of the keys to press for each tone, as well as the variables we'll need for our later functions.
Step 3: The "serial.begin" Function
We'll call our custom function that replicates the "serial.begin" function U0init(). It takes the desired baudrate as input and starts the serial port at that baudrate.
Step 4: The "serial.available" Function
We'll call the function that imitates "serial.available" U0kbhit(). It doesn't take input but instead detects if there is a change made on the keyboard using the RDA status bit and returns true when a change is detected.
Step 5: The "serial.read" Function
We'll call the function that imitates the "serial.read" function U0getchar(), which takes no input and outputs whatever change is made on the keyboard, which is stored in the UDR0 register.
Step 6: The "serial.write" Function
We'll call the function that imitates "serial.write" U0putchar(), which takes the data from the UDR0 register while a change is detected and stored, and outputs that change back to the serial monitor.
Step 7: The Setup Function
This is the basic setup function that will use our "serial.begin" imitation to initialize the serial port, and will initialize our bit settings for the timer registers and set PB6 to output our tones.
Step 8: The Loop and ISR Functions
The loop functions as so: if a change is detected with our "serial.available" function, our "serial.read" function stores that change, and our "serial.write" function puts that change into the serial monitor. As long as a variable i is less than the size of the frequency array, it will set the output to be the position of i in that array, outputting the frequency at that position. The ISR functions as the reset, where if the frequency array position does not equal 0 (in other words if "q" is not pressed), it will output the frequency, but when "q" is pressed it will reset.
Please note: this code uses interrupts, but it can be done with interrupts disabled. I'll post the code without interrupts if I get any requests for it, I just think the interrupt version is more fun.
Step 9: Wiring
The wiring for this code is extremely easy, simply put an output wire from PB6 to a breadboard, connect a buzzer or speaker in series with that, and connect it back to ground. Note: if you use a speaker, put a small resistor in before the speaker. If you just want to see the output but not hear it, just connect PB6 to the red lead of an oscilloscope and the black lead to ground.
Step 10: Putting It All Together!
I added the full code to this step, since I've explained all of the parts of it in previous steps. It just takes a keyboard input for different frequencies and outputs that frequency to PB6. Hope you enjoyed reading a different way to code with the IDE!
Also, please vote for this in the Microcontroller contest :D