How to combine two 8-bit PWM output into one 16-bit DAC?
Single PWM output can use one output pin to generate analog signals with 8-bit resolution (as we learned in LAB2). However, we can simply create another analog signal via another PWM output channel. Then we can make this second analog signal represent lower order bits via selecting correct summing ratio.
In this project, the PWM Combination circuit is composed of two resitors: 1M and 3.9K. And the summing ratio is about 8:1. The larger resistor is connected to LSB PWM output channel while the smaller resistor is connected to MSB PWM channel. In this way, when MSB PWM outputs 0V while LSB PWM outputs 5V, we can calculate the minimum resolution is about 0.01942V. Assume resistor accuracy (tolerance) is 1%, then the actual output should be: 0.0191V < V < 0.0199V. The deviation range is only 800uV. It is obvious that if the resistor accuracy can be up to 0.3%, then it is able to achieve full 16-bit resolution.
You can check 16-Bit DAC / PWM on Arduino UNO - Ec-Projects for more information.
How to Access SD Card
SD card can only accept 3.3V operating voltage level. Even though ATmega1284p can also work at 3.3V, it may not be able to work at 16MHz ("Atmel-8272-8-bit-AVR-microcontroller-ATmega164A_PA-324A_PA-644A_PA-1284_P_datasheet", pp 336, Figure 28-1). To ensure system stability, ATmega1284p is powered by 5V while SD card is powered by 3.3V with a cheap, linear regulator such as LT1117-3V3.
To interact with SD card correctly, resistor dividers (3.3K and 1.8K) are used to guarantee AVR's SPI signal level would never exceed 3.3V. Note that, for MISO signal, 3.3V is acceptable by AVR, so it does not need extra resistor divider.
Petit-FatFs File System
This light-weight / open-source file system is developed by ChaN, which support FAT12/16/32. Then we can access SD card following file system standard. Note that if you need to write SD card efficiently, than you should use FatFs (also developed by ChaN) NOT Petit-FatFs.
Audio Data Process and Software Utilization
Basically, audio data process can be divided into "Retrieving" and "Consuming" process. To guarantee the audio quality, the code in sampling ISR must be as short as possible as is shown above.
In order to implement the methodology above, 4 independent buffers are created based on the data structure of 16-bit Wave File. All these four buffers share the same "Head" and "Tail" pointer. The buffer length is 256 bytes and the pointers are also 8-bit - just let it overflow to zero when it exceeds 255, then we don't need to do extra compare operations.
Meanwhile, since we access these two variables frequently. I put them in register. For instance, in AVR Studio 6.2.1502 - Service Pack 1, you can define the variable in this way "register uint8_t Buff_Head asm("r2")", please note that it should be safe to use r2 through r7. Registers r8 through r15 can be used for argument passing by the compiler in case many or long arguments are being passed to callees. If this is not the case throughout the entire application, these registers could be used for register variables as well. Extreme care should be taken that the entire application is compiled with a consistent set of register-allocated variables, including possibly used library functions. (Referred from "Register Variable ATMEL").
On the other side, when data is retrieved from SPI, it will go to specified buffer directly without extra judgement statement. I can do this since I implement a function pointer which is initialized to one of four different data buffer allocation functions as soon as the type of wave file is determined.
Except for utilization above, some other strategies are also applied to execute the key scan routine. You can check the project link for my detail report on this project.