メニュー
▼ アクセス(地図)
▼ Windowsアプリ開発 ▼ マイコン開発 ▼ CGI・PHP制作 |
▼ サイトマップ(全投稿記事)
▼ タイトル(画像一覧) ▼ 事業内容 |
お問い合わせメールフォームが開きます。
2015年10月22日
PIC12F1501でソフトウエアDDS
最近秋月電子で取り扱いを始めた8ピンのPICマイコン・PIC12F1501(70円)ですが、5bitのDAC機能を利用して ソフトウエアDDS(ダイレクト・デジタル・シンセシス) を作ってみました。
(同じ5bitのDAC機能があるPIC12F1822(100円)でも同様の動作が実装できます。)
なお、同様の機能はアナログ回路の方がより良い波形が得られますが、あくまでマイコン機能確認ということで。
【動作波形】
5bit(32段階)なので歪の多い正弦波です。
【回路】
ボリュームで出力周波数を可変する仕様です。(~約1kHz)
【動画】
ボリュームで周波数を可変させてオシロスコープで波形を見ている様子です。
【ソースコード】
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection Bits (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 = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program 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)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#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 LPBOR = OFF // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)
//───────────────────────────────────
// 定数定義
//───────────────────────────────────
// 基本になる出力波形の周波数(Hz)
#define BASE_FREQ 500
//───────────────────────────────────
// ユーザメモリ
//───────────────────────────────────
unsigned int Freq = 500; // 出力周波数
//───────────────────────────────────
// 正弦波テーブル
//───────────────────────────────────
const unsigned char WaveTable[64] = {
16,17,19,20,22,23,24,26,
27,28,28,29,30,30,31,31,
31,31,31,30,30,29,28,28,
27,26,24,23,22,20,19,17,
16,15,13,12,10,9,8,6,
5,4,4,3,2,2,1,1,
1,1,1,2,2,3,4,4,
5,6,8,9,10,12,13,15
};
//───────────────────────────────────
// 初期化
//───────────────────────────────────
static void init(void)
{
// Clock
OSCCON = 0b01111010; // Internal Osc 16MHz
OPTION_REG = 0b00000000; // PSA=0 1/2分周 WPU許可
// Port
TRISA = 0b00011000;
PORTA = 0b00000000;
ANSELA = 0b00010000; // AN3(RA4)使用
WPUA = 0b00001000; // RA3(INPUT専用)のみWPU
// DAC
DACCON0 = 0b10010000; // DAC許可 DACOE2出力
// ADC
ADCON1 = 0b10000000; // ADC 結果右詰, FOSC/2, VREF+ to VDD
ADCON0 = 0b00001101; // AN3 選択
// Timer2(32kHz)
T2CON = 0b00000100;
PR2 = 125;
// Interrupt
PEIE = 1; // Peripheral Interrupt Enable
TMR2IE = 1; // Timer2 Interrupt Enable
GIE = 1;
}
//───────────────────────────────────
// タイマ割込み処理
//───────────────────────────────────
static void interrupt isr( void )
{
static unsigned int IdxOffset = 0; // テーブルのオフセット
static unsigned int PhCnt = 0; // ブレゼンハム用作業変数
if( TMR2IF ) // TMR2オーバーフロー 31.25usec
{
TMR2IF = 0; // オーバーフローフラグクリア
PhCnt += Freq;
while( PhCnt >= BASE_FREQ)
{
PhCnt -= BASE_FREQ;
IdxOffset ++;
IdxOffset &= (sizeof(WaveTable)-1);
}
DACCON1 = WaveTable[IdxOffset];
}
}
//───────────────────────────────────
// ADC処理(周波数可変)
//───────────────────────────────────
static void getAnData( void )
{
unsigned int tmpAd;
GO_nDONE = 1; // スタート
while( GO_nDONE ); // 終了待ち
tmpAd = ADRESH;
tmpAd <<= 8;
tmpAd |= ADRESL;
Freq = tmpAd; // ~1024Hzとする
}
//───────────────────────────────────
// メイン処理
//───────────────────────────────────
int main(int argc, char** argv)
{
static unsigned char cnt = 0;
init();
while(1)
{
if( ++cnt >= 100 )
{
cnt = 0;
getAnData();
}
}
return (EXIT_SUCCESS);
}
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection Bits (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 = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program 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)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#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 LPBOR = OFF // Low-Power Brown Out Reset (Low-Power BOR is disabled)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)
//───────────────────────────────────
// 定数定義
//───────────────────────────────────
// 基本になる出力波形の周波数(Hz)
#define BASE_FREQ 500
//───────────────────────────────────
// ユーザメモリ
//───────────────────────────────────
unsigned int Freq = 500; // 出力周波数
//───────────────────────────────────
// 正弦波テーブル
//───────────────────────────────────
const unsigned char WaveTable[64] = {
16,17,19,20,22,23,24,26,
27,28,28,29,30,30,31,31,
31,31,31,30,30,29,28,28,
27,26,24,23,22,20,19,17,
16,15,13,12,10,9,8,6,
5,4,4,3,2,2,1,1,
1,1,1,2,2,3,4,4,
5,6,8,9,10,12,13,15
};
//───────────────────────────────────
// 初期化
//───────────────────────────────────
static void init(void)
{
// Clock
OSCCON = 0b01111010; // Internal Osc 16MHz
OPTION_REG = 0b00000000; // PSA=0 1/2分周 WPU許可
// Port
TRISA = 0b00011000;
PORTA = 0b00000000;
ANSELA = 0b00010000; // AN3(RA4)使用
WPUA = 0b00001000; // RA3(INPUT専用)のみWPU
// DAC
DACCON0 = 0b10010000; // DAC許可 DACOE2出力
// ADC
ADCON1 = 0b10000000; // ADC 結果右詰, FOSC/2, VREF+ to VDD
ADCON0 = 0b00001101; // AN3 選択
// Timer2(32kHz)
T2CON = 0b00000100;
PR2 = 125;
// Interrupt
PEIE = 1; // Peripheral Interrupt Enable
TMR2IE = 1; // Timer2 Interrupt Enable
GIE = 1;
}
//───────────────────────────────────
// タイマ割込み処理
//───────────────────────────────────
static void interrupt isr( void )
{
static unsigned int IdxOffset = 0; // テーブルのオフセット
static unsigned int PhCnt = 0; // ブレゼンハム用作業変数
if( TMR2IF ) // TMR2オーバーフロー 31.25usec
{
TMR2IF = 0; // オーバーフローフラグクリア
PhCnt += Freq;
while( PhCnt >= BASE_FREQ)
{
PhCnt -= BASE_FREQ;
IdxOffset ++;
IdxOffset &= (sizeof(WaveTable)-1);
}
DACCON1 = WaveTable[IdxOffset];
}
}
//───────────────────────────────────
// ADC処理(周波数可変)
//───────────────────────────────────
static void getAnData( void )
{
unsigned int tmpAd;
GO_nDONE = 1; // スタート
while( GO_nDONE ); // 終了待ち
tmpAd = ADRESH;
tmpAd <<= 8;
tmpAd |= ADRESL;
Freq = tmpAd; // ~1024Hzとする
}
//───────────────────────────────────
// メイン処理
//───────────────────────────────────
int main(int argc, char** argv)
{
static unsigned char cnt = 0;
init();
while(1)
{
if( ++cnt >= 100 )
{
cnt = 0;
getAnData();
}
}
return (EXIT_SUCCESS);
}
【その他】
DACポートは出力ですが
内部プルアップ抵抗(WPU)がOn
の場合は電圧が正常に出ないようです。
したがって該当ポートをWPUAレジスタで "0" にします。
これはPIC12F1822でも同様です。
例としてノコギリ波を出力、WPUがOffの場合は正常な波形です。
同じ仕様でWPUがOnの場合は歪んだ波形となります。