3チャンネル・クロックジェネレータSi5351Aを使ったVFOは、既にAeduinoやSTM32で製作済みです。
今回は、MPLABX XC8の環境で、PIC16F883で7MHz VFOを作ってみました。
Si5351Aの制御は、Arduinoのプログラムが、そのまま使えました。7MHzのVFOとしましたが、周波数は任意に設定できます。STEPは、ボタンを押すたびに1000Hz→100Hz→10Hz→1000Hzと循環します。
回路図です。
Si5351AとLCD AQM0602Aは、I2Cバスに接続します。プルアップ抵抗は、AQM0802Aモジュールに内蔵されています。
ブレッドボードです。
プログラムです。ちょっと長いですが、参考までに掲載します。
ロータリーエンコーダは、PORTBのピン変化割り込みを利用しています。PIC16F883では、RB0~RB7を利用できますが、割り込みを使うピンを
IOCB0 = 1; //RB0を割り込みピンに指定
IOCB1 = 1; //RN1を割り込みピンに指定
IOCB1 = 1; //RN1を割り込みピンに指定
で指定しています。
プログラムサイズは、メモリ4kワードの90%です。
--------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTRC_NOCLKOUT //内蔵クロックを使う
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#pragma config FOSC = INTRC_NOCLKOUT //内蔵クロックを使う
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
#pragma config LVP = OFF
// CONFIG2
#pragma config BOR4V = BOR40V
#pragma config WRT = OFF
#define _XTAL_FREQ 8000000
/* LCD AQM0802関係定義 */
#define LCD_addr 0x7C //3E<<1
#define LCD_addr 0x7C //3E<<1
/* ロータリーエンコーダ関係定義*/
#define ECA RB0 //エンコーダA
#define ECB RB1 //エンコーダB
unsigned char curDat;
unsigned char befDat;
signed char count= 0;
#define ECA RB0 //エンコーダA
#define ECB RB1 //エンコーダB
unsigned char curDat;
unsigned char befDat;
signed char count= 0;
/* SW関係定義 */
#define SW_step RB2
unsigned int Step = 1000; //Step初期値
unsigned char step_val;
unsigned char step_old = 1;
#define SW_step RB2
unsigned int Step = 1000; //Step初期値
unsigned char step_val;
unsigned char step_old = 1;
/* Si5351A関係定義*/
#define Si5351_ADDR 0xC0 //60<<1
#define MSNA_ADDR 26
#define MS0_ADDR 42
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define PLL_RESET 177
#define XTAL_LC 183
unsigned long Freq = 7000000; //周波数初期値 7MHz
unsigned long Freq_old; //周波数の前の値
const unsigned long XtalFreq = 25000000; //水晶発振器の周波数
unsigned long divider;
unsigned long PllFreq;
unsigned int mult;
unsigned long l;
float f;
unsigned long num;
const unsigned long denom = 1048575;
unsigned long P1;
unsigned long P2;
unsigned long P3;
/* I2C関係関数*/
/* I2C 初期化*/
void I2C_init()
{
SSPCON = 0x28; //SSPEN = 1,I2C Master Mode
SSPSTATbits.SMP = 1; //標準速度モード(100KHz)
SSPADD = 19; //Fosc/(4*Clock)-1 Clock=100kHz,Fosc=8MHz
}
/* I2C 初期化*/
void I2C_init()
{
SSPCON = 0x28; //SSPEN = 1,I2C Master Mode
SSPSTATbits.SMP = 1; //標準速度モード(100KHz)
SSPADD = 19; //Fosc/(4*Clock)-1 Clock=100kHz,Fosc=8MHz
}
/* スタートコンディション */
void I2C_start()
{
SEN = 1;
while(SEN);
}
void I2C_start()
{
SEN = 1;
while(SEN);
}
/* ストップコンディション */
void I2C_stop()
{
SSPIF = 0;
PEN = 1;
while(PEN);
SSPIF = 0;
}
void I2C_stop()
{
SSPIF = 0;
PEN = 1;
while(PEN);
SSPIF = 0;
}
/* I2Cに1byte送信 */
void I2C_write(unsigned char dat)
{
SSPIF = 0;
SSPBUF = dat;
while(!SSPIF);
}
void I2C_write(unsigned char dat)
{
SSPIF = 0;
SSPBUF = dat;
while(!SSPIF);
}
/* write command */
void LCD_cmd(unsigned char cmd){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x00); //send control byte
I2C_write(cmd); //send command
I2C_stop(); //stop condition
}
/* write charactor */
void LCD_char(unsigned char dat){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x40); //send control byte
I2C_write(dat); //send data
I2C_stop(); //stop condition
}
void LCD_cmd(unsigned char cmd){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x00); //send control byte
I2C_write(cmd); //send command
I2C_stop(); //stop condition
}
/* write charactor */
void LCD_char(unsigned char dat){
I2C_start(); //start condition
I2C_write(LCD_addr); //send slave address
I2C_write(0x40); //send control byte
I2C_write(dat); //send data
I2C_stop(); //stop condition
}
/* LCD initialize */
void LCD_init(){
__delay_ms(40); //40ms wait
LCD_cmd(0x38); //8bit,2line
LCD_cmd(0x39); //IS=1 : extention mode set
LCD_cmd(0x14); //Internal OSC Frequency
LCD_cmd(0x70); //Contrast set
LCD_cmd(0x56); //Power/ICON/Contrast Control
LCD_cmd(0x6C); //Follower control
__delay_ms(200);//200ms wait
LCD_cmd(0x38); //IS=0 : extention mode cancel
LCD_cmd(0x0C); //Display ON
LCD_cmd(0x01); //Clear Display
__delay_ms(2); //wait more than 1.08ms
}
void LCD_init(){
__delay_ms(40); //40ms wait
LCD_cmd(0x38); //8bit,2line
LCD_cmd(0x39); //IS=1 : extention mode set
LCD_cmd(0x14); //Internal OSC Frequency
LCD_cmd(0x70); //Contrast set
LCD_cmd(0x56); //Power/ICON/Contrast Control
LCD_cmd(0x6C); //Follower control
__delay_ms(200);//200ms wait
LCD_cmd(0x38); //IS=0 : extention mode cancel
LCD_cmd(0x0C); //Display ON
LCD_cmd(0x01); //Clear Display
__delay_ms(2); //wait more than 1.08ms
}
/* Clear Display */
void LCD_clear(){
LCD_cmd(0x01);
__delay_ms(1);
__delay_us(80);
}
void LCD_clear(){
LCD_cmd(0x01);
__delay_ms(1);
__delay_us(80);
}
/* Return Home */
void LCD_home(){
LCD_cmd(0x02);
__delay_ms(1);
__delay_us(80);
}
void LCD_home(){
LCD_cmd(0x02);
__delay_ms(1);
__delay_us(80);
}
/* Cursor 2line top */
void LCD_2line(){
LCD_cmd(0xC0);
}
void LCD_2line(){
LCD_cmd(0xC0);
}
/* write 1 charactor to LCD */
void putch(unsigned char ch){
LCD_char(ch);
}
void putch(unsigned char ch){
LCD_char(ch);
}
void Freq_disp(){
LCD_home();
printf("%8ld",Freq);
}
LCD_home();
printf("%8ld",Freq);
}
void Step_disp(){
LCD_cmd(0xC4); //cursol 4,1
printf("%4d",Step);
}
LCD_cmd(0xC4); //cursol 4,1
printf("%4d",Step);
}
void Step_change(){
__delay_ms(10);
if(Step==10){
Step = 1000;
}else{
Step = Step / 10;
}
Step_disp();
while(!SW_step);
__delay_ms(10);
}
__delay_ms(10);
if(Step==10){
Step = 1000;
}else{
Step = Step / 10;
}
Step_disp();
while(!SW_step);
__delay_ms(10);
}
/* Si5351A関係関数*/
/* Si5351Aのレジスタにデータ送信*/
void Si5351_write(unsigned char Reg , unsigned char Data)
{
I2C_start();
I2C_write(Si5351_ADDR);
I2C_write(Reg);
I2C_write(Data);
I2C_stop();
}
/* Si5351Aのレジスタにデータ送信*/
void Si5351_write(unsigned char Reg , unsigned char Data)
{
I2C_start();
I2C_write(Si5351_ADDR);
I2C_write(Reg);
I2C_write(Data);
I2C_stop();
}
/* Si5351A初期化 */
void Si5351_init(){
Si5351_write(OUTPUT_CTRL,0xFF); //Disable oll output
Si5351_write(CLK0_CTRL,0x80); //CLOCK0 power down
Si5351_write(CLK1_CTRL,0x80); //CLOCK1 power down
Si5351_write(CLK2_CTRL,0x80); //CLOCK2 power down
Si5351_write(XTAL_LC,0x80); //Crystal Load Capasitance=8pF
Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
Si5351_write(CLK0_CTRL,0b01001111); //CLOCK0 Power up 8mA
Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}
void Si5351_init(){
Si5351_write(OUTPUT_CTRL,0xFF); //Disable oll output
Si5351_write(CLK0_CTRL,0x80); //CLOCK0 power down
Si5351_write(CLK1_CTRL,0x80); //CLOCK1 power down
Si5351_write(CLK2_CTRL,0x80); //CLOCK2 power down
Si5351_write(XTAL_LC,0x80); //Crystal Load Capasitance=8pF
Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
Si5351_write(CLK0_CTRL,0b01001111); //CLOCK0 Power up 8mA
Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}
//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(unsigned char REG_ADDR,unsigned long Pa1,unsigned long Pa2,unsigned long Pa3)
{
Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF));
}
void Parameter_write(unsigned char REG_ADDR,unsigned long Pa1,unsigned long Pa2,unsigned long Pa3)
{
Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF));
}
void PLLA_set()
{
divider = 900000000 / Freq;
divider >>= 1; //dividerは整数かつ偶数
divider <<= 1;
PllFreq = divider * Freq;
mult = PllFreq / XtalFreq;
l = PllFreq % XtalFreq;
f = l;
f *= denom;
f /= XtalFreq;
num = f;
P1 = (unsigned long)(128 * ((float)num /(float)denom));
P1 = (unsigned long)(128 * (unsigned long)mult + P1 - 512);
P2 = (unsigned long)(128 * ((float)num / (float)denom));
P2 = (unsigned long)(128 * num -denom * P2);
P3 = denom;
Parameter_write(MSNA_ADDR,P1,P2,P3);
}
{
divider = 900000000 / Freq;
divider >>= 1; //dividerは整数かつ偶数
divider <<= 1;
PllFreq = divider * Freq;
mult = PllFreq / XtalFreq;
l = PllFreq % XtalFreq;
f = l;
f *= denom;
f /= XtalFreq;
num = f;
P1 = (unsigned long)(128 * ((float)num /(float)denom));
P1 = (unsigned long)(128 * (unsigned long)mult + P1 - 512);
P2 = (unsigned long)(128 * ((float)num / (float)denom));
P2 = (unsigned long)(128 * num -denom * P2);
P3 = denom;
Parameter_write(MSNA_ADDR,P1,P2,P3);
}
void MS0_set()
{
P1 = 128 * divider - 512;
P2 = 0;
P3 = 1;
Parameter_write(MS0_ADDR,P1,P2,P3);
}
{
P1 = 128 * divider - 512;
P2 = 0;
P3 = 1;
Parameter_write(MS0_ADDR,P1,P2,P3);
}
void interrupt isr(){
RBIF = 0; //RB割り込みフラッグクリア
__delay_ms(2);
curDat = ECA + (ECB<<1);
if (befDat != curDat){
unsigned char d = ((befDat<<1)^curDat) & 3; //回転方向判定
if(d < 2){
count++;
}else{
count--;
}
befDat = curDat;
}
if(count >= 4){
Freq += Step;
count = 0;
}
else if(count <= -4){
Freq -= Step;
count = 0;
}
}
RBIF = 0; //RB割り込みフラッグクリア
__delay_ms(2);
curDat = ECA + (ECB<<1);
if (befDat != curDat){
unsigned char d = ((befDat<<1)^curDat) & 3; //回転方向判定
if(d < 2){
count++;
}else{
count--;
}
befDat = curDat;
}
if(count >= 4){
Freq += Step;
count = 0;
}
else if(count <= -4){
Freq -= Step;
count = 0;
}
}
void main() {
ANSEL = 0; //アナログポートを使わない
ANSELH = 0;
OSCCON = 0x70; //内蔵クロック8MHz
TRISA = 0b00000000;
TRISB = 0b00000111; //RB0,RB1,RB2 input
TRISC = 0b00011000; //RC3(SCL),RC4(SDA)input
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
OPTION_REGbits.nRBPU = 0; //PORTB Pull up
I2C_init(); //I2C initiatize
LCD_init(); //LCD initialize
Si5351_init(); //Si5351A initialize
IOCB0 = 1; //RB0を割り込みピンに指定
IOCB1 = 1; //RN1を割り込みピンに指定
RBIE = 1; //RBピン割り込み許可
GIE = 1; //全割り込み許可
befDat = ECA + (ECB<<1);
Freq_old = Freq;
Freq_disp();
PLLA_set();
MS0_set();
Step_disp();
while(1){
if(Freq != Freq_old){
PLLA_set();
MS0_set();
Freq_disp();
}
Freq_old = Freq;
step_val = SW_step;
if((step_val == 0)&&(step_old == 1)){
Step_change();
}
step_old = step_val;
}
}
----------------------------------------------------
ANSEL = 0; //アナログポートを使わない
ANSELH = 0;
OSCCON = 0x70; //内蔵クロック8MHz
TRISA = 0b00000000;
TRISB = 0b00000111; //RB0,RB1,RB2 input
TRISC = 0b00011000; //RC3(SCL),RC4(SDA)input
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
OPTION_REGbits.nRBPU = 0; //PORTB Pull up
I2C_init(); //I2C initiatize
LCD_init(); //LCD initialize
Si5351_init(); //Si5351A initialize
IOCB0 = 1; //RB0を割り込みピンに指定
IOCB1 = 1; //RN1を割り込みピンに指定
RBIE = 1; //RBピン割り込み許可
GIE = 1; //全割り込み許可
befDat = ECA + (ECB<<1);
Freq_old = Freq;
Freq_disp();
PLLA_set();
MS0_set();
Step_disp();
while(1){
if(Freq != Freq_old){
PLLA_set();
MS0_set();
Freq_disp();
}
Freq_old = Freq;
step_val = SW_step;
if((step_val == 0)&&(step_old == 1)){
Step_change();
}
step_old = step_val;
}
}
----------------------------------------------------