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