PIC12F1840とSi5351Aを組み合わせて、7MHzのVFOを試作しました。
回路図です。電源は3.3Vとします。(試作では電池2本3Vとしました。)
3チャンネルクロックジェネレータSi5351Aと表示用のLCD AQM0802Aは、I2Cバスでコントロールします。
RA0~RA5はすべてウィークプルアップして、プルアップ抵抗を省略しています。
STEPボタンを押すたびに周波数ステップが1000Hz→100Hz→10Hz→1000Hzと循環します。
TXボタンを押す(RA0が0になると)とCLK0に7MHzが出力されます。CWの場合は、KEYをここに接続します。
TXボタンを押して、7MHzを出力しているところです。
プログラムです。XC8Ver2.05でコンパイルしました。プログラムサイズは、3631wordsです。
(v2.0でコンパイルするとプログラムサイズがオーバーしてしまいます)
/*
* File: main.c
* Author: JH7UBC Keiji Hata
* PIC12F1840 Si5351A VFO
* Created on 2019/06/28
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
// CONFIG1
#pragma config FOSC = INTOSC
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = OFF
#pragma config CP = OFF
#pragma config CPD = OFF
#pragma config BOREN = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO = OFF
#pragma config FCMEN = OFF
// CONFIG2
#pragma config WRT = OFF
#pragma config PLLEN = ON
#pragma config STVREN = ON
#pragma config BORV = HI
#pragma config LVP = OFF
#define _XTAL_FREQ 32000000
#define LCD_addr 0x7C //3E+0
/* ロータリーエンコーダ関係定義*/
#define ECA RA5 //エンコーダA
#define ECB RA4 //エンコーダB
unsigned char curDat;
unsigned char befDat;
signed char count= 0;
/* Si5351A関係定義 */
#define Si5351_ADDR 0xC0 //60<<1
#define MSNA_ADDR 26
#define MS0_ADDR 42
#define MS1_ADDR 50
#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
const int dF = 116; //周波数補正値
unsigned long Freq_old; //周波数の前の値
const unsigned long XtalFreq = 25000000; //PLL クリスタル周波数
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;
/* STEP関係定義 */
unsigned int Step = 1000;
#define STEP_SW RA3
/* TX 関係定義 */
#define TX_SW RA0
unsigned char val=1; //TX_SWの値
unsigned char old_val = 1; //TX_SWの前の値
/* I2C intialize */
void I2C_init(){
SSP1CON1 = 0x28; //SSPEN = 1,I2C Master Mode
SSP1STATbits.SMP = 1; //standard mode(100KHz)
SSP1ADD = 0x4F; //Fosc/(4*Clock)-1 Clock=100kHz,Fosc=32MHz
}
/* start condition */
void I2C_start(){
SEN = 1;
while(SEN);
}
/* stop condition */
void I2C_stop(){
SSP1IF = 0;
PEN = 1;
while(PEN);
SSP1IF = 0;
}
/* write 1byte to I2C */
void I2C_write(unsigned char dat){
SSP1IF = 0;
SSP1BUF = dat;
while(!SSP1IF);
}
/* 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){
unsigned char ackn;
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
}
/* Return Home `*/
void LCD_home(){
LCD_cmd(0x02);
__delay_ms(1);
__delay_us(80);
}
/* Cursor x,y */
void LCD_cursor(unsigned char x,unsigned char y){
if(y == 0){
LCD_cmd(0x80 + x);
}
if(y == 1){
LCD_cmd(0xC0 + x);
}
}
/* write 1 charactor to LCD */
void putch(unsigned char ch){
LCD_char(ch);
}
/* Si5351A関係関数*/
/* Send data to Si5351A register*/
void Si5351_write(unsigned char Reg , unsigned char Data)
{
I2C_start();
I2C_write(Si5351_ADDR);
I2C_write(Reg);
I2C_write(Data);
I2C_stop();
}
/* Si5351A initialize */
void Si5351_init(){
Si5351_write(OUTPUT_CTRL,0xFF); //Disable all 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,0xAC); //Reset PLLA and PLLB
Si5351_write(CLK0_CTRL,0b01001111); //CLOCK0 Power up 8mA
Si5351_write(OUTPUT_CTRL,0xFF); //Disable CLK0?CLK2 output
}
/* Send parameter to register */
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));
}
/* Set frequency */
void VFO_set(unsigned long frequency){
/* Set PLLA */
frequency += dF; //周波数補正
divider = 900000000 / frequency;
divider >>= 1; //dividerは整数かつ偶数
divider <<= 1;
PllFreq = divider * frequency;
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);
/* Set MS0 */
P1 = 128 * divider - 512;
P2 = 0;
P3 = 1;
Parameter_write(MS0_ADDR,P1,P2,P3);
}
/* Display Frequency */
void Freq_disp(unsigned long frequency){
LCD_home();
printf("%8ld",frequency);
}
/* Display Step */
void Step_disp(unsigned int stp){
LCD_cursor(4,1);
printf("%4d",stp);
}
/* STEP Change */
void Step_change(){
__delay_ms(5); // チャタリング待ち
if(Step == 10){
Step = 1000;
}else{
Step /= 10;
}
Step_disp(Step);
while(!STEP_SW){
__delay_ms(5);
}
}
/* 割り込みサービスルーチン */
void __interrupt() isr(){
IOCIF = 0; //割り込みフラッグクリア
IOCAF = 0;
__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 Tx_send(){
if(val == 0){
Si5351_write(OUTPUT_CTRL,0xFE);
}else{
Si5351_write(OUTPUT_CTRL,0xFF);
}
__delay_ms(2);
}
/* main program */
void main() {
OSCCON = 0b01110000 ; // クロック8MHz
ANSELA = 0b00000000 ; // アナログを使わない
TRISA = 0b00111111 ; // RA0-RA5入力
PORTA = 0b00000000 ; // PORTAクリア
OPTION_REGbits.nWPUEN=0; //ウィークプルアップ許可
WPUA = 0b00111111; //RA0-RA5をプルアップ
/* 割り込み関係設定 */
IOCIE = 1; //状態変化割り込み許可
IOCAP = 0b00110000; //RA4,RA5立ち上がりエッジ検出
IOCAN = 0b00110000; //RA4,RA5立ち下りエッジ検出
PEIE = 1; //周辺割り込み許可
GIE = 1; //全割り込み許可
I2C_init();
LCD_init();
Si5351_init();
/* Rotary Encoder 初期値 */
befDat = ECA + (ECB<<1);
/*周波数とSTEP初期値表示*/
Freq_disp(Freq);
VFO_set(Freq);
Freq_old = Freq;
LCD_cursor(0,1);
printf("STEP");
Step_disp(Step);
while(1){
if(STEP_SW == 0){
Step_change();
}
val = TX_SW;
if(val != old_val){
Tx_send();
}
old_val = val;
if(Freq != Freq_old){
Freq_disp(Freq);
VFO_set(Freq);
Freq_old = Freq;
}
}
}