#1.目的
12MHzの外部オシレータにPLLをかけて、Foscを48MHzにした後UART(PIC18F14K50ではEUSARTという。)の通信を行なうことが目的。
#2.早速つまずいた
例のごとく早速つまずきました。
一番初めに書いたコードは以下。
/*
* File: main.c
* Author: kashiharaakira
*
* Created on April 16, 2016, 8:35 PM
*/
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <p18f14k50.h>
#define _XTAL_FREQ 48000000
#pragma config CPUDIV = NOCLKDIV
#pragma config USBDIV = OFF
#pragma config FOSC = HS
#pragma config PLLEN = ON
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config PWRTEN = OFF
#pragma config BOREN = OFF
#pragma config BORV = 30
#pragma config WDTEN = OFF
#pragma config WDTPS = 32768
#pragma config MCLRE = OFF
#pragma config HFOFST = OFF
#pragma config STVREN = ON
#pragma config LVP = OFF
#pragma config XINST = OFF
#pragma config BBSIZ = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF
void putch(unsigned char ch) {
while(!TXIF);
TXREG = ch;
}
void DelayMs(int dlyms){
while(dlyms--){
__delay_ms(1);
}
}
void main(void) {
OSCCON = 0b00000000;
TRISB = 0xF0;
RCSTA = 0b10010000;
TXSTA = 0b00100100;
BAUDCON = 0b00001000;
SPBRGH = 0;
SPBRG = 1249;
__delay_ms(10);
while(1){
printf("Hello World!\n");
DelayMs(1000);
}
}
ここで肝になるのはTXSTA、BAUDCON、SPBRGH、SPBRGレジスタ。
上のコードを動かしてみると、文字化けしてとても読める文字列を返してこない。
#3.解決策
##3.1.考えられる原因
・オシレータの精度
・PLLの精度
・Baudrateの何かしらの不具合
#3.3.実験してみた
長くなるので詳細は省きますが、結果一番高いのはBaundrateの何かしらの不具合orPLLの精度でした。
理由としては、オシレータの精度はオシロスコープで見た波形から誤差がほぼ無く綺麗なものでした。PLLの精度に関しては、内部オシレータのPLLは正常に行なえUARTの送受信は正常に行えた。しかし、周波数が高い場合特性が変わることは否定出来ない。最後のBaudrateの設定は、データシートの対応表を元にコードを組んだ。しかし、この設定が根本的におかしいことを否定することは出来ない。
##3.4.データシートをもう一度見る
PIC18F14K50のデータシートは以下。
日本語; http://ww1.microchip.com/downloads/jp/DeviceDoc/41350D_JP.pdf
英語; http://ww1.microchip.com/downloads/en/DeviceDoc/40001350F.pdf
PLLは今のところ調査しようがないので、Baudrateを疑う。
Baudrateに関しての基本的な情報は以下。
baudrate timerはフリーラン動作
SPBRGH:SPBRGレジスタペアで動作する
TXSTAレジスタのBRGHビットとBAUDCONレジスタのBRG16ビットによってbaudrate周期の乗数が決まる
BRGHビットは高baudレート、BRG16ビットは16bitBRGを意味する。それぞれHにすることで、利用が可能。どちらもbaudレートエラーの低減に効果があり、16bitBRGに関してはオシレータの周波数が高い場合でも低速のbaudレートを得ることが出来る。
SPBRGH:SPBRGレジスタペアに新しい値を書き込むとBRG timerはリセットされる
->BRG timerオーバフローを待たず、新しいbaudrateを出力できるようになる。
##3.5.今回利用する設定
今回利用する設定内容は、以下
BRG16=1,SYNC=0,BRGH=1
この、設定の場合Desired Baud RateとSPBRGH:SPBRGの関係式は以下
Desired Baud Rate = Fosc/64([SPBRGH:SPBRG]+1)
これをとりあえず解いていきましょう。目的はSPBRGH:SPBRGを導き出すことです。
SPBRGH:SPBRG = X, Desired Baud Rate = Yと置く。
Y = Fosc/4(X+1)
X+1 = Fosc/4Y
X = (Fosc/4Y)-1
今、Fosc=48MHz, Desired Baud Rate = 9600より
X = {(4810^6) / 49600} - 1
X = 1249
ここでXとはなんぞやというところが、ネックだったわけです。
これまでは、このSPBRGH:SPBRGは「比率」だと思っていたのですが、そうではなくて8bit:8bitの意味だったんです。
つまり
SPBRGH:SPBRG = 1249[D] = 0000010011100001[B]
SPBRGH = 00000100[B] = 4[D]
SPBRG = 11100001[B] = 225[D]
ということだったんですね。
つまり、さっきのコードのこの部分をきちんと変えてやれば動くはずです。
##3.6.やっと動いた…
今までの過程で、書いた以下のコードでやっと動きました…完成です。
/*
* File: main.c
* Author: kashiharaakira
*
* Created on April 16, 2016, 8:35 PM
*/
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <p18f14k50.h>
#define _XTAL_FREQ 48000000
#pragma config CPUDIV = NOCLKDIV
#pragma config USBDIV = OFF
#pragma config FOSC = HS
#pragma config PLLEN = ON
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config PWRTEN = OFF
#pragma config BOREN = OFF
#pragma config BORV = 30
#pragma config WDTEN = OFF
#pragma config WDTPS = 32768
#pragma config MCLRE = OFF
#pragma config HFOFST = OFF
#pragma config STVREN = ON
#pragma config LVP = OFF
#pragma config XINST = OFF
#pragma config BBSIZ = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF
void putch(unsigned char ch) {
while(!TXIF);
TXREG = ch;
}
void DelayMs(int dlyms){
while(dlyms--){
__delay_ms(1);
}
}
void DelayUs(int dlyum){
while(dlyum--){
__delay_us(1);
}
}
void main(void) {
OSCCON = 0b00000000;
TRISB = 0xF0;
RCSTA = 0b10010000;
TXSTA = 0b00100100;
BAUDCON = 0b00001000;
SPBRGH = 1249 / 256;
SPBRG = 1249 % 256;
__delay_ms(10);
while(1){
printf("Hello World!\n");
DelayMs(1000);
}
}
#4.参考にしたサイト
参考1; http://www.microchip.com/forums/m924490.aspx
参考2; http://www48.atpages.jp/cent22/Electronics/PIC/Usart_Calc_Baudrate/Usart_Calc_Baudrate.html
#5.謝辞
参考1のサイトで、本件について質問をさせていただきました。解決の糸口は、答えていただいたMicrochip Forumの利用者の方の助言があったからです。本、情報を書くにあたり御礼申し上げます。
#6.問い合わせ
なんか「ここおかしいんじゃないの?」とか「ここもっと聞きたい!」という所があれば、以下のメールアドレスもしくは本記事にコメントいただけると、嬉しいです。
akira.kashihara@hotmail.com