元プログラムはこちらです⇒: PIC/16F1705のDAC(DAコンバータ)を使ってみた(ソース付き)

プログラム本体はダウンロードできるようにいつものところにおいてあります。zip解凍にパスコードが必要です。

あまり解説は要らないのかも知れませんが少しだけ。追加したところのみ。

#pragma config WDTE=OFF,FOSC=ECH,PLLEN=ON

FOSC=ECH で外部クロック(高速High)を指定。
PLLEN=ON でソフトウェア内部でクロック4倍を指定。

void main()
{
    OSCCON = 0b11110000;
    DAC1CON0 = DAC1EN | DAC1OE2 | DAC1PSS_VDD | DAC1NSS_VSS;
    OPA2CON = OPAxEN | OPAxSP_Low | OPAxUG_ON | OPAxCH_DAC;

    asm("MOVLB 0x02");  // バンクセレクトレジスタでDAC1CON1のバンクを指定
    while(1) {
        asm("MOVLW 0x80");      // いわゆるインライン展開^^;
        asm("MOVWF DAC1CON1");  // テーブルジャンプだと5サイクル必要なところ
        asm("NOP");             // 即値で書けば命令は2サイクルで済みます
        asm("NOP");             // NOPが2つあるのはループ最後のGOTO分のサイクル長
    :
        asm("MOVLW 0x76");
        asm("MOVWF DAC1CON1");  // この後ろにNOP2つが無いのでぴったり4サイクルで1電圧設定
    }
}

・asm("OPCODE xx"); を使ってアセンブラを書きます。 
・コンパイル時にワーニングが出ますが、バンクセレクトの問題です。
・配列の値を直接DAC1CON1に書き込むようにすると(WREGにイミディエット値ロードとDAC1CON1へのストア)2命令で済みます。
・ループの最後から先頭に戻るGOTO命令が2命令サイクル分かかるので、最後の書き込み以外は仕方なくNOPを2つ追加しています。
・以上4命令を1セットとして64個書きます(ただし最後の64番目はNOPなしの2命令)
・正弦波の上端か下端部分の電圧設定を端折って、63書き込み(上記は64書き込み)NOPなしにすれば周波数はさらに2倍上げることができます。GOTO分が命令2サイクル長なので。
・可変抵抗を1MΩにすればさらに低い周波数も出せるようになりますが、その分、微調整が難しくなると思います。
・PLLENをConfigurationBitで指定しないようにして、ディップスイッチで外部で4倍、1倍を切り替えられるようにできるかも(まだ思いつきレベル)


-----------追記----------

#define マクロを使って4命令をまとめると短く書くことができるようになります。
VOUT(x)というのを定義してパラメータが渡せるようにしました。
ダウンロードしたものとHEXファイルは違いがないはずですが、こちらのほうが見通しがよくなります。

-----------プログラムはここから----------
#include <xc.h>
#include <math.h>
//#define _XTAL_FREQ 500000   // delay用クロック設定は今回不要

// DAC1CON0
#define DAC1EN  0x80
#define DAC1OE2 0x10
#define DAC1PSS_VDD     0x00
#define DAC1NSS_VSS     0x00

// OPAxCON
#define OPAxEN  0x80
#define OPAxSP_High 0x40  // 扱う周波数が高いときはこちらの方が安全でしょう。
#define OPAxSP_Low  0x00
#define OPAxUG_ON   0x10
#define OPAxCH_DAC  0x02

#define VOUT(x) asm("MOVLW "#x); asm("MOVWF DAC1CON1"); asm("NOP"); asm("NOP")

#pragma config WDTE=OFF,FOSC=ECH,PLLEN=ON

void main()
{
    OSCCON = 0b11110000;
    DAC1CON0 = DAC1EN | DAC1OE2 | DAC1PSS_VDD | DAC1NSS_VSS;
    OPA2CON = OPAxEN | OPAxSP_Low | OPAxUG_ON | OPAxCH_DAC;

    asm("MOVLB 0x02");  // バンクセレクトレジスタでDAC1CON1のバンクを指定
    while(1) {
        VOUT(0x80); VOUT(0x89); VOUT(0x93); VOUT(0x9D);
        VOUT(0xA6); VOUT(0xAF); VOUT(0xB7); VOUT(0xBF);
        VOUT(0xC6); VOUT(0xCD); VOUT(0xD3); VOUT(0xD8);
        VOUT(0xDC); VOUT(0xDF); VOUT(0xE2); VOUT(0xE3);
        VOUT(0xE4); VOUT(0xE3); VOUT(0xE2); VOUT(0xDF);
        VOUT(0xDC); VOUT(0xD8); VOUT(0xD3); VOUT(0xCD);
        VOUT(0xC6); VOUT(0xBF); VOUT(0xB7); VOUT(0xAF);
        VOUT(0xA6); VOUT(0x9D); VOUT(0x93); VOUT(0x89);
        VOUT(0x80); VOUT(0x76); VOUT(0x6C); VOUT(0x62);
        VOUT(0x59); VOUT(0x50); VOUT(0x48); VOUT(0x40);
        VOUT(0x39); VOUT(0x32); VOUT(0x2C); VOUT(0x27);
        VOUT(0x23); VOUT(0x20); VOUT(0x1D); VOUT(0x1C);
        VOUT(0x1B); VOUT(0x1C); VOUT(0x1D); VOUT(0x20);
        VOUT(0x23); VOUT(0x27); VOUT(0x2C); VOUT(0x32);
        VOUT(0x39); VOUT(0x40); VOUT(0x48); VOUT(0x50);
        VOUT(0x59); VOUT(0x62); VOUT(0x6C);
        asm("MOVLW 0x76");
        asm("MOVWF DAC1CON1");
    }
}