Skip to main content

JUKEBOX:

Source code for Jukebox using Timer0 interrupts as time signature control and PWM for note generation.


Source Code:

 /*Program Name: Jukebox

Author: TY Lew

This program generates a tune at the piezo speaker of PICDEM2 Plus. It uses PWM to

generate each note and timer0 interrupt to execute the time signature of each note. Thus, the

PR2 is updated with the value upon each inetrrupt. Whereas, the CCPR1L simple updated with

PR2/2 for 50-50 mark-space ratio. The CCP1CON<5:4> is ignore as we do not need such

accuracy for the tones.

The 'note' array determines the tone frequecy of the notes that will go into PR2. The number

is the MIDI notation. The 'tmsig' array determines the duration of each note and will be used

to update timer registers. The larger the value the longer is the duration. In general the sytem

goes into the ISR for every tone played. */


/* Pressing RA4 button toggles the sound tune on and off */


#include <xc.h>

#include <stdlib.h>


#pragma config OSC = XT

#pragma config LVP = OFF

#pragma config WDT = OFF


#define LCD_DATA PORTD

#define LCD_RS RE1 // RS signal for LCD

#define LCD_E RE0 // E signal for LCD 

#define LED RA3

const char note[30]={72,74,76,78,78,79,83,81,78,81,79,78,79,76,78,74,76,78,78,79,83,81,78,81,79,78,79,76,74,0};

const char tmsig_chart[29]={2,2,2,4,4,4,4,8,4,4,6,2,4,4,10,2,2,4,4,4,4,8,4,4,6,2,4,4,16};

char sound_on=0;

char i,tmsig,ndx;

unsigned int temp;

void Init_LCD(void); // Initialize the LCD

void W_ctr_4bit(char); // 4-bit control word for LCD

void W_data_4bit(char); // 4-bit text Data for LCD

char buff[10];

int count, flag = 0;

unsigned char i, LCD_TEMP;

const char MESS[] = "Press RB1 for "; // Message on LCD 

const char MESS1[] = "sound ON/OFF"; 

void interrupt low_priority tmr0(void); // Low priority interrupt service routine


#define _XTAL_FREQ 4000000

unsigned char sec;


// Low priority interrupt routine

//#pragma interruptlow InterruptHandlerLow


void interrupt low_priority tmr0(void) {

char note_tbl[25]={238,224,212,200,189,178,168,158,149,141,133,126,118,112,105,99,94,88,83,79,74,70,66,62,59};

// The above is just like a piano keyboard

if (INTCONbits.TMR0IF) { // Check for Timer0 overflow

CCP1CON = CCP1CON & 0b11110000; // disable CCP1

i++;

if(note[i]==0){

T0CONbits.TMR0ON = 0; // DISABLE TMR0 INTERRUPT

sound_on=0;

}

else {


ndx=note[i]-60;

PR2=note_tbl[ndx];

CCPR1L = PR2/2 ; //

CCP1CON = 0b00001111; // DC1B1 & DC1B0 = 0, PWM mode

tmsig=tmsig_chart[i];

temp =(65536-4000*tmsig);

TMR0H = temp/256;

TMR0L = temp%256;

T0CONbits.TMR0ON = 1;

INTCONbits.TMR0IF = 0; // Clear Timer0 interrupt flag

}

}

}


/* --------------------------------*/

void main()

{


ADCON1 = 0x0f;


INTCONbits.GIE = 0; // Disable all interrupt first

PORTA = 0;

TRISA = 0b11110000;

TRISC = 0b11111011; // Port C as output

TRISE = 0x0c;

TRISB = 0b11111111; // bit0-3 of Port B as output

PORTB = 0b00001000; // RB3 on first

TRISD = 0x0f;

T2CON = 0b00000111; // Timer 2 On, postscaler = 1:1, prescaler = 1:16

RCONbits.IPEN = 1; // Enable priority interrupt

INTCON2bits.TMR0IP = 0; // Set Timer0 interrupt to high priority

TMR0H = 0xff; // Timer0 high byte register

TMR0L = 0x00; // Timer0 low byte register

T0CON = 0b00000011; // Off Timer0, set to 16-bit mode,

// Use internal instruction clock,

// Rising-edge trigger, set prescaler to 1:8

INTCONbits.TMR0IF = 0;

INTCONbits.TMR0IE = 1; // Enable Timer0 overflow interrupt

Init_LCD(); // Init LCD 4-bit interface, multiple line

 for (i = 0; MESS[i]; i++) // Output message to LCD

 W_data_4bit(MESS[i]); // Write individual character to LCD

W_ctr_4bit(0xc3);

 for (i = 0; MESS1[i]; i++) // Output message to LCD

 W_data_4bit(MESS1[i]); // Write individual character to LCD

INTCONbits.GIEL = 1; // Enable interrupts with low priority

INTCONbits.GIE = 1; // Enable globle interrupt

while(1){

if (!PORTBbits.RB1) {

__delay_ms(20);

if (!PORTBbits.RB1){

while(!PORTBbits.RB1);

sound_on=~sound_on;

if (sound_on)   i=0;

    

 }

}


if (sound_on){

T0CONbits.TMR0ON = 1;

LED = 1;

}

else {

T0CONbits.TMR0ON = 0;

CCP1CON = CCP1CON & 0b11110000; // disable CCP1

                         LED = 0;

}

}

}

void W_ctr_4bit(char x)

{

 /* Write control word in terms of 4-bit at a time to LCD */

LCD_RS = 0; // Logic 0?

LCD_TEMP = x; // Store control word

LCD_E = 1; // Logic 1?

LCD_DATA = LCD_TEMP; // Send upper nibble of control word

_delay(1000); // 1ms delay

LCD_E = 0; // Logic 0?

_delay(1000); // 1ms delay

LCD_TEMP = x; // Store control word

LCD_TEMP <<= 4; // Shift lower nibble to upper nibble

LCD_E = 1; // Logic 1?

LCD_DATA = LCD_TEMP; // Send lower nibble of control word


_delay(1000); // 1ms delay

LCD_E = 0; // Logic 0?

_delay(1000); // 1ms delay

}

void W_data_4bit(char x)

{

 /* Write text data in term of 4-bit at a time to LCD */

LCD_RS = 1; // Logic 1?

LCD_TEMP = x; // Store text data

LCD_E = 1; // Logic 1?

LCD_DATA = LCD_TEMP; // send upper nibble of text data

_delay(1000); // 1ms delay

LCD_E = 0; // Logic 0?

_delay(1000); // 1ms delay

LCD_TEMP = x; // Store text data

LCD_TEMP <<= 4; // Shift lower nibble to upper nibble

LCD_E = 1; // Logic 1?

LCD_DATA = LCD_TEMP; // Send lower nibble of text data

_delay(1000); // 1ms delay

LCD_E = 0; // Logic 0?

_delay(1000); // 1ms delay

}

void Init_LCD() // Function to initialize LCD

{

 _delay(15); // a)15ms LCD power-up delay

W_ctr_4bit(0x03); // b) Function Set (DB4-DB7:8-bit interface)

_delay(5); // c) 5ms delay

W_ctr_4bit(0x02); // d) Function Set (DB4-DB7:4-bit interface)

W_ctr_4bit(0b00101000); // Function Set - 4-bit, 2 lines, 5x7

W_ctr_4bit(0b00001100); // Display on, cursor off

W_ctr_4bit(0b00000110); // Entry mode - inc addr, no shift

W_ctr_4bit(0b00000001); // Clear display & home position

}

Comments

Just blog said…
The debouncing delay should be changed to 80ms instead of 20ms.

Popular posts from this blog

Generating Monophonic Melody using PWM of PIC18

/*Program Name: Jukebox Author: TY Lew This program generates a tune at the piezo speaker of PICDEM2 Plus. It uses PWM to generate each note and timer0 interrupt to execute the time signature of each note. Thus, the PR2 is updated with the value upon each inetrrupt. Whereas, the CCPR1L simple updated with PR2/2 for 50-50 mark-space ratio. The CCP1CON<5:4> is ignore as we do not need such accuracy for the tones. The 'note' array determines the tone frequecy of the notes that will go into PR2. The number is the MIDI notation. The 'tmsig' array determines the duration of each note and will be used to update timer registers. The larger the value the longer is the duration. In general the sytem goes into the ISR for every tone played. */ /* Pressing RA4 button toggles the sound tune on and off */ #include < xc.h > #include < stdlib.h > const char note[30]={72,74,76,78,78,79,83,81,78,81,79,78,79,76,78,74,76,78,78,79,83,81,78,81,79,78,79,76,74,0}; const char

Why "PORTB=~PORTB" sometime does not work?

When performinging READ-MODIFY-WRITE action, some target board setup may encounter problems. Don't know what is this? Simple. Let's look at the following example: PORTB=~PORTB; The statement required the PORTB to be read first then invert then assign back to PORTB, when executed. This is called READ-MODIFY-WRITE. Some students (not all) encounter problem whereby the execution does not lead to the toggling port PORTB. The remedy is to change the statement to: PORTB=~LATB. Why? All the I/O ports in the PIC18F are buffered when they are configured as output ports. In this context, all the 8 bits of PORTB are directed from the output sides of the buffer. The output devices (e.g. LEDs) may have loaded heavily and causing the voltage to drop far below the VDD value. When the READ action takes place the logic states might have been read wrongly. Consider that the PORTB LEDs are currently lighted up by logic 1s. If the READ-MODIFY-WRIRE statement mentioned above is used to t