PIC12F1822を使用してメロディーIC的なことをさせてみました。手元にPICがあれば好きな曲でメロディーICを作れちゃいます。
使用したPIC
PIC12F1822です。手元にあるCCPが使用できるPICの中で一番小さかったので採用しました。本当はNCOを使いたかった(泣)

ちまちま作ろう
演奏する曲はロシア民謡のカチューシャにしました。PWM変調をすると矩形以外の波形を出力することが出来ますがそれは今後出します。(今は僕の技術がありません)
何もせず単純に矩形波で音を出していこうと思います。
CCPから出てくるPWM波形の周期はCCPが参照するタイマの周期で決まるので、タイマ周期を変えるレジスタの値を変更すれば音程を変えることが出来ます。
音の長さはタイマ割り込みを利用して制御しています。
使い方
MPLABXにXC8を入れて以下のソースコードをメインファイルとしてコンパイルします。後は書き込むだけです。
書き込んだPICは外から見ればこのようなメロディICに見えます。

PLAYとrepeatはプルアップしているので何も繋がないと1になります。
ソースコード
#pragma config FOSC = INTOSC // Oscillator Selection->INTOSC oscillator: I/O function on CLKIN pin
#pragma config WDTE = OFF // Watchdog Timer Enable->WDT disabled
#pragma config PWRTE = OFF // Power-up Timer Enable->PWRT disabled
#pragma config MCLRE = ON // MCLR Pin Function Select->MCLR/VPP pin function is MCLR
#pragma config CP = OFF // Flash Program Memory Code Protection->Program memory code protection is disabled
#pragma config CPD = OFF // Data Memory Code Protection->Data memory code protection is disabled
#pragma config BOREN = ON // Brown-out Reset Enable->Brown-out Reset enabled
#pragma config CLKOUTEN = OFF // Clock Out Enable->CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin
#pragma config IESO = ON // Internal/External Switchover->Internal/External Switchover mode is enabled
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable->Fail-Safe Clock Monitor is enabled
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection->Write protection off
#pragma config PLLEN = OFF // PLL Enable->4x PLL disabled
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable->Stack Overflow or Underflow will cause a Reset
#pragma config BORV = LO // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (Vbor), low trip point selected.
#pragma config LVP = ON // Low-Voltage Programming Enable->Low-voltage programming enabled
#define C4 0xEE
#define D4 0xD4
#define E4 0xBD
#define F4 0xB2
#define G4 0x9E
#define A4 0x8D
#define H4 0x7E
#define C5 0x77
#define D5 0x6A
#define E5 0x5E
#define F5 0x59
#define G5 0x4F
#define A5 0x46
#define H5 0x3E
#define C6 0x3B
#define END 0x01
#define bu4 0x4647
#define bu8 0xA33A
#define bu16 0xD19D
#define bu0 0x00
#define _XTAL_FREQ 4000000
#include <xc.h>
unsigned int read_counter = 0;
const int tone[]={A4,A4,H4,C5,C5,A4,C5,H4,A4,H4,E4
,H4,H4,C5,D5,D5,H4,D5,0,D5,0,C5,H4,A4,A4
,E5,A5,G5,A5,G5,F5,E5,D5,E5,A4
,0,F5,D5,E5,E5,C5,D5,0,D5,0,C5,H4,A4,A4
,E5,A5,G5,A5,G5,F5,E5,D5,E5,A4
,0,F5,D5,E5,E5,C5,D5,0,D5,0,C5,H4,A4,END};
const unsigned long time[]={bu4,bu8,bu8,bu4,bu8,bu8,bu4,bu8,bu8,bu4,bu4
,bu4,bu8,bu8,bu4,bu8,bu8,bu16,bu16,bu16,bu16,bu8,bu8,bu4,bu4
,bu4,bu4,bu4,bu8,bu8,bu4,bu8,bu8,bu4,bu4
,bu8,bu4,bu8,bu4,bu8,bu8,bu16,bu16,bu16,bu16,bu8,bu8,bu4,bu4
,bu4,bu4,bu4,bu8,bu8,bu4,bu8,bu8,bu4,bu4
,bu8,bu4,bu8,bu4,bu8,bu8,bu16,bu16,bu16,bu16,bu8,bu8,bu4,bu4};
void OSCILLATOR_WDT_Initialize(void)
{
// SCS FOSC; SPLLEN disabled; IRCF 4MHz_HF;
OSCCON = 0x68;
// TUN 0;
OSCTUNE = 0x00;
// SBOREN disabled;
BORCON = 0x00;
WDTCON = 0x16;
}
void TMR1_Initialize(void)
{
//Set the Timer to the options selected in the GUI
//T1GSS T1G_pin; TMR1GE disabled; T1GTM disabled; T1GPOL low; T1GGO done; T1GSPM disabled;
T1GCON = 0x00;
//TMR1H 209;
TMR1H = 0xD1;
//TMR1L 157;
TMR1L = 0x9D;
// Clearing IF flag before enabling the interrupt.
PIR1bits.TMR1IF = 0;
// Enabling TMR1 interrupt.
PIE1bits.TMR1IE = 1;
// T1CKPS 1:8; T1OSCEN disabled; nT1SYNC synchronize; TMR1CS FOSC/4; TMR1ON enabled;
T1CON = 0x31;
}
void TMR2_Initialize(void)
{
// Set TMR2 to the options selected in the User Interface
// PR2 238;
PR2 = 0xEE;
// TMR2 0;
TMR2 = 0x00;
// Clearing IF flag.
PIR1bits.TMR2IF = 0;
// T2CKPS 1:16; T2OUTPS 1:8; TMR2ON on;
T2CON = 0x3E;
}
void EPWM_Initialize(void)
{
// Set the EPWM to the options selected in the User Interface
// CCP1M P1A: active high; P1B: active high; DC1B 3; P1M single;
CCP1CON = 0x3C;
// CCP1ASE operating; PSS1BD low; PSS1AC low; CCP1AS disabled;
ECCP1AS = 0x00;
// P1RSEN automatic_restart; P1DC 0;
PWM1CON = 0x80;
// STR1B P1B_to_port; STR1A P1A_to_CCP1M; STR1SYNC start_at_begin;
PSTR1CON = 0x01;
// CCPR1H 0;
CCPR1H = 0x00;
// CCPR1L 127;
CCPR1L = 0x7F;
}
void EPWM_LoadDutyValue(unsigned int dutyValue)
{
// Writing to 8 MSBs of pwm duty cycle in CCPRL register
CCPR1L = ((dutyValue & 0x03FC)>>2);
// Writing to 2 LSBs of pwm duty cycle in CCPCON register
CCP1CON = ((unsigned char)(CCP1CON & 0xCF) | ((dutyValue & 0x0003)<<4));
}
void Set_Tone(int pr2){
unsigned int ccpr;
PR2 = pr2;
if(pr2 == C4){
ccpr = 477;
}else if(pr2 == D4){
ccpr = 425;
}else if(pr2 == E4){
ccpr = 377;
}else if(pr2 == F4){
ccpr = 357;
}else if(pr2 == G4){
ccpr = 317;
}else if(pr2 == A4){
ccpr = 283;
}else if(pr2 == H4){
ccpr = 251;
}else if(pr2 == C5){
ccpr = 237;
}else if(pr2 == D5){
ccpr = 213;
}else if(pr2 == E5){
ccpr = 187;
}else if(pr2 == F5){
ccpr = 179;
}else if(pr2 == G5){
ccpr = 159;
}else if(pr2 == A5){
ccpr = 141;
}else if(pr2 == H5){
ccpr = 125;
}else if(pr2 == C6){
ccpr = 117;
}else{
ccpr = 0;
}
EPWM_LoadDutyValue(ccpr);
}
void TMR1_WriteTimer(unsigned int timerVal)
{
if (T1CONbits.nT1SYNC == 1)
{
// Stop the Timer by writing to TMRxON bit
T1CONbits.TMR1ON = 0;
// Write to the Timer1 register
TMR1H = (timerVal >> 8);
TMR1L = timerVal;
// Start the Timer after writing to the register
T1CONbits.TMR1ON =1;
}
else
{
// Write to the Timer1 register
TMR1H = (timerVal >> 8);
TMR1L = timerVal;
}
}
void __interrupt() Interrupt (void){
if(PIR1bits.TMR1IF == 1){
Set_Tone(tone[read_counter]);
TMR1_WriteTimer(time[read_counter]);
if(tone[read_counter] != END){
read_counter++;
}else{
if(RA5 == 1){
read_counter = 0;
}else{
}
}
PIR1bits.TMR1IF = 0;
}
}
void main(void) {
OSCILLATOR_WDT_Initialize();
TMR1_Initialize();
TMR2_Initialize();
EPWM_Initialize();
TRISA = 0b11111011;
INTCONbits.GIE = 1;
INTCONbits.PEIE = 1;
WPUA = 0b00100000;
OPTION_REGbits.nWPUEN = 0;
read_counter = 0;
while(1){
}
return;
}
コメントを残す