My R2R DAC...



(Pictures of the mmc module taken through a high resolution camera :-))

Features:

  • TV remote control to control the player remotely.
  • Hi quality audio output
  • Maximum bit rate supported - 144 kilobytes/second.
  • Stereo support
  • Automatic repeat from the top after all the songs are played.
  • Additional bitrate adjustment on remote ( << , default, >> ).
  • Forwarding (fwd) option while playing. (seconds could be set on a macro).
(more features will be added on next update)


A brief explanation about the working:


MMC card initialization, reading, writing and interfacing are already explained on my previous post
http://vinodstanur.blogspot.com/2011/07/attempt-to-access-memory-card-mmc-using.html
, so I think no need to repeat in again. Any way, in short words, MMC/SD cards are to be initialized by proper commands and it enters to working mode (SPI mode) only if it is initialized successfully.
So, here I used a 16x2 lcd to display many things. At first, it displays error message (if any) while trying to initialize the mmc. When it is initialized successfully, it shows MMC INITIALIZED message on lcd..
Now, the next step is to check the boot sector of the MMC card (sector 0) to check if it contains a FAT16 file system. For this, we need to read the sector 0 of MMC card to a buffer. Here I used a 512 byte buffer. From the boot sector data, we could see what file system is there in the MMC card. My code is only for FAT16, so if I found it is not FAT16, then it display an error ie NOT A FAT16. If it found FAT16, then it reads few more data from the buffer and calculates sector number for the data start, fat start and root directory start. Also it detects the sectors per clusters. Each sector is 512 bytes. These four data is required for the further activities on MMC/SD card. So those are stored as global variables.
Now the next step is to get into the root directory. (MMC commands and the sector reading is covered in my previous post as linked above). Now we need to load the first sector of root directory into the 512 byte buffer. Then, each root directory entry is of 32 byte (in general for 8.3 file name format). Each entry contains details of a file or folder in the root directory. From there we can read the file name, file attribute, actual file starting address (cluster address) and many more... We are interested in the file name extension (WAV), the file attribute and the file starting cluster address. So we compare the extension with the string "WAV" and if it matches, then we return the cluster address of the wav file.

Now we could read the first cluster (a group of sectors, size depends on the size of MMC/SD). We could find the sector address from the cluster number using as equation.(u can see that on my code). Now after reading and playing all the sectors in the first cluster, (playing the data will be explained after this) then we need to find the next cluster number of the same file. A file may not be distributed on the memory as one section. Instead, it can be splitted into parts and placed here and there to utilize the free memory effectively.... (actually this happens only when there are some deleted files and we add new files to the MMC/SD). So we can't predict that the next cluster of the file will be successive numbers... But all the cluster order for each file is perfectly tracked on a linked list called the FAT...(File Allocation Table). Each cluster number have a unique position on the FAT. We already calculated the FAT starting address. From the FAT, we could get the next cluster number of the file. Since it is a linked list, the 16 bit data present on the 1st cluster number position will be the second cluster number. Now after reading the second cluster, we check which is the third cluster by checking the data on the location of second cluster position,,,This continues until we read a 0xffff from a location on FAT... This denotes the end cluster for the particular file..
We have first cluster number and we calculated the sector starting address of the particular cluster number. Now we read the data from first sector of the file and from there we could get the bitrate, sample rate, number of channel (stereo, mono) and many more.. We take the bit rate and use it to set the timer interrupt frequency... Now the timer interrupt is generated according to the bitrate and channel number. Now on each timer interrupt, an 8 bit data is introduced to the OCR register of Timer PWM module. Accordingly it generate PWM signal in background without any CPU resource. This PWM signal could be easily demodulated with an RC filter. If the capacitor value increases or resistor value decreases ,then it will affect the audio quality ie it may filter out some higher frequency components of the audio and may feel it like hearing some thing from an AM MW radio..:-)...So care must be given while choosing RC.. 
Now, we know, if we use a single buffer for both playing data and collecting data, then there will be a small contuinity problem while the song is played.... So, it will be really irritating if we are hearing our fav music like that... Thus, here I had implemented two special 512 bytes buffer for audio data only. This buffer is filled and played by a special technique.. ie when one buffer is playing (used inside timer interrupt) , the other buffer will be filling.,, This buffers will be exchanged alternatively... By this technique, I could obtain a pure uninterrupted high quality audio .... Thats all about the working ..............;-)


Now, decoding the TV remote is a simple process, ie using a timer interrupt, we sample the incoming signal on each 1778 us. To sample the data at mid point of the first half of the manchester code, I made a small delay of about 400ms from the time zero (ie the time when the start bit is detected)...After that the timer is activated to generate interrupt flag on each 1778us. So Now u can check my ISR(INT2_vect) code to see how RC5 is decoded. Also, check my previous code about an RC5 decoder which shows the structure of RC5 code. Other wise a simple google image search for "RC5 structure" will show the required data....


I had implemented RC5 decoding on the same Atmega32. But I think it is not a good method. Becuase, it will be inside the Timer ISR for more time while playing the file. So, most time, RC5 external interrupt will be triggered when the processor is handling the Timer interrupt. Then the external interrupt will be handled only after that and this results an invalid start bit detection inside the rc5 interrupt handler and that will be treated as an invalid RC5 signal. So if we are lucky enough, the first keypress itself will do the job, else we need to press the key for a while or retry after key release... But probably, it will work with in 1 or 2 keypress... So at present I think, the best method is to decode the RC5 outside the atmega32 and send the value via a serial interface for a better performance..
Circuit Diagram[wav player with PWM output]:





Wave structure:

The WAVE file format is a subset of Microsoft's RIFF specification for the storage of multimedia files. A RIFF file starts out with a file header followed by a sequence of data chunks. A WAVE file is often just a RIFF file with a single "WAVE" chunk which consists of two sub-chunks -- a "fmt " chunk specifying the data format and a "data" chunk containing the actual sample data.

































As an example, here are the opening 72 bytes of a WAVE file with bytes shown as hexadecimal numbers:


52 49 46 46 24 08 00 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 02 00 22 56 00 00 88 58 01 00 04 00 10 00 64 61 74 61 00 08 00 00 00 00 00 00 24 17 1e f3 3c 13 3c 14 16 f9 18 f9 34 e7 23 a6 3c f2 24 f2 11 ce 1a 0d 

Source code[for WAV player with PWM output]: 
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
/*
TV REMOTE CONTROLLED HIGH QUALITY MMC WAV PLAYER USING ATMEGA32

By Vinod S <vinodstanur@gmail.com> <http://blog.vinu.co.in>

First release Date: 25/02/2012
Last update Date: 02/03/2012
[improved maximum bitrate support to 1600kbps for stereo and 1300 for mono]

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

*/

#include<avr/io.h>
#define F_CPU 16450000
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>

#define SECONDS_TO_FORWARD 15
#define RS PD6
#define EN PD5
#define LCD_NIBBLE PORTC
#define LINE1 cmd(0x80)
#define LINE2 cmd(0xc0)
#define LEFT_SWITCH PIND&(1<<2)
#define RIGHT_SWITCH PIND&(1<<3)
#define SWITCH_EVENT PIND&((1<<3)|(1<<2))
#define CS 4
#define RC5_LED PB0

unsigned char readdata;
unsigned int count;
unsigned long int arg = 0;
unsigned char mmc_buf[512];
unsigned char mmc_buf0[512];
unsigned char mmc_buf1[512];
unsigned int fat_start, dir_start, data_start;
unsigned char sect_per_clust;
volatile unsigned char BUF1_EMPTY, BUF0_EMPTY = 0;
unsigned long int OCR1A_BACKUP;
register int ISR_i asm("r2");
register char STEREO asm("r4");
register char TOGGLE_BUFFER asm("r5");
unsigned long int bitrate;
unsigned int RC5_DATA;
char RC5_FLAG;
unsigned int OCR1A_ADJUST;
unsigned int STARTING_CLUSTER;

void LCD_STROBE(void);
void data(unsigned char);
void cmd(unsigned char);
void clear(void);
void lcd_init();
void string(char *, char);
void spi_init();
void spi_write(char);
unsigned char spi_read();
void command(unsigned char, unsigned long int, unsigned char);
char mmc_init();
void mmc_read_sector(unsigned long int);
void fat16_init();
void print_num(unsigned long int, char);
unsigned int scan_root_dir(unsigned char *,char [], char);
void play_cluster(unsigned int);
unsigned int find_next_cluster(unsigned int);
void pwm_init();
void mmc_read_double_buffer(unsigned long int, unsigned char []);
void timer1_init();
char check_bitrate_and_stereo(unsigned int);
void INT2_init();
unsigned int forward_seconds(unsigned int);
void pull_up_enable();
void init_RC5_valid_indicator_LED();
int main()
{
unsigned char fname[12];
unsigned int cluster;
char NEXT_OR_PREVIOUS = 1;

_delay_ms(50);
spi_init();
lcd_init();
pull_up_enable();
while(mmc_init());
pwm_init();
fat16_init();
timer1_init();
INT2_init();
init_RC5_valid_indicator_LED();
while(1) {
while((cluster = scan_root_dir("WAV", fname, NEXT_OR_PREVIOUS)) == 0) {
NEXT_OR_PREVIOUS = 1;
}
NEXT_OR_PREVIOUS = 1;
LINE1;
clear();
string(fname,1);
if(!check_bitrate_and_stereo(cluster)) {
TOGGLE_BUFFER = 0;
BUF0_EMPTY = 1;
ISR_i = 0;
sei();
while(cluster != 0xffff) {
play_cluster(cluster);
cluster = find_next_cluster(cluster);
if(SWITCH_EVENT) {
if(RIGHT_SWITCH) {NEXT_OR_PREVIOUS = 1;break;};
if(LEFT_SWITCH) {NEXT_OR_PREVIOUS = 0; break;};
}
if(RC5_FLAG) {
RC5_FLAG = 0;
if(RC5_DATA == 32) {NEXT_OR_PREVIOUS = 1;break;};
if(RC5_DATA == 33) {NEXT_OR_PREVIOUS = 0;break;};
if(RC5_DATA == 1) {OCR1A = (OCR1A_ADJUST += OCR1A_BACKUP/20);};
if(RC5_DATA == 3) {OCR1A = (OCR1A_ADJUST -= OCR1A_BACKUP/20);};
if(RC5_DATA == 2) {OCR1A = (OCR1A_ADJUST = OCR1A_BACKUP);};
if(RC5_DATA == 6) {cluster = forward_seconds(cluster);}
if(RC5_DATA == 5) {cluster = STARTING_CLUSTER;}
}
}
cli();
clear();
_delay_ms(100);
}
}
return 0;
}

unsigned int forward_seconds(unsigned int cluster)
{
cli();
unsigned long int clusters_to_forward;
clusters_to_forward = ((bitrate / 512) * SECONDS_TO_FORWARD) / sect_per_clust;
while(clusters_to_forward) {
cluster = find_next_cluster(cluster);
if(cluster == 0xffff) break;
clusters_to_forward--;
}
sei();
return cluster;
}
void INT2_init()
{
DDRB &= ~(1<<PB2);
GICR |=(1<<INT2);
sei();
}

ISR (INT2_vect)
{
char i = 0;
//TIMSK &= ~(1 << OCIE1A);
RC5_DATA = 0;
OCR1A = ((double)F_CPU/1000000)*1778;
_delay_us(350);
TCNT1 = 0;
for(i = 0; i < 13; i++) {
while(!(TIFR & (1 << OCF1A)));
TIFR |= 1<<OCF1A;
RC5_DATA <<= 1;
if(PINB & (1<<PB2)) {
_delay_us(10);
if(PINB & (1<<PB2)) {
_delay_us(10);
if(PINB & (1<<PB2)) {
RC5_DATA++;
}
}
}
}
if((RC5_DATA & 0b1111101100000000) != 0b0000001100000000) {
OCR1A = OCR1A_ADJUST;
return;
}
PORTB |= (1<<RC5_LED);
RC5_DATA &= 0b111111;
RC5_FLAG = 1;
OCR1A = OCR1A_ADJUST;
TIMSK |= (1 << OCIE1A);
_delay_ms(500);
PORTB &= ~(1<<RC5_LED);
GIFR |= (1<<INTF2);
}

char check_bitrate_and_stereo(unsigned int cluster)
{
int i;
mmc_read_sector(((unsigned long int)(cluster -2) * sect_per_clust) + data_start);
if(mmc_buf[34] != 8) return 1;
for (i = 31; i > 27; i--) {
bitrate <<= 8;
bitrate |= mmc_buf[i];
}
STEREO = mmc_buf[22] - 1;
print_num(bitrate,2);
OCR1A_BACKUP = ((F_CPU *(STEREO + 1))/bitrate);
OCR1A = OCR1A_ADJUST = OCR1A_BACKUP;
return 0;
}

unsigned int find_next_cluster(unsigned int cluster)
{
unsigned int cluster_index_in_buff = (2 * (cluster % 256));
mmc_read_sector(fat_start + cluster/256);
return ((mmc_buf[cluster_index_in_buff + 1] << 8) + mmc_buf[cluster_index_in_buff]);
}



void mmc_read_double_buffer(unsigned long int sector, unsigned char a[])
{
int i;
sector *= 512;
command(17, sector, 0xff);
while (spi_read() != 0);
while (spi_read() != 0xfe);
for(i = 0; i < 512; i++)
a[i] = spi_read();
spi_write(0xff);
spi_write(0xff);
}

void play_cluster(unsigned int cluster)
{
unsigned long int sector;
int i, j;
sector = ((unsigned long int)(cluster -2) * sect_per_clust);
sector += data_start;
for(i = 0; i < sect_per_clust; i++) {
while((!BUF1_EMPTY) && (!BUF0_EMPTY));
if(BUF0_EMPTY) {
mmc_read_double_buffer(sector, mmc_buf0);
BUF0_EMPTY = 0;
} else if(BUF1_EMPTY) {
mmc_read_double_buffer(sector, mmc_buf1); BUF1_EMPTY = 0;}
sector += 1;
}
}

ISR (TIMER1_COMPA_vect)
{
if(STEREO) {
if(TOGGLE_BUFFER == 1) {
OCR0 = mmc_buf0[ISR_i++];
OCR2 = mmc_buf0[ISR_i++];
}else{
OCR0 = mmc_buf1[ISR_i++];
OCR2 = mmc_buf1[ISR_i++];
}
if(ISR_i == 512) {
if(TOGGLE_BUFFER)
BUF0_EMPTY = 1;
else
BUF1_EMPTY = 1;
TOGGLE_BUFFER ^= 1;
ISR_i = 0;
}
} else {
if(TOGGLE_BUFFER == 1)
OCR0 = OCR2 = mmc_buf0[ISR_i++];
else
OCR0 = OCR2 = mmc_buf1[ISR_i++];
if(ISR_i == 512) {
if(TOGGLE_BUFFER)
BUF0_EMPTY = 1;
else
BUF1_EMPTY = 1;
TOGGLE_BUFFER ^= 1;
ISR_i = 0;
}
}
}

void timer1_init()
{
TCCR1B |= (1 << WGM12)|(1 << CS10);
TCNT1 = 0;
OCR1A = 10000;
TIMSK |= (1 << OCIE1A);
}

unsigned int scan_root_dir(unsigned char *FILE_EXTENSION, char FNAME[], char UP_DOWN)
{
while(1) {
unsigned int i;
static unsigned char read_end = 0;
static int base_count = -32, sect_plus = 0;
if(UP_DOWN == 1) {
base_count += 32;
if(base_count == 512) {base_count = 0; sect_plus += 1;};
} else {
base_count -= 32;
if(base_count == -32) {base_count = (512 - 32); sect_plus -= 1;}
if(sect_plus < 0) {sect_plus = 0; base_count = 0;}
}
while(1) {
mmc_read_sector(dir_start + sect_plus);
while(base_count < 512) {
if(mmc_buf[base_count] == 0) { read_end = 1; break;}
if ((mmc_buf[1] != 0) && (mmc_buf[base_count + 2] != 0) && (mmc_buf[base_count] != 0xe5) && (mmc_buf[base_count] != 0x00) && ((mmc_buf[base_count + 11] & 0b00011110) == 0) && (strncmp(mmc_buf + base_count + 8, FILE_EXTENSION, 3) == 0)) {
for(i = 0; i < 11; i++)
FNAME = mmc_buf[base_count + i];
FNAME[11] = 0;
return (STARTING_CLUSTER = (unsigned int)((mmc_buf[27 + base_count] << 8) + mmc_buf[26 + base_count]));
}
if(UP_DOWN) base_count += 32;
else base_count -= 32;
}
base_count = 0;
sect_plus++;
if(read_end) { base_count = -32; sect_plus = 0; read_end = 0; return 0;}
}
}
}

void print_num(unsigned long int i, char line)
{
char u = 0;
unsigned char lcd_buf[16];
if(line == 1) cmd(0x80);
else cmd(0xc0);
while(i) {
lcd_buf[u++] = (i % 10 + '0');
i /= 10;
}
while(u) data(lcd_buf[--u]);
}

void fat16_init() //BOOT SECTOR SCANNING//
{
mmc_read_sector(0);
clear();
LINE1;
if((mmc_buf[0x36] == 'F') && (mmc_buf[0x39] == '1') && (mmc_buf[0x3a] == '6'))
string("FAT16 DETECTED",1);
else {
string("NOT A FAT16",1);
while(1);
}
_delay_ms(500);
fat_start = mmc_buf[0x0e];
dir_start = (fat_start + (((mmc_buf[0x17] << 8) + mmc_buf[0x16]) * 2));
data_start = (dir_start + ((((mmc_buf[0x12] << 8) + (mmc_buf[0x11])) * 32) / 512));
sect_per_clust = mmc_buf[0x0d];
}

void mmc_read_sector(unsigned long int sector)
{
int i;

sector *= 512;
command(17, sector, 0xff);
while (spi_read() != 0);
while (spi_read() != 0xfe);
for(i = 0; i < 512; i++)
mmc_buf = spi_read();
spi_write(0xff);
spi_write(0xff);
}

char mmc_init()
{
int u = 0;

PORTB |= 1<<CS;
for (u = 0; u < 50; u++) {
spi_write(0xff);
}
PORTB &= ~(1<<CS);
_delay_ms(1);
command(0, 0, 0x95);
count = 0;
while ((spi_read() != 1) && (count < 1000))
count++;
if (count >= 1000) {
string("CARD ERROR-CMD0 ",1);
_delay_ms(500);
return 1;
}
command(1, 0, 0xff);
count = 0;
while ((spi_read() != 0) && (count < 1000)) {
command(1, 0, 0xff);
count++;
}
if (count >= 1000) {
string("CARD ERROR-CMD1 ",1);
_delay_ms(500);
return 1;
}
command(16, 512, 0xff);
count = 0;
while ((spi_read() != 0) && (count < 1000))
count++;
if (count >= 1000) {
string("CARD ERROR-CMD16",1);
_delay_ms(500);
return 1;
}
string("MMC INITIALIZED!",1);
_delay_ms(500);
SPCR &= ~(1<<SPR1); //increase SPI clock from f/32 to f/2
return 0;
}


void command(unsigned char command, unsigned long int fourbyte_arg, unsigned char CRCbits)
{
spi_write(0xff);
spi_write(0b01000000 | command);
spi_write((unsigned char) (fourbyte_arg >> 24));
spi_write((unsigned char) (fourbyte_arg >> 16));
spi_write((unsigned char) (fourbyte_arg >> 8));
spi_write((unsigned char) fourbyte_arg);
spi_write(CRCbits);
spi_read();
}

unsigned char spi_read()
{
SPDR = 0xff;
while(!(SPSR & (1<<SPIF)));
return SPDR;
}

void spi_write(char cData)
{
SPDR = cData;
while(!(SPSR & (1<<SPIF)));
}

void spi_init()
{
DDRB |= (1<<5)|(1<<7)|(1<<4);
SPCR = (1<<SPE)|(1<<MSTR)|(1<<CPOL)|(1<<CPHA)|(1<<SPR1);
SPSR = 1;
}

void LCD_STROBE(void)
{
PORTD |= (1 << EN);
_delay_us(1);
PORTD &= ~(1 << EN);
}

void data(unsigned char c)
{
PORTD |= (1 << RS);
_delay_us(50);
LCD_NIBBLE = (c >> 4);
LCD_STROBE();
LCD_NIBBLE = (c);
LCD_STROBE();
}

void cmd(unsigned char c)
{
PORTD &= ~(1 << RS);
_delay_us(50);
LCD_NIBBLE = (c >> 4);
LCD_STROBE();
LCD_NIBBLE = (c);
LCD_STROBE();
}

void clear(void)
{
cmd(0x01);
_delay_ms(5);
}

void lcd_init()
{
DDRC = 0x0f;
DDRD |= (1 << RS)|(1 << EN);
_delay_ms(15);
cmd(0x30);
_delay_ms(1);
cmd(0x30);
_delay_us(100);
cmd(0x30);
cmd(0x28);
cmd(0x28);
cmd(0x0c);
clear();
cmd(0x6);
}

void string(char *p, char line)
{
if(line == 1) LINE1;
else LINE2;
while(*p) data(*p++);
}

void pwm_init()
{
TCCR0|=(1<<WGM00)|(1<<WGM01)|(1<<COM01)|(1<<CS00);
TCCR2|=(1<<WGM20)|(1<<WGM21)|(1<<COM21)|(1<<CS20);
DDRB|=(1<<PB3);
DDRD|=(1<<PD7);
}

void pull_up_enable()
{
PORTB |= (1<<PB2);
}
void init_RC5_valid_indicator_LED()
{
DDRB |= (1<<RC5_LED);
}






download source code + hex + asm + object + makefile from below link
repo

Screen shots:










Development tools:
I am using avr-gcc in linux.
We need avr-gccbinutils-avr & avr-libc to be installed before trying to build the hex file. These are available via synaptic package manager.
Also, I am using avrdude package to burn the avr, hardware used is usbasp. So the makefile is made according to that... We can also use a simple parallel port burning circuit but in my case I don't have a parallel port in my lap, so I stick with the usbasp. It contains an atmega8 microcontroller programmed with the hex available at usbasp homepage.

here are the photos of my usbasp programmer, made according to the circuit diagram and firmware provided at the usbasp home page.




You could download the repo from the my bitbucket link.. (posted just below the source code).

How to make?
cd into the directory where the Makefile and c file is moved.
just need to type "make" to build the hex

How to burn it using usbasp?
Just need to type "sudo make burn_hex"

How to burn fuse bits?
Just need to type "sudo make burn_fuse"

How to clear the build?
Just need to type "make clean"

How to build & burn it without using Makefile?
?
1
2
3
4
avr-gcc -mmcu=atmega32 -O2 main.c
avr-objcopy -j .text -j .data -O ihex a.out a.hex
sudo avrdude -c usbasp -p m32 -U flash:w:a.hex:i
sudo avrdude -c usbasp -p m32 -U lfuse:w:0xef:m



Finished......;-)

Fuse bits: LFUSE = 0b1110111
1