AD9833 オシレータの製作
トランジスタ技術 2024年10月号に載っていた、AD9833を使ったオシレータをPICで作ってみた。
フィルタの部分はトラ技を見ていただくとして、周波数指定・表示の部分を記載する。
いつもの通り動作や安全性、その他一切保証しませんし、責任も負いませんので、内容を確認の上自己責任で、よろしければご利用ください。
1.構想
・サイン波、三角波、矩形波が選べることとする。
・周波数は上限の12.5MHzまで設定できるものとする。
・周波数の有効数字は4桁(10MHz以上は3桁・・・手抜きプログラムのため)。
・表示はI2CのLCDを用いる。2.54mmピッチで使えるAQM1602Y-NLW-FBWを選択した。
・PICは使い慣れたPIC16F1503(安いし・・・でもちょっと苦労した)。
・設定するボタンは減らす→機能ボタンと、変更ボタンの2つとする。
・AD9833ボードは、トラ技に載っていた写真に近いものをアマゾンで購入。少々不安もあったが、問題なく動いている。
・電源は5Vで動くようにする→3.3Vの降圧回路を追加する。
2.回路
・PICと各モジュールを単純につないだもの。
・シリアル転送はソフトウェアで対応した。シリアル転送はきっとハードが実装されているのだろうと思ったが、AD9833のマニュアルを見たらソフトで作れそうだったのと、PICのマニュアルを読み込むのが面倒で・・・。
・LCDの線は細く、少々接触不良気味。例えば表裏を逆にして、直接半田付けする等を考えたほうがよいかもしれない。

3.ソフトウェア
・出力周波数とAD9833へ書き込む周波数データの関係は以下の通り
fdata=f*10.73742・・・
fdata:AD9833へ書き込む周波数データ
f:出力周波数
当初、Floatで書き込みデータを計算しようとしたところ、メモリ不足となってしまった。ハードは作ってしまっていたので焦ったが(荒井注カラオケ状態)、別方法で対応できた。
・I2Cコントロール、およびLCD操作モジュールは、以前ここで公開したものを流用した。幸いLCDのI2Cアドレスも同じで、修正せずに使うことができた。
・MCCを用いてクロック周波数、ピンアサイン、I2C設定を行った。初めて使ってみたが思ったより楽なので、今後開発をする方にはおすすめ。ピン名称を付けられるのは良かったが、結局使い方はソースを見て調べる必要があった・・・考えてみればあたりまえである。
・RA4,RA5がInput。RC2,3,4がOutput。Pin9、10でI2C接続。
・PICのクロックは4MHzとした。
・FSYNK=Lowにした後の待ち時間は、オシロで波形を見ながらエイヤで決めた。
・MHz設定時の値チェックは行っていないので、12.5MHzを超えぬよう注意のこと。
/*
AD9833 Oscilator
2025.4.13 Shiro46tan
著作権は放棄しません
商用利用不可
*/
#include "mcc_generated_files/system/system.h"
void i2cTxData(char data);
void i2c_lcd_cmd(unsigned char data);
void i2c_lcd_data(unsigned char data);
void LCD_Init(void);
void lcd_str(char * str);
void lcd_home(void);
void send_AD9833(unsigned int);
void exec_AD9833(void);
void disp_lcd(void);
/*
Main application
*/
char csr[6]={0xc4,0xca,0xc6,0xc7,0xc8,0xc9}; //カーソルの位置
char val[6]={0,0,0,4,4,0}; //波形の種類、周波数の桁、数字4桁
char maxval[6]={2,2,9,9,9,9}; //各値の最大値
char place=0;
int main(void)
{
//editモードから抜けるときflag=1になる
char flag=0;
//MCCが作った初期設定。概要は説明文にある。
SYSTEM_Initialize();
//LCD Initialize
LCD_Init();
lcd_str(" Oscilator V1.0");
i2c_lcd_cmd(0xcB); //2行目にアドレスセット
lcd_str("Hz");
// 周波数の表示
disp_lcd();
// AD9833にデータを送る
exec_AD9833();
while(1)
{
//SW1が押されたかチェックする
if(SW1_GetValue()==0){
__delay_ms(100);
while(SW1_GetValue()==1){} //edit mode に入る
__delay_ms(100);
flag=0;
//カーソルBlinkをOnにして、変更点にカーソルを動かす
i2c_lcd_cmd(0x0d);
i2c_lcd_cmd(csr[place]);
while(flag==0){
if(SW1_GetValue()==0){ //SW1が押されたら位置を変える
__delay_ms(100);
while(SW1_GetValue()==0){}
__delay_ms(100);
place++;
//最後まで進んだら、editモードを抜ける
if (place==6){
place=0;
flag=1;
}
//カーソル位置を動かす
i2c_lcd_cmd(csr[place]);
}
//SW2 が押されたら、その位置の値を変更する
if(SW2_GetValue()==0){
__delay_ms(100);
while(SW2_GetValue()==0){}
__delay_ms(100);
val[place]++;
if (val[place]>maxval[place]){
val[place]=0;
}
//値を表示し直す
disp_lcd();
i2c_lcd_cmd(csr[place]);
}
}
//カーソルBlinkをOffにする
i2c_lcd_cmd(0x0c);
//AD9833にデータを送る
exec_AD9833();
}
}
}
void exec_AD9833() {
unsigned int LSB;
unsigned int MSB;
unsigned int cmd;
unsigned long freq;
unsigned long f;
// コマンドを決める
if (val[0]==0)
cmd=0x2000;
else if (val[0]==1)
cmd=0x2002;
else
cmd=0x2028;
// 周波数データを計算する
freq=val[2]*1000+val[3]*100+val[4]*10+val[5];
//kHz表示のとき
if (val[1]==1) freq=freq*1000;
//MHz表示のとき
if (val[1]==2) freq=(val[2]*100+val[3]*10+val[5])*100000;
//AD9833に送る周波数データ
f=freq*10+freq*7/10+freq*3/100+freq*7/1000;
LSB=(f&16383)|16384;
MSB=((f>>14)&16383)|16384;
send_AD9833(cmd);
send_AD9833(LSB);
send_AD9833(MSB);
}
// AD9833 へコマンドとデータを送る
void send_AD9833(unsigned int data){
char i;
unsigned int j;
FSYNC_SetHigh();
SCLK_SetHigh();
FSYNC_SetLow();
j=32768;
for(i=16;i>=1;i--){
if ((j & data)>0)
SDATA_SetHigh();
else
SDATA_SetLow();
j=j/2;
SCLK_SetLow();
__delay_us(38);
SCLK_SetHigh();
}
FSYNC_SetHigh();
}
void disp_lcd()
{
// 0/が多いのはキーを押した際に表示がおかしくなったから
char swave={' ',' ',' ','\0','\0','\0','\0'};
char sfreq={' ',' ',' ',' ',' ','\0','\0','\0','\0'};
char tmp;
char i;
// 波形の定義
if (val[0]==0){
swave[0]='S';
swave[1]='i';
swave[2]='n';
}
if (val[0]==1) {
swave[0]='T';
swave[1]='r';
swave[2]='i';
}
if (val[0]==2){
swave[0]='S';
swave[1]='q';
swave[2]='u';
}
for(i=0;i<4;i++){
sfreq[i]=val[i+2]+48;
}
// MHzのときの処理
if (val[1]==2) {
sfreq[2]='.';
}
if (val[1]==1) sfreq[4]='k';
if (val[1]==2) sfreq[4]='M';
//書き込み
i2c_lcd_cmd(0xc2);
lcd_str(swave);
i2c_lcd_cmd(0xc6);
lcd_str(sfreq);
}
//2) 1byteデータ送信
void i2cTxData(char data){
SSP1IF = 0; // 終了フラグクリア
SSPBUF = data;
while(!SSP1IF); // 送信終了まで待つ
}
void i2c_lcd_cmd(unsigned char data){
SEN = 1; // Start condition 開始
while(SEN); // Start condition 確認
i2cTxData(0x7c);
i2cTxData(0x00);
i2cTxData(data);
SSP1IF = 0; // 終了フラグクリア
PEN = 1; // Stop condition 開始
while(PEN); // Stop condition 確認
}
void i2c_lcd_data(unsigned char data){
SEN = 1; // Start condition 開始
while(SEN); // Start condition 確認
i2cTxData(0x7c);
i2cTxData(0x40);
i2cTxData(data);
SSP1IF = 0; // 終了フラグクリア
PEN = 1; // Stop condition 開始
while(PEN); // Stop condition 確認
}
void LCD_Init(){
__delay_ms(50);
i2c_lcd_cmd(0x38);
__delay_us(30);
i2c_lcd_cmd(0x39);
__delay_us(30);
i2c_lcd_cmd(0x14);
__delay_us(30);
i2c_lcd_cmd(0x77);//70
__delay_us(30);
i2c_lcd_cmd(0x56);//56
__delay_us(30);
i2c_lcd_cmd(0x6c);
__delay_ms(200);
i2c_lcd_cmd(0x38);
__delay_us(30);
i2c_lcd_cmd(0x0c);
__delay_us(30);
i2c_lcd_cmd(0x01);
__delay_ms(2);
i2c_lcd_cmd(0x80);
__delay_us(30);
}
//////// 文字列出力関数
void lcd_str(char *str){
while(*str != 0x00){ // 文字列の終わり判定
i2c_lcd_data(*str);
str++; //ポインタ+1
}
}
4.ケース
自作時のポイントの1つは、ケース加工。大概はアルミケースになるが、加工が少々面倒だったり、うまくできなかったり。特に四角い穴をきれいに開けるのは、不器用な私には至難の業。そこで最近は、100均の合版+角材を用いている。大きさが自由に決められるのも良し。マスキングテープを貼れば塗装不要だし、そこそこ見栄えがよくなる・・・写真でみるとアラが多いが・・・。

PIC24FJ64GA002 のプログラム領域ROMをデータROMとして使う
1.はじめに
PIC24Fはデータ記憶用のROMがなく、データの保存にはプログラム領域のROMを用いる必要があるらしい。
とあることから、300程度のINTデータを保存しておきたくなったのだが、インターネット上でざっと私が調べた範囲では、多くても1ページ(3Byte×64)を使うもので、ページをまたがってデータを保存する方法が簡単に書かれたものはなかった。使い方を理解するのに相当の時間を費やしたので、ここにまとめておく。
ただしまだ完全とは言えず、データROMに使う領域を変更すると、一部使えなくなることがある(詳細は後述)。つまり、ここに書いてあることは、まったくの間違いという可能性もある。このプログラムを使う際はトライアルを厳重にする等、十分注意をお願いします。
なお、本調査にあたっては、以下のブログを参考にさせていただいた。
あらためてお礼を申し上げるとともに、リンクフリーとのことなので、リンクを張らせていただく。まず、こちらを読んだほうが理解が深まると思う。
ハム三昧さん
http://jr4pdp.blog.enjoy.jp/myblog/2019/05/pic24f64ga004ee-d37e.html
2.前提条件
・利用したPICは、PIC24FJ64GA002
その他のPIC24でも使えると思う(未確認)。その場合はメモリマップを参照の上、利用する領域を適宜変更のこと。
・INT(16Bit)データを読み書きする。
・データの消去単位である1536Byteを対象とするが、1/3は使いにくいので使わないこととし、1024Bit(=Intで512個)のデータを読み書きする。
・コンパイラは、X16 V1.24。古いがご勘弁を。
3.使い方
3-1.事前準備
(1) ヘッダーファイルの準備
p24FJ64GA002.h
をインクルードする。私の環境では、
C:\Program Files (x86)\Microchip\xc16\v1.24\support\PIC24F\h
にあった。
(2)データROM領域の定義
コンフィギュレーションビット設定の前に
const __attribute__*1 unsigned int _flash_datas[96*8];
を追記する。
上記は、0x9C00~0xA1FFをデータROMにする例である。
このスタートアドレス(0x9C00)は、0x600 ごとに設定できる(0x9000,0x9600,0x9C00,0xA200・・・)。
*なぜか0x9600、0xA200に設定した場合は、半分の256個までしか使えなかった。
理由は不明。間違っているのかもしれない と書いたのはこれが理由。
3-2.書き込み
da_tmp にあるデータを書き込みたい場合は以下とする。
複数ページに書き込む場合、要はoffsetを128づつインクリメントしていけばよい。
void Flash_Write() {
unsigned int i=0;
unsigned int j=0;
unsigned int page = __builtin_tblpage(&_flash_datas);
unsigned int offset = __builtin_tbloffset(&_flash_datas);
// ページ消去
TBLPAG = page;
__builtin_tblwtl(offset, 0x0000); //
NVMCON = 0x4042;
asm volatile ("disi #5");
__builtin_write_NVM(); //erase 8line = 64*8
while(NVMCONbits.WR);
// メモリー書き込み
for(j=0;j<8;j++) {
NVMCON = 0x4001;
TBLPAG = page;
for (i=0; i<64; i++) {
__builtin_tblwtl(offset + i*2, da_tmp[i+j*64]); // *2 偶数
__builtin_tblwth(offset + i*2, 0xFF);
}
asm volatile ("disi #5");
__builtin_write_NVM();
while(NVMCONbits.WR);
offset=offset+128;
}
}
3-3. 読み込み
da にデータを読み込む場合は以下。
こちらも、offsetを128づつインクリメントする。
void Flash_Read() {
unsigned int i=0;
unsigned int j=0;
unsigned int page = __builtin_tblpage(&_flash_datas);
unsigned int offset = __builtin_tbloffset(&_flash_datas);
// フラッシュメモリー読み込み
TBLPAG=page;
for(j=0;j<8;j++){
for(i=0;i<64;i++){
da[i+j*64] = __builtin_tblrdl(offset + i*2);
}
offset=offset+128;
}
}
*1:section(".Data_Memory"), space (prog), address (0x9c00)
I2C LCDディスプレイ ACM1602NI-FLW-FBW-M01 の使い方
メモ
・IDは0X50(1010000)+0なので、送信するIDは0xA0
・I2Cのクロックは100kHzまで
・コマンド送信は、ID(0xA0)、0x00、コマンド
・データ送信は、ID(0XA0)、0X80、データ
・コマンド、データ送信の間は5ms空ける
・Initは、以下コマンドを送信する(詳細は利用形態に合わせて変更方)
0x01,0x38,0x0f,0x06
・表示前に、1行目なら0x80、2行目なら0xc0のコマンドを送る(パラレルと同じ)
RDA5807を使ったラジオ
0.まずはお約束
1.回路
これで良しとした。
・PIC,74HC4511,7セグLEDを1枚に、RDA5807とパワーアンプを1枚に載せる。
・電源は5V、または単3×3(ニッケル水素電池の充電式)とする。
・私は赤の7セグLEDを利用した。暗いという方は抵抗を調整してください。


2.ソフト
・マニュアルによれば、RDA5807のIDは10H。1Bit左シフトして、20Hを送信となる。
・レジスタへのデータ書き込み方法は、レジスタアドレスが自動INCされるので、それに合わせて順にデータを送る方法。この方法では、電源ON時に最後までデータを書き込めないことがあった。
・ネットにてレジスタアドレスとデータをペアで送る裏コマンドを発見(ID:11h。1Bit左シフトで22h)。これを用いる方法に変更した。
・それでも電源ON時はなぜか周波数が書き込めなかったので、電源ON時に限り周波数を2回書き込んでいる。個体ばらつきによっては、これでも電源ON時は動かないかもしれない。その場合はWait(プログラム中のdelay_ms(1000) )を大きくする。
・10ch分のプリセットは、関東地区用。周波数を変える場合は、freq[]の値を変える。
計算式は、(freq(kHz)-760000)/100。0chは別掲のTransmitterに合わせた。
3.動作確認
・電源を入れると、7セグLEDが約0.5秒間0を表示して消灯する。スピーカからは、ホワイトノイズまたは音声が聞こえる。
・SWを押すと、7セグLEDは順にカウントアップ、9の後は0になる。それに伴い受信局が変化すれば完成。
・電流は、5V、LED OFF、音量0で25mA前後。
4.プログラム
XC8 V1.34用。
/* File: RDA5087radio.c
*
* By Shiro46tan
* 2021/5/7
*/
// --PIC16F1503--
// SDA : PORTC(1) pin 9
// SCL : PORTC(0) pin10
// 4MHz
#include <xc.h>
#define _XTAL_FREQ 4000000
#define SW1 PORTAbits.RA0
#define LED0 PORTCbits.RC2 //Bit0
#define LED1 PORTAbits.RA2 //Bit1
#define LED2 PORTAbits.RA4 //Bit2
#define LED3 PORTCbits.RC3 //Bit3
#define LED4 PORTCbits.RC5 //LED ON/OFF
#define PL PORTAbits.RA5 //LED for check
//************* Config ***********************************
#pragma config FOSC = INTOSC, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = ON
// Watch Dog timer をOffにしないと、途中でリセットがかかる可能性あり
#pragma config BOREN = ON, CLKOUTEN = OFF
// CLKOUTをRA4として使う
#pragma config WRT = OFF
#pragma config STVREN = ON, BORV = LO, LPBOR = OFF, LVP = OFF
//********************************************************
void i2cTxData(char);
void SendCMD(void);
void SleepIn(void);
void i2cStart(void);
void i2cStop(void);
void reset(void);
void send(unsigned char, unsigned char, unsigned char);
//初期設定
char VOLUME=0x08;
int freq[10]={0x3138,14500,15600,17000,0x7d0,0xdac,0xfa0,0x14b4,0x1964,0x21fc};
// 受信周波数 88.6 90.5 91.6 93 78 79.5 80 81.3 82.5 84.7
//
//
int RcvFreq=0x3138; //(受信周波数-76000KHz)/100 88.6MHz
char ch=0;
//////////// Main //////////////////////////////////
void main(void){
int i=0,j=0;
unsigned char num = 0;
OSCCON = 0b01101000; // 4MHz
__delay_ms(100);
ANSELA = 0x0;
ANSELC = 0x0; // PortCをデジタルI/Oにする
TRISC = 0b11000011; // PortC I/O設定 1がInput
TRISA = 0b11000011;
SSPCON1 = 0x28; // I2Cマスター
SSPSTAT = 0x00;
SSPADD = 0x24; // I2C周波数100KHz for 4MHz
WPUA=0b00000011; // pullup
OPTION_REGbits.nWPUEN=0; //
LED4=0;
__delay_ms(1000); //十分なWaitを取る
RcvFreq=0x3138;
reset();
__delay_ms(100);
SendCMD();
send(0x08,0x31,0x38);
INTCONbits.IOCIE=1; //IOC(状態変化割り込み)Enable
IOCAPbits.IOCAP0=1; //Port RA0(=SW1)ボタン割り込み
INTCONbits.GIE=1; //INTグローバルEnable
while(1){
SleepIn();
}
}
void i2cStart(void){
SEN = 1; // Start condition
while(SEN); // Start condition
}
void i2cStop(void){ //i2c STOP
SSP1IF = 0; // クリア
PEN = 1; // Stop condition
while(PEN); // Stop condition
}
void i2cTxData(char data){
SSP1IF = 0; //
SSPBUF = data; //
while(!SSP1IF); //
}
void send(unsigned char ch,unsigned char i,unsigned char j){
i2cStart();
i2cTxData(0x22); // スレーブアドレス 0010001+0
i2cTxData(ch); // channel
i2cTxData(i); // upper
i2cTxData(j); // lower
i2cStop();
}
void reset(void) {
send(0x02,0xa0,0x03);
}
void SendCMD(void){
//02H
send(0x02,0xd0,0x01);
// Mono+Bass Boost disable seekしない
// 32.768k Power On
//03H
send(0x03,0x00,0x04);
//Band=1
//04H
send(0x04,0x0a,0x00);
//De-Emphasis50us
//I2S,GPIO 使わない
//05H
send(0x05,0x88,0x80+VOLUME);
//
//Volume 0 - 15
//06H
send(0x06,0x00,0x00);
//I2Setc 使わない
//07H
send(0x07,0x40,0x03);
//0Bit=1にして、08Hに周波数を書き込む
//08H
send(0x08,RcvFreq>>8,0x00ff & RcvFreq);
//chの表示
LED4=1; //LED ON
LED0=ch&1;
LED1=(ch>>1)&1;
LED2=(ch>>2)&1;
LED3=(ch>>3)&1;
__delay_ms(1000); //1秒待つ
LED4=0; //LED OFF
}
void SleepIn(void){
IOCAPbits.IOCAP0=1; //RA0(=SW3)ボタン割り込みON
PL=0;
SLEEP();
}
void interrupt isr(void)
{
if(IOCAFbits.IOCAF0){ //RA0(=SW1)ボタンが押されたとき
while(SW1==0); //離すのを待つ
ch++;
if(ch>=10){ //ch=10になったら0にする
ch=0;
}
RcvFreq=freq[ch]; //受信周波数をfreq[ch]にする
SendCMD();
__delay_ms(100);
IOCAFbits.IOCAF0=0; //clear flag
}
}
QN8027使用 FM Transmitterの製作
0.お約束
念のため。確認ください。
1.回路
以下で動作したので、これでよしとする。

2.製作
・QN8027のピッチは0.5mmピッチなので、変換基盤を使う。
変換基盤への半田付けは、フラックス塗でべた付け後、フラックスを吸わせた半田吸い取り線で余分な半田を除去した(トラ技式)。
・0.12uHのコイルは、エイヤでφ8のドリルに10回巻。
・LEDは動作チェック用。省略可。
・回路上にはICPSのコネクタを書いたが、現物では省略した。
写真:製作した基盤。一部1uFのチップコンを使ったため、すべての部品は見えない。

電源は、本機の移動が不要なことから、スマホ用の5V電源を利用した。
3.ソフト
・周波数は88.6MHzとした。
88.6=76+0.05*252 なので、レジスタ値は0xFCとする。
・無音後約58秒で電源を切れるようにした。
・I2C通信の前の100msウェイトは必須。無いと不安定になったり、データの書き込みができなかったりする。
・WatchDock TimerをOffにしないと、途中で突然リセットがかかることがある。
4.動作確認
・PIC16F1503にプログラムが正常に書き込まれていれば、LEDが0.5秒程度光った後消える
・5V電源を使った場合の電流は13mA、無音1分後(電波発射なし)の時で5mA程度。
5.注意
電波法に違反せぬよう利用のこと。
6.プログラム
XC8 V1.34用。
/*
* transmit.C
* By Shiro_46
* 2021.2
*/
// --PIC16F1503--
// SDA : PORTC(1) pin 9 I2Cで使う場合は入力にすること
// SCL : PORTC(0) pin10 I2Cで使う場合は入力にすること
// 他IOは、RA0~5、RC2~5
// 4MHz
#include <xc.h>
#define _XTAL_FREQ 4000000
#define PL PORTCbits.RC4 //チェック用LED
//************* Config ***********************************
#pragma config FOSC = INTOSC, WDTE = OFF, PWRTE = OFF, MCLRE = OFF, CP = ON
// Watch Dog timer をOffにしないと、途中でリセットがかかる可能性あり
#pragma config BOREN = ON, CLKOUTEN = OFF
#pragma config WRT = OFF
#pragma config STVREN = ON, BORV = LO, LPBOR = OFF, LVP = OFF
//********************************************************
void i2cStart(void); // Start Condition
void i2cStop(void); //Stop Condition
void i2cTxData(char); // i2c 8Bitデータ送信
void QN8027Send(unsigned char,unsigned char);
void Init(void);
//////////// Main //////////////////////////////////
void main(void){
OSCCON = 0b01101000; // CLOCK 4Mhz
ANSELA = 0x0; // PortAをすべてデジタルにする
ANSELC = 0x0; // PortCをすべてデジタルにする
TRISC = 0b11000011; // RC0,1が入力(2~5は出力)
TRISA = 0b00000000; // RAはすべて出力
SSPCON1 = 0x28; // I2Cマスターとする
SSPSTAT = 0x00; // I2Cバスをオープンにする
SSPADD = 0x09; // I2C Clock 100KHz for 4MHz
__delay_ms(100); //送信の前にWaitを入れること。入れないと不安定になる。
PL=0; //LED消灯
Init();
SLEEP();
}
void Init(void){
PL=1;
QN8027Send(0x00,0x80); //reset to default
QN8027Send(0x01,0xfc); //Freq=88.6MHz F=76+0.05*0xFC
QN8027Send(0x02,0x09); //PreEmphasis50 etc 0x09にすると、58秒無音でOffになる 0x39ならOnのまま
QN8027Send(0x04,0xd9); //RegVga Xtal=24MHz Input Gain 12dB Z=10k
QN8027Send(0x10,45); //TXPow Setting
QN8027Send(0x00,0x20); //Transmit On
__delay_ms(500);
PL=0;
}
// QN8027 のアドレスにデータを送る
// アドレス、データを渡す
//QN8027 のデバイスIDは0x2C
void QN8027Send(unsigned char reg,unsigned char data){
i2cStart(); //i2c Start
//QN8027のデバイスID 0X2C+PICからの送信(0)=0x58
i2cTxData(0x58); //
i2cTxData(reg); // レジスタ番号の送信
i2cTxData(data); // データの送信
i2cStop(); //i2c Stop
}
void i2cStart(void){
SEN = 1; // Start condition 送信
while(SEN); // Start condition 終了待ち
}
void i2cStop(void){ //i2c STOP
SSP1IF = 0; // 終わり
PEN = 1; // Stop condition 送信
while(PEN); // Stop condition 終了待ち
}
//-------- SSPBUFに1文字保存し送信終了を待つ -----------------
void i2cTxData(char data){
SSP1IF = 0; // 終了フラグクリア
SSPBUF = data; // データセット
while(!SSP1IF); // 送信終了待ち
}
PIC16F1503 のI2C通信
今回PICでデータ受信はしないので、データ送信のみできればよい。
やり方だけ書く。
1.準備
・SCL,SDAピンは入力モードに指定する
・SSPCON1のSSPMビットをセットする→マスタモードにするため
2.1バイト送信
1)~3)の手順でOK。
1) Start Conditionの送信
void i2cStart(void){
SEN = 1;
while(SEN);
}
2) 1byteデータ送信
void i2cTxData(char data){
SSP1IF = 0; // 終了フラグクリア
SSPBUF = data;
while(!SSP1IF); // 送信終了まで待つ
}
3)Stop Conditionの送信
void i2cStop(void){ //i2c STOP
SSP1IF = 0; // 終了フラグクリア
PEN = 1;
while(PEN);
TV音声トランスミッタ(とFMラジオ)の製作
0.はじめに
TV画面がどんどん大きくなっている。
画面が大きくなると、離れて見なければならない。
離れると、音声は大きくしないといけない。
近くを通るとうるさい。
だんだん耳も悪くなってきた。
ますますうるさい。
そこで、TV音声を耳元で聞くシステムを作ることにした。
1.構想
・どうせなら音の良いFMにしたい
・安定度の高いFMトランスミッタとラジオをアナログ回路で作るのは、私の腕では難しい
→1チップのFMトランスミッタを使う
DSPラジオを使う
・どうせなら通常のFM放送も聞けるようにしたい。
ただし、TV音声は電源Onですぐ聞けるようにしたい
→チューニングボリュームは無いほうがよい
→I2Cを使ったラジオにして、
電源On時には、TV音声が聞けるようにする
チューニングボタンを押すと、通常のFM放送が聞けるようにする
・TV放送と一般放送を合わせて、10chとする(これだけあれば十分と考えた)
・あまり大きくしたくない
→ラジオはモノラルにする
ただし、トランスミッタは将来の気変わりに備えてステレオにしておく。
・どうせならAM放送も聞けるようにと思ったが、今回はあきらめる
N〇Kラジオは卒業した
民放AMはFMで聞ける
・電池で駆動できること
2.ICの選定
・I2Cのデータ送信は、PIC16F1503を使うことにした
私がPICKITを持っている
安い
I2C通信が簡単にできる
・FMトランスミッタはQN8027、ラジオはRDA5807を使う
安い
・オーディオパワーアンプはNJM2073にする→ノイズ大のためHT82V739に変更
手持ちの関係で
・チャンネル表示用に、7セグLEDとデコーダ(74HC4511)を使う。
デコーダを使うほうが、PICの出力を減らせる(実際は余ってますが)
プログラムが楽になる
・電源は3.3Vが必要。→電池3本から、レギュレータで3.3Vを作る
→レギュレータはS-812C33AY(手持ちの関係で)