48時間以内の記事は
で表示されます

interrupt関数
みなさんこんばんは。
F1827で、以下のプログラム(もとは、猛牛ロックさん作成)ですが、大変重宝しています。
F1827でコンパイルするとエラーが出ます。
IDXは、interrupt関数に!が出ています。
原因を分かったらご教授願います。
/*間欠*/
// PIC16F1827 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = HI // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#define KanketuTime 2
#define DousaTime 1
#define On 0
#define Off 1
// __delay_ms(), __delay_us()関数が使用する
#define _XTAL_FREQ 4000000
int Start;
// プロトタイプ宣言
void InitInterTimer0 (void);
void WaitTime (int cnt);
// メイン関数
void main (void){
OSCCON = 0b01101010;//内蔵発振器 4MHz使用に設定
PORTA = 0x00;// 電圧レベルの初期設定
PORTB = 0xFF;
// 入出力設定
TRISA = 0b01010000;//RA5は入力専用・RA6は入力他は出力
TRISB = 0b00000000;//RB0は入力
//初期設定
RA2=Off;//MACH
RA6=Off;
Start=0;
// タイマ0割込み設定関数の呼び出し
InitInterTimer0();
// 割込み全体の許可
GIE = 1;
int Buzer=0;//
// 永久ループ
while(1){
if(Start==1){//スタートフラグが1なら動作開始
RA2=On;
Buzer=On;
Second(DousaTime);
//RA2=Off;
//Second(KanketuTime);
}
if(Start==0){
if(Buzer==On){
Second(KanketuTime);
RA2=Off;
Buzer=Off;
}
}
}
}
static void interrupt isr()
{//TMR0の割り込み処理(割り込みサブルーチン)
if(INTCONbits.TMR0IF==1){//割り込みがあったら
TMR0 =0 ; // タイマー0の初期化
if(RA6==0){//MACHからの信号をしらべて0(Low)だったらスタートのフラグを1にする
if(Start==0){
Start=1;
}
}
if(RA6==1){//MACHからの信号をしらべて1(High)だったらスタートのフラグを0にして
if(Start==1){
Start=0;
//RA2=Off;//機器の動作を強制的に止める。
}
}
INTCONbits.TMR0IF=0;
TMR0=0;
}
}
// タイマ0割込み設定関数
void InitInterTimer0 (void)
{//割り込み設定
OPTION_REG=0b000000000;//プリスケラ? 2 5ビット=0 WDTは使わない
TMR0=0; //TMR0カウンターを0に
INTCONbits.TMR0IF=0; //TMR0フラグクリアー
INTCONbits.TMR0IE=1;//TMR0割り込み許可
INTCONbits.GIE=1;//全体割り込み許可
}
// 待ち時間関数}
void Second(int sec){
while(sec >0){
sec--;
__delay_ms(10);
}
}
F1827で、以下のプログラム(もとは、猛牛ロックさん作成)ですが、大変重宝しています。
F1827でコンパイルするとエラーが出ます。
IDXは、interrupt関数に!が出ています。
原因を分かったらご教授願います。
/*間欠*/
// PIC16F1827 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = HI // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
#define KanketuTime 2
#define DousaTime 1
#define On 0
#define Off 1
// __delay_ms(), __delay_us()関数が使用する
#define _XTAL_FREQ 4000000
int Start;
// プロトタイプ宣言
void InitInterTimer0 (void);
void WaitTime (int cnt);
// メイン関数
void main (void){
OSCCON = 0b01101010;//内蔵発振器 4MHz使用に設定
PORTA = 0x00;// 電圧レベルの初期設定
PORTB = 0xFF;
// 入出力設定
TRISA = 0b01010000;//RA5は入力専用・RA6は入力他は出力
TRISB = 0b00000000;//RB0は入力
//初期設定
RA2=Off;//MACH
RA6=Off;
Start=0;
// タイマ0割込み設定関数の呼び出し
InitInterTimer0();
// 割込み全体の許可
GIE = 1;
int Buzer=0;//
// 永久ループ
while(1){
if(Start==1){//スタートフラグが1なら動作開始
RA2=On;
Buzer=On;
Second(DousaTime);
//RA2=Off;
//Second(KanketuTime);
}
if(Start==0){
if(Buzer==On){
Second(KanketuTime);
RA2=Off;
Buzer=Off;
}
}
}
}
static void interrupt isr()
{//TMR0の割り込み処理(割り込みサブルーチン)
if(INTCONbits.TMR0IF==1){//割り込みがあったら
TMR0 =0 ; // タイマー0の初期化
if(RA6==0){//MACHからの信号をしらべて0(Low)だったらスタートのフラグを1にする
if(Start==0){
Start=1;
}
}
if(RA6==1){//MACHからの信号をしらべて1(High)だったらスタートのフラグを0にして
if(Start==1){
Start=0;
//RA2=Off;//機器の動作を強制的に止める。
}
}
INTCONbits.TMR0IF=0;
TMR0=0;
}
}
// タイマ0割込み設定関数
void InitInterTimer0 (void)
{//割り込み設定
OPTION_REG=0b000000000;//プリスケラ? 2 5ビット=0 WDTは使わない
TMR0=0; //TMR0カウンターを0に
INTCONbits.TMR0IF=0; //TMR0フラグクリアー
INTCONbits.TMR0IE=1;//TMR0割り込み許可
INTCONbits.GIE=1;//全体割り込み許可
}
// 待ち時間関数}
void Second(int sec){
while(sec >0){
sec--;
__delay_ms(10);
}
}
Re: interrupt関数
> !が出ています。
元のプログラムと見比べてみてください。
何が変わっているのか。
場所は「!」が付いているので判っているんですよね?
更にコンパイルすればエラーメッセージが出ると思います。
元のプログラムと見比べてみてください。
何が変わっているのか。
場所は「!」が付いているので判っているんですよね?
更にコンパイルすればエラーメッセージが出ると思います。
Re: interrupt関数
猛牛ロックさんありがとうございます。
自分なりに、データシートを見たりして、プログラむ見直しをしたりして、
現在は以下の通りですが、
エラーの表示は、
interrupt関数に「!」の表示はそのままです。
でも何度見ても、エラーには思えないプログラム内容です。
エラー原因をわかりましたら、教えてください。
宜しくお願いします。
エラー表示も添えておきます。
なお、グーグル翻訳では、翻訳しきれませんでした。
エラー表示内容
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
make -f nbproject/Makefile-default.mk dist/default/production/LESSON_C.X.production.hex
make[2]: Entering directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
"C:\Program Files (x86)\Microchip\xc8\v2.00\bin\xc8-cc.exe" -mcpu=16F1827 -c -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -Wa,-a -DXPRJ_default=default -msummary=-psect,-class,+mem,-hex,-file -ginhx032 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -std=c99 -gdwarf-3 -mstack=compiled:auto:auto -o build/default/production/main.p1 main.c
main.c:71:6: error: variable has incomplete type 'void'
void interrupt isr(){//TMR0<82><U+030A><84><82><U+835E><82><U+074F><88><97><9D>
^
main.c:71:15: error: expected ';' after top level declarator
void interrupt isr(){//TMR0<82><U+030A><84><82><U+835E><82><U+074F><88><97><9D>
^
;
2 errors generated.
make[2]: *** [build/default/production/main.p1] Error 1
(908) exit status = 1
nbproject/Makefile-default.mk:106: recipe for target 'build/default/production/main.p1' failed
make[2]: Leaving directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
nbproject/Makefile-default.mk:90: recipe for target '.build-conf' failed
make[1]: Leaving directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2
BUILD FAILED (exit value 2, total time: 1s)
//*****************************
//* 間欠タイマー
//* 16F627A
//* XC8 V1.35
//* No481
//* RA6←MACHからの入力想定
//* RA2←制御機器へ
//*****************************
#include <xc.h>
#define _XTAL_FREQ 4000000 //delay用宣言
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#define KanketuTime 2
#define DousaTime 1
#define On 0
#define Off 1
// __delay_ms(), __delay_us()関数が使用する
#define _XTAL_FREQ 4000000
int Start;
//関数宣言
void Second(int sec);
// プロトタイプ宣言
void InitInterTimer0 (void);
// メイン関数
void main (void){
OSCCON = 0b01101010;//内蔵発振器 4MHz使用に設定
PORTA = 0x00;// 電圧レベルの初期設定
PORTB = 0xFF;
// 入出力設定
TRISA = 0b01010000;//RA5は入力専用・RA6は入力他は出力
TRISB = 0b00000000;//RB0は入力
//初期設定
RA2=Off;//MACH
RA6=Off;
Start=0;
int Buzer=0;//
void InitInterTimer0 (void);
GIE=1;//全体割り込み許可
// 永久ループ
while(1){
if(Start==1){//スタートフラグが1なら動作開始
RA2=On;
Buzer=On;
Second(DousaTime);
//RA2=Off;
//Second(KanketuTime);
}
if(Start==0){
if(Buzer==On){
Second(KanketuTime);
RA2=Off;
Buzer=Off;
}
}
TMR0=0;
TMR0IF=0;
}
}
void interrupt isr(){//TMR0の割り込み処理
if(TMR0IF==1){//割り込みがあったら
if(RA6==0){//MACHからの信号をしらべて0(Low)だったらスタートのフラグを1にする
if(Start==0){
Start=1;
}
}
if(RA6==1){//MACHからの信号をしらべて1(High)だったらスタートのフラグを0にして
if(Start==1){
Start=0;
//RA2=Off;//機器の動作を強制的に止める。
}
}
TMR0=0;
TMR0IF=0;
}
}
// タイマ0割込み設定関数
void InitInterTimer0 (void)
{
//割り込み設定
OPTION_REG=0b000000000;//プリスケラ? 2 5ビット=0 WDTは使わない
INTCONbits.TMR0IF=0; //TMR0フラグクリアー
INTCONbits.TMR0IE=1;//TMR0割り込み許可
INTCONbits.GIE=1;//全体割り込み許可
}
// 待ち時間関数}
void Second(int sec){
while(sec >0){
sec--;
__delay_ms(10);
}
}
自分なりに、データシートを見たりして、プログラむ見直しをしたりして、
現在は以下の通りですが、
エラーの表示は、
interrupt関数に「!」の表示はそのままです。
でも何度見ても、エラーには思えないプログラム内容です。
エラー原因をわかりましたら、教えてください。
宜しくお願いします。
エラー表示も添えておきます。
なお、グーグル翻訳では、翻訳しきれませんでした。
エラー表示内容
make -f nbproject/Makefile-default.mk SUBPROJECTS= .build-conf
make[1]: Entering directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
make -f nbproject/Makefile-default.mk dist/default/production/LESSON_C.X.production.hex
make[2]: Entering directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
"C:\Program Files (x86)\Microchip\xc8\v2.00\bin\xc8-cc.exe" -mcpu=16F1827 -c -fno-short-double -fno-short-float -O0 -fasmfile -maddrqual=ignore -xassembler-with-cpp -Wa,-a -DXPRJ_default=default -msummary=-psect,-class,+mem,-hex,-file -ginhx032 -Wl,--data-init -mno-keep-startup -mno-osccal -mno-resetbits -mno-save-resetbits -mno-download -mno-stackcall -std=c99 -gdwarf-3 -mstack=compiled:auto:auto -o build/default/production/main.p1 main.c
main.c:71:6: error: variable has incomplete type 'void'
void interrupt isr(){//TMR0<82><U+030A><84><82><U+835E><82><U+074F><88><97><9D>
^
main.c:71:15: error: expected ';' after top level declarator
void interrupt isr(){//TMR0<82><U+030A><84><82><U+835E><82><U+074F><88><97><9D>
^
;
2 errors generated.
make[2]: *** [build/default/production/main.p1] Error 1
(908) exit status = 1
nbproject/Makefile-default.mk:106: recipe for target 'build/default/production/main.p1' failed
make[2]: Leaving directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
nbproject/Makefile-default.mk:90: recipe for target '.build-conf' failed
make[1]: Leaving directory 'C:/Users/TOSHI/MPLABXProjects/LESSON_C.X'
nbproject/Makefile-impl.mk:39: recipe for target '.build-impl' failed
make[1]: *** [.build-conf] Error 2
make: *** [.build-impl] Error 2
BUILD FAILED (exit value 2, total time: 1s)
//*****************************
//* 間欠タイマー
//* 16F627A
//* XC8 V1.35
//* No481
//* RA6←MACHからの入力想定
//* RA2←制御機器へ
//*****************************
#include <xc.h>
#define _XTAL_FREQ 4000000 //delay用宣言
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
#define KanketuTime 2
#define DousaTime 1
#define On 0
#define Off 1
// __delay_ms(), __delay_us()関数が使用する
#define _XTAL_FREQ 4000000
int Start;
//関数宣言
void Second(int sec);
// プロトタイプ宣言
void InitInterTimer0 (void);
// メイン関数
void main (void){
OSCCON = 0b01101010;//内蔵発振器 4MHz使用に設定
PORTA = 0x00;// 電圧レベルの初期設定
PORTB = 0xFF;
// 入出力設定
TRISA = 0b01010000;//RA5は入力専用・RA6は入力他は出力
TRISB = 0b00000000;//RB0は入力
//初期設定
RA2=Off;//MACH
RA6=Off;
Start=0;
int Buzer=0;//
void InitInterTimer0 (void);
GIE=1;//全体割り込み許可
// 永久ループ
while(1){
if(Start==1){//スタートフラグが1なら動作開始
RA2=On;
Buzer=On;
Second(DousaTime);
//RA2=Off;
//Second(KanketuTime);
}
if(Start==0){
if(Buzer==On){
Second(KanketuTime);
RA2=Off;
Buzer=Off;
}
}
TMR0=0;
TMR0IF=0;
}
}
void interrupt isr(){//TMR0の割り込み処理
if(TMR0IF==1){//割り込みがあったら
if(RA6==0){//MACHからの信号をしらべて0(Low)だったらスタートのフラグを1にする
if(Start==0){
Start=1;
}
}
if(RA6==1){//MACHからの信号をしらべて1(High)だったらスタートのフラグを0にして
if(Start==1){
Start=0;
//RA2=Off;//機器の動作を強制的に止める。
}
}
TMR0=0;
TMR0IF=0;
}
}
// タイマ0割込み設定関数
void InitInterTimer0 (void)
{
//割り込み設定
OPTION_REG=0b000000000;//プリスケラ? 2 5ビット=0 WDTは使わない
INTCONbits.TMR0IF=0; //TMR0フラグクリアー
INTCONbits.TMR0IE=1;//TMR0割り込み許可
INTCONbits.GIE=1;//全体割り込み許可
}
// 待ち時間関数}
void Second(int sec){
while(sec >0){
sec--;
__delay_ms(10);
}
}
Re: interrupt関数
TRさん,猛牛ロックさん,こんばんは。
ちょっとご無沙汰してました。
TRさん,自分で試してないので,?ですが,
多分,プロトタイプの宣言ををしてないからかなと推測します。
static void interrupt isr()
の関数がプロトタイプの宣言をしてなくて,
main関数より後にでてくるからだと思います。
多分コンパイラーは,プログラムの先頭から解釈していきますが,
main関数のなかで,プロトタイプの宣言をしてない関数に出会う,
あるいは,main関数より前に書いてない関数に出会うと,
関数の参照ができなくて,エラーがでます。
プロトタイプの宣言,面倒なので,サブ関数をメインの前に,
記述するといいかもです。
確か,私の流儀では,そうしてました。
ちょっとご無沙汰してました。
TRさん,自分で試してないので,?ですが,
多分,プロトタイプの宣言ををしてないからかなと推測します。
static void interrupt isr()
の関数がプロトタイプの宣言をしてなくて,
main関数より後にでてくるからだと思います。
多分コンパイラーは,プログラムの先頭から解釈していきますが,
main関数のなかで,プロトタイプの宣言をしてない関数に出会う,
あるいは,main関数より前に書いてない関数に出会うと,
関数の参照ができなくて,エラーがでます。
プロトタイプの宣言,面倒なので,サブ関数をメインの前に,
記述するといいかもです。
確か,私の流儀では,そうしてました。
Re: interrupt関数
No.1249は私の環境ではその部分にはエラーは出ません。
No.1251はMABOさんの言う通り、関数宣言でしょう。
但し、チラッと見ただけですけど、1827用のプログラムになっていないようですし、
内容的にも劣化しているように思います。
No.1251はMABOさんの言う通り、関数宣言でしょう。
但し、チラッと見ただけですけど、1827用のプログラムになっていないようですし、
内容的にも劣化しているように思います。
Re: interrupt関数

maboさんへ
>プロトタイプの宣言,面倒なので,サブ関数をメインの前に,
記述するといいかもです。
やってみましたが、同じエラーが出ました。
猛牛ロックさんへ
>No.1249は私の環境ではその部分にはエラーは出ません。
そうでしたか〜。
と思って、
IDXにおいて、PICをF627Aに選択して、ビルトしましたが
同じエラーが出ました。
もう、自分レベルにはどうしようもありません。
AM11:51 追記
猛牛ロックさんの 環境 という話から、やむを得ず、
MPLAB のバージョンを変更したら、コンパイルできました。
IDXが3.36
コンパイラが1.42です。
自分には、こんな方法しか思いつきませんでした(泣く
でも、皆さんのおかげで、猛牛ロックさんが考えたプログラムを
自分風にアレンジして、しかも、1827、、違うか1823?の日本語バージョンで
書き込みに成功できて、うれしいです。
感謝申し上げます。
Config、AD、PWM、割り込み、タイマー0 そのほかにも
制御文、等々 学習を深められましたm(__)m
Re: interrupt関数
> IDXが
何度か書いていますけど、これってMPLAB X IDEですよね?せめてXIDEにして欲しいです。
> コンパイラが1.42です。
一度バージョンを2.?に上げましたよね?書式がちょっと変わったのかもしれません。
その辺りはコンパイラのマニュアルを見る必要があります。
基本的に、コンパイラの仕様に従うしかありません。
それは初心者でもベテランでも同様です。
で、学習ですけど、私の思うには人のコードを書き換えても上達しません。
まずはデジタル入出力を使った、簡単なものから始めた方が良いです。
※本文にあたる部分が10行以下のもの。
取り敢えず重要な事は
・C言語の書式を覚える。(宣言、初期化、関数、引数、戻り値、変数の型)
・GPIOを操作出来るようになる。(入出力を正しく操作)
・if文、while文(for文)を覚える。
・変数を使えるようになる(フラグ、カウント)
といった事です。
レジスタを詳しく覚える事は、時間の無駄が大きいです。
レジスタを操作すること自体は重要な事ですけど、逆に言えば、適切に設定するだけの事です。
最低限の事だけにしておいた方が良いです。
何度か書いていますけど、これってMPLAB X IDEですよね?せめてXIDEにして欲しいです。
> コンパイラが1.42です。
一度バージョンを2.?に上げましたよね?書式がちょっと変わったのかもしれません。
その辺りはコンパイラのマニュアルを見る必要があります。
基本的に、コンパイラの仕様に従うしかありません。
それは初心者でもベテランでも同様です。
で、学習ですけど、私の思うには人のコードを書き換えても上達しません。
まずはデジタル入出力を使った、簡単なものから始めた方が良いです。
※本文にあたる部分が10行以下のもの。
取り敢えず重要な事は
・C言語の書式を覚える。(宣言、初期化、関数、引数、戻り値、変数の型)
・GPIOを操作出来るようになる。(入出力を正しく操作)
・if文、while文(for文)を覚える。
・変数を使えるようになる(フラグ、カウント)
といった事です。
レジスタを詳しく覚える事は、時間の無駄が大きいです。
レジスタを操作すること自体は重要な事ですけど、逆に言えば、適切に設定するだけの事です。
最低限の事だけにしておいた方が良いです。
Re: interrupt関数
TRさん,猛牛ロックさん,こんばんは。
TRさん,私の環境でコンパイルしてみました。
X IDE 5.03→5.05の最新でした。
X C8 2.00
の最新の環境です。
TRさんのアップした最初のプログラムです。
若干変更しました。まず,プロトタイプの宣言で,
// プロトタイプ宣言
void InitInterTimer0 (void);
void WaitTime (int);
void Second(int);
のようにvoid Second(int);を付け加えました。
それから,ブログの方に掲載しますが,若干の変更をしたら,
コンパイル通りました。
新しい,コンパイラーは,文法にシビアなようです。
それと,xc.h の読み取りのバグがあるようです。
http://mabo52.sakura.ne.jp/files/newmain.c
TRさん,私の環境でコンパイルしてみました。
X IDE 5.03→5.05の最新でした。
X C8 2.00
の最新の環境です。
TRさんのアップした最初のプログラムです。
若干変更しました。まず,プロトタイプの宣言で,
// プロトタイプ宣言
void InitInterTimer0 (void);
void WaitTime (int);
void Second(int);
のようにvoid Second(int);を付け加えました。
それから,ブログの方に掲載しますが,若干の変更をしたら,
コンパイル通りました。
新しい,コンパイラーは,文法にシビアなようです。
それと,xc.h の読み取りのバグがあるようです。
http://mabo52.sakura.ne.jp/files/newmain.c
Re: interrupt関数
Re: interrupt関数
TRさん,猛牛ロックさん,こんにちは。
今回のプログラム16F1827だったんですね。
以前,猛牛ロックさんにご紹介いただいて,私も使いました。
ただ,クロックが早くなる分,いままで動いていたプログラムが動かなくなったり,
88〜1827への移植に苦労したりました。
LATレジスタ使ったり,間にNOPのアセンブラーいれたり,見よう見まねでやりました。
http://kuri6005.sakura.ne.jp/pic/index.php?%C8%AF%B8%F7%A5%C0%A5%A4%A5%AA%A1%BC%A5%C9%28LED%29%A4%CE%C5%C0%CC%C7%2816F1827%20XC8%29
http://mitt.la.coocan.jp/pic/pic1320_06.html
http://www.microtechnica.tv/faq/faq.cgi?kate=mikroC&faq=16
あたりにその記事があります。私のブログでも,
http://mabo52.sakura.ne.jp/index.php?e=970#cmt257
に関連の記事書いてます。
なんかもういろいろ忘れています。
今回のプログラム16F1827だったんですね。
以前,猛牛ロックさんにご紹介いただいて,私も使いました。
ただ,クロックが早くなる分,いままで動いていたプログラムが動かなくなったり,
88〜1827への移植に苦労したりました。
LATレジスタ使ったり,間にNOPのアセンブラーいれたり,見よう見まねでやりました。
http://kuri6005.sakura.ne.jp/pic/index.php?%C8%AF%B8%F7%A5%C0%A5%A4%A5%AA%A1%BC%A5%C9%28LED%29%A4%CE%C5%C0%CC%C7%2816F1827%20XC8%29
http://mitt.la.coocan.jp/pic/pic1320_06.html
http://www.microtechnica.tv/faq/faq.cgi?kate=mikroC&faq=16
あたりにその記事があります。私のブログでも,
http://mabo52.sakura.ne.jp/index.php?e=970#cmt257
に関連の記事書いてます。
なんかもういろいろ忘れています。
PIC3ボード製作

猛牛ロックさんもF1827を所有という事ですね!
お〜と言う感じ。
自分もすでにポチット行きました。
さてさて、
心機一転でPICを始めることにしました。
最初に、PIC3が安定して書き込みできる環境づくりということで、
外部給電によるPIC3用ボードを作ることにしました。
先ず悩んでいる点があります。
それは、PIC端子の処置です。
プルダウンかプルアップかです。
自分は、プルダウンで行こうかとおもいます。
そこでなんですが、将来、出力端子にした時の事を想定して、写真の様に抵抗2本と
LEDを繋いでみました。
実験では動作しましたが、写真の様な繋ぎ方は、どうなんでしょうか?
アブノーマルでしょうか?
Re: PIC3ボード製作
学習ボートは使わないのですか?
あれには外部給電端子もついているので作る必要は無いと思います。
> 自分は、プルダウンで行こうかとおもいます。
それは自由ですけど、参考とするプログラムにはプルアップが多いと思いますよ。
で、大抵は内部プルアップを使います。
※今のマイコンは個別に内部プルアップ出来ます。なので、あまり外には配線しません。
回路図は意図が解りません。
あれには外部給電端子もついているので作る必要は無いと思います。
> 自分は、プルダウンで行こうかとおもいます。
それは自由ですけど、参考とするプログラムにはプルアップが多いと思いますよ。
で、大抵は内部プルアップを使います。
※今のマイコンは個別に内部プルアップ出来ます。なので、あまり外には配線しません。
回路図は意図が解りません。
Re: PIC3ボード製作

>学習ボートは使わないのですか?
あれには外部給電端子もついているので作る必要は無いと思います。
キットで遊ぼうのぼーどですか、
あのボードは、書き込みが不安定で使い物になりません。
購入当初も、電圧を4.75Vにしたりして、なんで5Vじゃダメなのと思ったりしていました。
そんな中、最近(MPLABのバージョンを最新にした。)は、書き込みが出来なくなり、壊れたと思い買い換えました。
それで、試しにと思って、書き込み専用ボードで書き込んでみると
安定して書きこめるようになりました。
PIC3の電流をLEDの方へ回すには限りがあるようです。
なので、書き込みとLEDなどへ回す電気とを分離して、書き込み専用のボードを作ることにしました。
プログラムをやっていて思っていたのですが、
例えば、
入力のSWをONで、出力LEDをHiとしますよね。
この場合、プルアップとプルダウンでの、
端子をHiにしたりLoにしたりの設定が分かりずらいと思っていました。
統一したらいいのにと思っていました。
PICの事が少しは分かってきたので、新たな視線で考えることにしました。←大袈裟(笑
写真は、今のプランです。LEDは、将来の事です。今は作りませんので、除いて見て下さい。
MCLAの端子は入力専用であり、且つ書き込み時には、PIC3に繋がっていなければならない。
書き込みが出来て、且つMCLAの機能を端子を持たせなければなりません。
プルアップにしました。
これなら、両方の機能を持たせられます。
そうなると、統一した方が分かり易いので、RA側は、全てプルアップに統一となりました。
プルアップの場合、SWがONで、端子は、Loとなる。
出力側は、プルダウン。
入力側が、SwがONの時、出力側端子はLEDがOn。この方がイメージが合う。
この時の端子の電位は
入力側はLoで出力側もLo(電流は、PICに流れる。)
イメージが合うんですよね。
追記
PICの電源にコンデンサを忘れました。
Re: PIC3ボード製作
確かに、プルダウン入力とアクティブハイでの出力は、感覚と一緒になります。
けれども、実際の素子や回路の状況でそれが変わるのは仕方がないことです。
それを避けるためには
#define Led_On 0
#define Led_Off 1
#define Sw_On 1
#define Sw_Off 0
等のように間違わないように、また書き換えも楽になるように工夫します。
> あのボードは、書き込みが不安定で使い物になりません。
それはボードのせいでは無いと思います。
それなりの数の購入者が居て、利用されているボードですから、
「書き込みが不安定」という反響がなければ、特にボードには問題は無いと推測します。
勿論、故障と言う事はあるでしょうけど。
「安定した書き込み」を第一に考えるなら、https://www.aliexpress.com/item//32949836067.html
のようなセッティングの楽な物を使う方が良いような気がします。
※これは配線だけを合わせるものです。それだけで多くの人が書き込んでいる実績があります。
第三者の視点で見ると、TRさんが作ろうとしているボードよりも、上記の2つのボードの方が信頼性が高いと言わざるを得ません。
けれども、実際の素子や回路の状況でそれが変わるのは仕方がないことです。
それを避けるためには
#define Led_On 0
#define Led_Off 1
#define Sw_On 1
#define Sw_Off 0
等のように間違わないように、また書き換えも楽になるように工夫します。
> あのボードは、書き込みが不安定で使い物になりません。
それはボードのせいでは無いと思います。
それなりの数の購入者が居て、利用されているボードですから、
「書き込みが不安定」という反響がなければ、特にボードには問題は無いと推測します。
勿論、故障と言う事はあるでしょうけど。
「安定した書き込み」を第一に考えるなら、https://www.aliexpress.com/item//32949836067.html
のようなセッティングの楽な物を使う方が良いような気がします。
※これは配線だけを合わせるものです。それだけで多くの人が書き込んでいる実績があります。
第三者の視点で見ると、TRさんが作ろうとしているボードよりも、上記の2つのボードの方が信頼性が高いと言わざるを得ません。
Re: PIC3ボード製作
キットで遊ぼうのボードで書き込みをしましたが、
やはり駄目でした。
自分のボードは故障していると思われます。
PIC3が故障しているようではないです。
今のところ、自作暫定ボードで書き損じはないので、
自作をやってみます。
やはり駄目でした。
自分のボードは故障していると思われます。
PIC3が故障しているようではないです。
今のところ、自作暫定ボードで書き損じはないので、
自作をやってみます。
Re: PIC3ボード製作

お陰様で、書き込み専用ボード完成しました。
あたりまえでしょうけど、一発で書き込みが出来ました。
でも、キットで遊ぼうのボードでは、その当たり前が出来ませんでした
写真は、自作ボードで書き込んだ後の、書きこめたかどうかのテストです。
壊れていたキットで遊ぼうのボードは、最後の御奉公です。
11/26追記
No1246について
ボードが出来たので、内容を拝見しました。
間欠のプログラムを思い出しました ↓
#define On 1
#define Off 0
#define 略
#define 略
#define vacuum RA3 //掃除機出力
略
// 待ち時間関数
略
}
// メイン関数
void main (void){
略
// 永久ループ
while(1){
略
if(MODE==0||(MODE==3&&MACH_sig==0)){//OFF
略
vacuum=Off;
関連付けさせるには、#defaineを2つ必要なんですね。
プログラムにたけている人は違いますね。
No1238から

猛牛ロックさん、こんばんは。
>速度の考え方は、PCと同じです。
通常、4MHz駆動なら1MHz駆動の4倍速く動いていますから、1MHz→4MHzとすると
動作時間が1/4になります。
やはりそうでしたか。
>(PICクロックの周期)と(Tosc)は同一です。
Fosc「周波数」=1/Tosc です。
で、
TADを「1.6us〜6.4usの間になるように」倍率を選択する、と言う事です。
1MHz駆動ならToscは1usです。そして設定できる倍率は、1,2,4,8、…なので、
2か4を選択する事になります。
なるほど!
有難うございました。
16F1827ですが、
16F1823のデータシートと似ている感じです。
>数値は見つけられなかったですけど、例えば、0.8us〜6.4usのように速い速度のほうが広がったのだと思います。
日本語版をみると、網掛を除いた部分が、推奨値で、1.0〜4.0μsecond となっていますかね?
ただ、FRCって何だろう?
RCはおそらく、抵抗とコンデンサを使ってクロック信号を外部から受ける事だと思っていたのですが。
Re: No1238から
16F1823と16F1827は同じシリーズ(ファミリ)です。なので、作りとしては殆ど共通しています。
なので、かなりの部分が同じ説明だと思います。
でも、データシートをまとめられなかったという事は、それなりの違いも存在するということでしょう。
> 網掛を除いた部分が、推奨値で、1.0〜4.0μsecond となっていますかね?
そうです。そこから推測しました。16F88で言えば2〜4usが推奨となります。
でも、設定できない、とかでは無く、測定値は保証しないよ、と言う事なので、
駆動クロックを変更したときには、エラーも無く、範囲外に出てしまう可能性があります。
FRCは単純に、内部のRC構成の発信回路を使った時のFREQUENCYという意味でしょうね。
なので、かなりの部分が同じ説明だと思います。
でも、データシートをまとめられなかったという事は、それなりの違いも存在するということでしょう。
> 網掛を除いた部分が、推奨値で、1.0〜4.0μsecond となっていますかね?
そうです。そこから推測しました。16F88で言えば2〜4usが推奨となります。
でも、設定できない、とかでは無く、測定値は保証しないよ、と言う事なので、
駆動クロックを変更したときには、エラーも無く、範囲外に出てしまう可能性があります。
FRCは単純に、内部のRC構成の発信回路を使った時のFREQUENCYという意味でしょうね。
Re: No1238から

PICは初心者にとって扱いずらいと言っていましたが、
PICのデータシートが分からないと本当に難しいと思います。
そこで、F1827を買うことにしました。F1823はデータシートの日本語版があるので
本来は、F1823を買いなのかもしれませんが、PIN数が少ないんですよね。
試しにF1827を買ってみて、今まで教わった事をデータシートを見ながら、
もう一度、やってみます。
PICにはTimerが0,1,2の3個あると、教科書に有ります。
でも、PWMモジュールを使う場合、何故、Timer2を使うのか知りませんでしたが、
データシートにありました。
やはり、日本語のデータシートを手にしながら、教えて頂いたプログラムをもう一度、やってみるのも良いと思った次第です。
Re: No1238から
PICなら、そのシリーズを選択するのが一番いいような気がします。
というのは、有名であっても、あまりに古い石だと、
・コンパイラが異なる
・古い情報となるので削除される
という流れになって、XCの例を見つけるのが困難になります。
また、最新(と言っても、秋月で発売されてから2,3年以内)の石だと情報自体が少なかったり、
また5桁のものや、アナログ機能が豊富なものはもっと複雑になっているので、取りあえず止めた方が良いです。
※マイコンとして薦めないのではなく、そういった機能を使いたい時に使い始めれば良い、という事です。
で、このシリーズ(ファミリ)は、ベーシックなラインにあるマイコンで、特別な物が無いかわりに
現在の標準的な機能はちゃんと揃っています。
まぁ、F88もF627Aもベーシックなラインですけどなのですけど、実質的には引退を控えているマイコン、というマイコンです。
F1827なら、開発の候補に挙がる現役のマイコンでしょう。価格も現役価格です。
※F1827なら所有しています。F88やF627Aはちょっと買う気にはなりませんでした。
ただ、日本語データシートがあるとはいえ、F627AやF88と比べると、マイコン(レジスタ)としては多少複雑になっています。
というのは、有名であっても、あまりに古い石だと、
・コンパイラが異なる
・古い情報となるので削除される
という流れになって、XCの例を見つけるのが困難になります。
また、最新(と言っても、秋月で発売されてから2,3年以内)の石だと情報自体が少なかったり、
また5桁のものや、アナログ機能が豊富なものはもっと複雑になっているので、取りあえず止めた方が良いです。
※マイコンとして薦めないのではなく、そういった機能を使いたい時に使い始めれば良い、という事です。
で、このシリーズ(ファミリ)は、ベーシックなラインにあるマイコンで、特別な物が無いかわりに
現在の標準的な機能はちゃんと揃っています。
まぁ、F88もF627Aもベーシックなラインですけどなのですけど、実質的には引退を控えているマイコン、というマイコンです。
F1827なら、開発の候補に挙がる現役のマイコンでしょう。価格も現役価格です。
※F1827なら所有しています。F88やF627Aはちょっと買う気にはなりませんでした。
ただ、日本語データシートがあるとはいえ、F627AやF88と比べると、マイコン(レジスタ)としては多少複雑になっています。
ADRES
上限を超えたので新しく投稿しました。
>RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
済みません。
もう少し分かり易く教えて下さい。お願いします。
return文 有難うございます。
ADRESは、変数宣言で対処しましいた。
//変数宣言
unsigned int Value;
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
>RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
済みません。
もう少し分かり易く教えて下さい。お願いします。
return文 有難うございます。
ADRESは、変数宣言で対処しましいた。
//変数宣言
unsigned int Value;
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
Re: ADRES
「行おうとしている事はボリュームに対応した点滅」なのですから、通常は
ボリューム(ADC)値×補正倍率を点灯/消灯時間、にしようとしますよね?
ボリューム値が1000なら2秒、100なら0.2秒、となるようにするなら補正倍率を2にします。
なので、通常は2msのディレイを1000回、或いは100回行って時間を合わせます。
RB3=1;
for(i=0;i<tmp;i++)__delay_ms(2);
だと、__delay_ms(2);がtmp回行われます。
> for(i = 0; i<tmp; i++){RB3=1;}
だと、tmp回、RB3=1;が行われます。(この処理時間をTとします。)
そして次行の
__delay_ms(2);
です。
つまり、RB3が1の時間は「T+2ms」ということになります。
Tはボリューム値に比例しますけど、意味のない2msが付きます。
for(i = 0; i<tmp*50; i++)RB3=1;
for(i = 0; i<tmp*50; i++)RB3=0;
のように、適当な倍率回数行う方がボリュームの設定値と点滅間隔の動きが一致します。
けれども、1ループで何クロックかかるかは、初心者には判断付きにくいと思います。
※実際、元のプログラムでは点滅時間が早すぎませんでしたか?
> Value=(ADRESH<<8)+ ADRESL;
> __delay_us(20); //アクィジョン時間 20us
> ADCON0bits.GO_DONE= 1; //AD変換開始
> while(ADCON0bits.GO_DONE); //変換完了待ち
> return (Value);
これは害が無いかもしれませんけど悪いプログラムです。
先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
とADC結果レジスタの値を取得してからADC変換を開始しています。
実際の所、これは前回行ったADCの値がレジスタに残っているでしょうからそれが出力されます。
ちゃんと今回の値を返したいなら変換完了→値を取得という順板を守ってください。
今回の場合は気付かない程度に遅れるだけですけど、これが計算なら、計算をせずに結果を返すような事です。
ボリューム(ADC)値×補正倍率を点灯/消灯時間、にしようとしますよね?
ボリューム値が1000なら2秒、100なら0.2秒、となるようにするなら補正倍率を2にします。
なので、通常は2msのディレイを1000回、或いは100回行って時間を合わせます。
RB3=1;
for(i=0;i<tmp;i++)__delay_ms(2);
だと、__delay_ms(2);がtmp回行われます。
> for(i = 0; i<tmp; i++){RB3=1;}
だと、tmp回、RB3=1;が行われます。(この処理時間をTとします。)
そして次行の
__delay_ms(2);
です。
つまり、RB3が1の時間は「T+2ms」ということになります。
Tはボリューム値に比例しますけど、意味のない2msが付きます。
for(i = 0; i<tmp*50; i++)RB3=1;
for(i = 0; i<tmp*50; i++)RB3=0;
のように、適当な倍率回数行う方がボリュームの設定値と点滅間隔の動きが一致します。
けれども、1ループで何クロックかかるかは、初心者には判断付きにくいと思います。
※実際、元のプログラムでは点滅時間が早すぎませんでしたか?
> Value=(ADRESH<<8)+ ADRESL;
> __delay_us(20); //アクィジョン時間 20us
> ADCON0bits.GO_DONE= 1; //AD変換開始
> while(ADCON0bits.GO_DONE); //変換完了待ち
> return (Value);
これは害が無いかもしれませんけど悪いプログラムです。
先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
とADC結果レジスタの値を取得してからADC変換を開始しています。
実際の所、これは前回行ったADCの値がレジスタに残っているでしょうからそれが出力されます。
ちゃんと今回の値を返したいなら変換完了→値を取得という順板を守ってください。
今回の場合は気付かない程度に遅れるだけですけど、これが計算なら、計算をせずに結果を返すような事です。
Re: ADRES
> MABOさんへ
> PICに比べると親切過ぎる感じですね。
そうですね。
基本的な思想としては、「なるべく低級な作業(レジスタの直接操作)はしない」というのがあるのだと思います。
analogReadやanalogWriteも、使い勝手のいいモデル(分周や周期)が採用されています。
で、そのモデルを外した使い方をするときは、本来は、レジスタを直接操作する方法をしなければならないわけなのですが、
私が見た処、そういった使い方は稀で、大抵は、外部のライブラリを使う、という方が多いです。
つまり、他人が作った、操作し易くなっているもの=ライブラリを多くの人が使っています。
それが見つからなくて最後の手段として、データシートをみて、レジスタを弄る、という感じでしょうか。
勿論、Arduino使いも色々な人がいるでしょうけど。
※低レベルで、きっちりと積み上げていけば、そのデバイスの能力を最大限に活かした、より良いものが出来るのは間違いありません。
けれども、低級な作業を学んでもデバイスを変えたら役に立ちません。
逆に言えば、中級でいろんなデバイスで共通の仕様になっているものを使った方が
覚えるのも優しいし、色んなデバイスでも使え、また、間違いも少なくなる、とも言えます。
そのデバイスを熟知している人でも、やはり多くの行を書いていけば間違いも起こります。
そういった人でさえ、ADC変換でレジスタ操作をするなら、データシートで確認する事は必須でしょう。
でも、analogRead(pin);を使えば5秒で安心して使うことが出来ます。
逆に言えば、仕事で「高度な事」をする人の方がデータシートを参照しなければならないレジスタ操作からは離れて
短時間で安全に操作できる方を優先させます。
実際の所、私も最初は、データシートを見て自分自身で書いていました。
でも、何度か同じような事をすると、前回の物を使いまわすようになります。
そうなってくると、レジスタの内容はちょっと忘れても、その作った関数だけが重要になります。
何度か使っているので、信頼性は多少高くなった、自分の関数です。
でも、Arduinoなら、ほとんど同じものが最初から提供されています。
そちらの方が、信頼性が高く、他の人にとっても判りやすい、と言う事は明白です。
プログラムの開発会社が、そのような中間的なものを使うのは必然だと思います。
(逆に、レジスタ操作なんて(極力)するな、というのが主流でしょう)
で、私としては、レジスタ操作(データシート)と悪戦苦闘して、無駄な時間を使ってしまうPICからは離れた方が良いと
思う次第です。(勿論、ベーシックな部分をおさえるのは重要です。)
ただ、そのデバイスの専門家になる目的ではないので、そのレジスタを覚えることは次の段階では役に立たなくなります。
なので、そのデバイス固有の事は、他の参考サイトからコピーすれば良いと思っています。
ただし、それ以上に安全で、信頼性の高いのが、中間的な処理をしてあるライブラリを使う事です。
※個人サイトは、間違いだらけです。「製作」サイトは仕方がないと思うのですけど、「説明」サイトならもう少し
ちゃんと(過去に書いたプログラムも)管理してもらいたいと思っています。
まぁ、結局の所、それだけマイコンプログラムや電子回路は「間違いが起こりやすい=見つけ辛い」ということなのでしょうけど。
> PICに比べると親切過ぎる感じですね。
そうですね。
基本的な思想としては、「なるべく低級な作業(レジスタの直接操作)はしない」というのがあるのだと思います。
analogReadやanalogWriteも、使い勝手のいいモデル(分周や周期)が採用されています。
で、そのモデルを外した使い方をするときは、本来は、レジスタを直接操作する方法をしなければならないわけなのですが、
私が見た処、そういった使い方は稀で、大抵は、外部のライブラリを使う、という方が多いです。
つまり、他人が作った、操作し易くなっているもの=ライブラリを多くの人が使っています。
それが見つからなくて最後の手段として、データシートをみて、レジスタを弄る、という感じでしょうか。
勿論、Arduino使いも色々な人がいるでしょうけど。
※低レベルで、きっちりと積み上げていけば、そのデバイスの能力を最大限に活かした、より良いものが出来るのは間違いありません。
けれども、低級な作業を学んでもデバイスを変えたら役に立ちません。
逆に言えば、中級でいろんなデバイスで共通の仕様になっているものを使った方が
覚えるのも優しいし、色んなデバイスでも使え、また、間違いも少なくなる、とも言えます。
そのデバイスを熟知している人でも、やはり多くの行を書いていけば間違いも起こります。
そういった人でさえ、ADC変換でレジスタ操作をするなら、データシートで確認する事は必須でしょう。
でも、analogRead(pin);を使えば5秒で安心して使うことが出来ます。
逆に言えば、仕事で「高度な事」をする人の方がデータシートを参照しなければならないレジスタ操作からは離れて
短時間で安全に操作できる方を優先させます。
実際の所、私も最初は、データシートを見て自分自身で書いていました。
でも、何度か同じような事をすると、前回の物を使いまわすようになります。
そうなってくると、レジスタの内容はちょっと忘れても、その作った関数だけが重要になります。
何度か使っているので、信頼性は多少高くなった、自分の関数です。
でも、Arduinoなら、ほとんど同じものが最初から提供されています。
そちらの方が、信頼性が高く、他の人にとっても判りやすい、と言う事は明白です。
プログラムの開発会社が、そのような中間的なものを使うのは必然だと思います。
(逆に、レジスタ操作なんて(極力)するな、というのが主流でしょう)
で、私としては、レジスタ操作(データシート)と悪戦苦闘して、無駄な時間を使ってしまうPICからは離れた方が良いと
思う次第です。(勿論、ベーシックな部分をおさえるのは重要です。)
ただ、そのデバイスの専門家になる目的ではないので、そのレジスタを覚えることは次の段階では役に立たなくなります。
なので、そのデバイス固有の事は、他の参考サイトからコピーすれば良いと思っています。
ただし、それ以上に安全で、信頼性の高いのが、中間的な処理をしてあるライブラリを使う事です。
※個人サイトは、間違いだらけです。「製作」サイトは仕方がないと思うのですけど、「説明」サイトならもう少し
ちゃんと(過去に書いたプログラムも)管理してもらいたいと思っています。
まぁ、結局の所、それだけマイコンプログラムや電子回路は「間違いが起こりやすい=見つけ辛い」ということなのでしょうけど。
Re: ADRES
1案
while (1)
{
tmp = adconv();
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
}
案1は、遅すぎです。
maboさんがご紹介下さったURLの動画を見て下さい。
>先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
No1216でプロトタイプ宣言をしたのに、関数を入れ忘れていました。
今のところ、下記のとおりです。
記
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
unsigned int Value;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
unsigned int adconv();
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;}
__delay_ms(2);
}
}
unsigned int adconv()
{
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
}
while (1)
{
tmp = adconv();
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
}
案1は、遅すぎです。
maboさんがご紹介下さったURLの動画を見て下さい。
>先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
No1216でプロトタイプ宣言をしたのに、関数を入れ忘れていました。
今のところ、下記のとおりです。
記
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
unsigned int Value;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
unsigned int adconv();
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;}
__delay_ms(2);
}
}
unsigned int adconv()
{
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
}
Re: ADRES
まず、行っている事の目的があります。
最初にも言ったように、プログラム的には「間違い」ではありません。
参考サイトは簡単に言えば、「A/D変換の結果を目視で確認する」というものです。
それなのに、プログラムが悪かったので、その動画のように訳の分からないものになってしまった、
と言う事です。つまり、プログラムも動画も目的を果たしていない例です。
TRさんは最初から勘違いしているようですが、ADRESはPWMとは関係ありません。
ADC変換の結果を格納するレジスタです。
PWMと連動させるなら全く別のプログラムになります。つまり点滅の速さではなく、
明るさを変えるプログラムです。
そして、内容をもっと考えてください。
点滅のスピードは単にADC値を何倍(x○秒)を変えるだけです。
意味が解っていれば
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
も
RB3=1;
for(i = 0; i<tmp; i++){__delay_us(20);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_us(20);}
も同じ(意味)のスケッチだと判る筈です。
また、
>先頭で、まだADCを始めてさえいないのに
の部分は
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
の間で値を取得しないと意味がない、と言う事です。
追記しておきます。
ADCの結果を確認するプログラムなら、ます、ちゃんとその結果が大まかにでも確認できる、
というのが重要です。
つまり、ADCは10ビット、つまり、0〜1023までの結果になります。
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
と書いているのは最大値を取得した時に約2秒程度(点滅周期では4秒)を想定しています。
で、ボリュームを半分にした時が1秒、つまり、その程度なら時計などで、判断付きやすい、かつ、それほど時間がかからない、
というのを想定したものです。更にはマイコンが想定しているクロックで動作しているか確認できます。
それとも
> 案1は、遅すぎです。
は点滅間隔が20秒や30秒かかったということでしょうか?
もしそうなら他に原因があるのかも知れません。動作クロックが違ったとか。
動画をみてどの位の速さ、つまりADCの値が幾つ位で、ボリューム位置がどの位置にあるのか、点滅を見て判断付きますか?
この位置なら、この位、というものを実証している動画には見えません。
最初にも言ったように、プログラム的には「間違い」ではありません。
参考サイトは簡単に言えば、「A/D変換の結果を目視で確認する」というものです。
それなのに、プログラムが悪かったので、その動画のように訳の分からないものになってしまった、
と言う事です。つまり、プログラムも動画も目的を果たしていない例です。
TRさんは最初から勘違いしているようですが、ADRESはPWMとは関係ありません。
ADC変換の結果を格納するレジスタです。
PWMと連動させるなら全く別のプログラムになります。つまり点滅の速さではなく、
明るさを変えるプログラムです。
そして、内容をもっと考えてください。
点滅のスピードは単にADC値を何倍(x○秒)を変えるだけです。
意味が解っていれば
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
も
RB3=1;
for(i = 0; i<tmp; i++){__delay_us(20);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_us(20);}
も同じ(意味)のスケッチだと判る筈です。
また、
>先頭で、まだADCを始めてさえいないのに
の部分は
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
の間で値を取得しないと意味がない、と言う事です。
追記しておきます。
ADCの結果を確認するプログラムなら、ます、ちゃんとその結果が大まかにでも確認できる、
というのが重要です。
つまり、ADCは10ビット、つまり、0〜1023までの結果になります。
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
と書いているのは最大値を取得した時に約2秒程度(点滅周期では4秒)を想定しています。
で、ボリュームを半分にした時が1秒、つまり、その程度なら時計などで、判断付きやすい、かつ、それほど時間がかからない、
というのを想定したものです。更にはマイコンが想定しているクロックで動作しているか確認できます。
それとも
> 案1は、遅すぎです。
は点滅間隔が20秒や30秒かかったということでしょうか?
もしそうなら他に原因があるのかも知れません。動作クロックが違ったとか。
動画をみてどの位の速さ、つまりADCの値が幾つ位で、ボリューム位置がどの位置にあるのか、点滅を見て判断付きますか?
この位置なら、この位、というものを実証している動画には見えません。
Re: ADRES

それと、読み取り値の変数宣言をValueからADRESにしました。
簡単にしました。
本当は、ADRESが変わったらといったif文も付けるのでしょうけど。
案1
for(i = 0; i<ADRES; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<ADRES; i++){RB3=0;}
__delay_ms(2);
案2
for(i = 0; i<ADRES; i++){__delay_ms(2);}
RB3=1;
for(i = 0; i<ADRES; i++){__delay_ms(2);}
RB3=0;
案1と案2の違い
案1は、iを0からADRESの値になったら、RBを点灯し、2msおいて、またiが0からADRESまでRBが点灯
案2は、iを0からADRESの値になったら、2msおいて、RBを点灯(またiが0からADRESになり、2ms経過するまでの間)し、次に、RBが消灯
どちらも同じに思えるが、周期は案1の方が早い。
結局、何故、案1が早くなるのか分かりませんでした。
while (1)
{
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
ADRES=(ADRESH<<8)+ ADRESL;
//※ここから案1と案2を入れ替えます。
for(i = 0; i<ADRES; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<ADRES; i++){RB3=0;}
__delay_ms(2);
}
追記
PWM機能とADC機能 よく理解せずに、今回、続けて読んできてしまった。
どちらも10ビット操作という点で似ていた。
これが、、、、間違えてしまった原因。
どちらも、Lチカなので、より一層困惑してしまった。
PWMは、CCPR1の上位8ビットとCCP1CONのDC1Bの2ビット
ADCは、上位8バイトがADRESH (ADRES High byte)、下位2バイトがADRESL
Lチカしたい場合、ADC変換時間を利用するのではなく、PWM機能でする。
while(ADCON0bits.GO_DONE); //ADCの変換完了待ち 、これを使うのではなく、
ADRESH + ADRESLのビットの値 =CCP1LとCCP1CONのDC1B
にすれば、OKと思われる。
どうでしょうか??
Re: ADRES
まず、聞きたいのはTRさんは、いったい何をしようとしているのでしょうか?
案1も案2もプログラム的には間違いではありません。
なので、両方ともにかかれたように動きます。
重要な事は、目的と合致しているかどうかです。
> 読み取り値の変数宣言をValueからADRESにしました。
それはあまりいい変更ではありません。
というのはレジスタ名とダブル可能性があるからです。
(つまり、自分で作る変数に今回無いレジスタでもPORTDとかを付けるのはどうでしょうか?)
小文字にしておくとか区別した方が良いです。
> 何故、案1が早くなるのか分かりませんでした
orz 時間の計測の仕方をちゃんと覚えた方が良いです。
重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
Arduinoなら、時間をそのままPCに送って表示出来ますけど、
PIC使いならLEDの点滅の速さで識別するような使い方も覚える必要があります。
※でなければ、LCDや7セグを繋いで数値を表示させます。
ADCが5V、つまり、1023の時の時間を付けておきます。
なぜ、その時間になるのかを考えてください。
(説明は複数回しています)
for(i = 0; i<ADRES; i++){RB3=1;}//18.7ms
__delay_ms(2);//2ms
for(i = 0; i<ADRES; i++){RB3=0;}//18.7ms
__delay_ms(2);//2ms
これだと、約21msの点灯/消灯です。
実際の所、__delay_ms(2);は付けないで、ADRES*30とかの方が、まだ、比例関係になります。
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 1;//3us
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 0;//3us
こっちだと約2.06sの点灯/消灯です。
案1も案2もプログラム的には間違いではありません。
なので、両方ともにかかれたように動きます。
重要な事は、目的と合致しているかどうかです。
> 読み取り値の変数宣言をValueからADRESにしました。
それはあまりいい変更ではありません。
というのはレジスタ名とダブル可能性があるからです。
(つまり、自分で作る変数に今回無いレジスタでもPORTDとかを付けるのはどうでしょうか?)
小文字にしておくとか区別した方が良いです。
> 何故、案1が早くなるのか分かりませんでした
orz 時間の計測の仕方をちゃんと覚えた方が良いです。
重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
Arduinoなら、時間をそのままPCに送って表示出来ますけど、
PIC使いならLEDの点滅の速さで識別するような使い方も覚える必要があります。
※でなければ、LCDや7セグを繋いで数値を表示させます。
ADCが5V、つまり、1023の時の時間を付けておきます。
なぜ、その時間になるのかを考えてください。
(説明は複数回しています)
for(i = 0; i<ADRES; i++){RB3=1;}//18.7ms
__delay_ms(2);//2ms
for(i = 0; i<ADRES; i++){RB3=0;}//18.7ms
__delay_ms(2);//2ms
これだと、約21msの点灯/消灯です。
実際の所、__delay_ms(2);は付けないで、ADRES*30とかの方が、まだ、比例関係になります。
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 1;//3us
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 0;//3us
こっちだと約2.06sの点灯/消灯です。
Re: ADRES
レジスタはともかく、PWMは出力です。目で見える状態としては点滅では無く点灯です。
LED照明などでの明るさ調整でも使われている手法です。
また、モータの速度調整にもよく使われています。
一方、ADC=アナログ/デジタルコンバータは入力したアナログ電圧を数値化するものです。
Lチカとは直接は関係ありません。
今回のように、ボリュームを付けた装置だと、ボリュームの位置が何処にあるのかを調べるために使います。
そして、そのボリューム位置が意味する出力?にします。
LED照明などでの明るさ調整でも使われている手法です。
また、モータの速度調整にもよく使われています。
一方、ADC=アナログ/デジタルコンバータは入力したアナログ電圧を数値化するものです。
Lチカとは直接は関係ありません。
今回のように、ボリュームを付けた装置だと、ボリュームの位置が何処にあるのかを調べるために使います。
そして、そのボリューム位置が意味する出力?にします。
Re: ADRES
>まず、聞きたいのはTRさんは、いったい何をしようとしているのでしょうか?
有難うございます。
教科書でのPWMについては、半固定抵抗器を使っての明るさ調整がありませんので、
学習したいと思っています。
>というのはレジスタ名とダブル可能性があるからです。
止めます。
>重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
これからの、学習テーマの1つにしたいと思います。
自分で、PWM機能により可変抵抗器を使いLEDを滑らかに明るくしたり暗くしたりを考えましたが、
コンパイル不可
出来ましたら、ご指摘願いたいと思います。
/*PWM機能によりRB3のLEDで滑らかに調光。可変抵抗で制御*/
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
unsigned int adconv(void);
// メイン関数
void main (void)
{
OSCCON = 0x40; //クロック周波数を1MHzに設定
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA =0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB =0x00;
ANSELA=0b00000001;//RA0をアナログピンに設定
ANSELB=0x00; //
// ADコンバータ設定
ADCON0 = 0b00000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 =0b10000000; // 結果数値は右寄せ、ADコンバータクロックはFOSC/2、基準電圧はVDD
//変数宣言
unsigned int num ;
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
while(1) {
// アナログ値の変換と読込み処理
adconv();
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
}
// アナログ値の変換と読込み処理
unsigned int adconv()
{
unsigned int temp;
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
temp = ADRESH ;//
temp = ( temp << 8 ) + ADRESL ;//RA0で読み込んだ値をtempに代入
return temp ;//読み込んだtempをnumに返す
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(99 + 1μ秒)
PR2 =100;
// デューティーサイクルを0msで初期化
CCPR1L = 0;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
有難うございます。
教科書でのPWMについては、半固定抵抗器を使っての明るさ調整がありませんので、
学習したいと思っています。
>というのはレジスタ名とダブル可能性があるからです。
止めます。
>重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
これからの、学習テーマの1つにしたいと思います。
自分で、PWM機能により可変抵抗器を使いLEDを滑らかに明るくしたり暗くしたりを考えましたが、
コンパイル不可
出来ましたら、ご指摘願いたいと思います。
/*PWM機能によりRB3のLEDで滑らかに調光。可変抵抗で制御*/
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
unsigned int adconv(void);
// メイン関数
void main (void)
{
OSCCON = 0x40; //クロック周波数を1MHzに設定
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA =0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB =0x00;
ANSELA=0b00000001;//RA0をアナログピンに設定
ANSELB=0x00; //
// ADコンバータ設定
ADCON0 = 0b00000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 =0b10000000; // 結果数値は右寄せ、ADコンバータクロックはFOSC/2、基準電圧はVDD
//変数宣言
unsigned int num ;
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
while(1) {
// アナログ値の変換と読込み処理
adconv();
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
}
// アナログ値の変換と読込み処理
unsigned int adconv()
{
unsigned int temp;
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
temp = ADRESH ;//
temp = ( temp << 8 ) + ADRESL ;//RA0で読み込んだ値をtempに代入
return temp ;//読み込んだtempをnumに返す
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(99 + 1μ秒)
PR2 =100;
// デューティーサイクルを0msで初期化
CCPR1L = 0;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
Re: ADRES
動作しました!
可変抵抗器で明るくなったり暗くなったりしました。
間違いを
MPLABが教えてくれました。
変更点
ANSELA →ANSEL
ANSELB → この行を削除
でも、ANSELにはAかBを付ける必要と思うのですが。
どうしてだろう?
と思って、、データシートを見ると、
88にはANSELはAがわの0〜7までしかありませんでした!!
可変抵抗器で明るくなったり暗くなったりしました。
間違いを
MPLABが教えてくれました。
変更点
ANSELA →ANSEL
ANSELB → この行を削除
でも、ANSELにはAかBを付ける必要と思うのですが。
どうしてだろう?
と思って、、データシートを見ると、
88にはANSELはAがわの0〜7までしかありませんでした!!
Re: ADRES
> while(1) { // アナログ値の変換と読込み処理
> adconv();
> num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
> CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
> }
の部分ですけど、adconv();を2回しています。
これ自体、間違いで残ったのだと思うのですけど、実際に複数のアナログ入力ピンを切り替えて使う時に
この手法を取ったりします。(1回を読み捨てる)
つまり、切り替えてすぐに取得すると間違った値になりやすいからです。
で、前に、
> 意味不明な__delay_us(20); //アクィジョン時間 20usを無くし
と言っていますけど、これもチャンネルを切り替えた後に、その電圧が安定するまで少し待っている、という意味だと思います。
今回はチャンネルを切り替える訳では無いので、無くても大丈夫かな?と思っています。
> // 周期を1μ秒に設定(99 + 1μ秒)
> PR2 =100;
以前もおかしな記述(コメント)があったのですけど、取りあえず放っておきました。
それは
> // 周期を1μ秒に設定(249 + 1μ秒)
> PR2 =249;
というものです。
この時の周期は(249+1)*4[us]=1000[us]=1msです。
で、今回の場合は
(100+1)*4=404usです。
あと、PWMやADCのメカニズム?をちゃんと押さえておいた方が良いです。
PWMだと、
PR2が周期、つまりこの値が上限に当たります。
duty値がHIGH→LOWに切り替わる値です。(場合によっては逆側に切り変える設定があったりもします)
タイマーが0で出力がHIGHになり、タイマーがduty値でLOWに切り替わります。
「249」と、250に1を引いた数にするのは249をカウントして、次の0になった時にHIGHになるので1を引きます。
そうした仕組みなので、PR2を101(101/255)にして、ADCの値をそのまま入れてもボリュームの半分以下の部分しか
反応しない(つまり、101/255以上になると常にHIGH)になっていると想像します。
感覚的にはどうだったでしょうか?
ボリュームのフルレンジを使って、明るさを調節したいのなら、PR2は255が適当でしょう。
勿論、ADCの値に101/255をかけたものをduty値に設定しても良いですけど。
で、
while(1) { // アナログ値の変換と読込み処理
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。つまり、PWMの1回の点滅よりも多く取得して、
切り替え値を操作しています。
そう言った時は適度なディレイを入れた方が良いと思います。(そのままで駄目という訳では無いですけど)
つまり、
while(1) {
num = adconv();
CCPR1L = num/4 ;
__delay_ms(10);
}
程度にするのが適当かな、と思う次第です。
で、これを書き換えると(まぁ、書き換えなくても良いのですけど)
while(1) {
CCPR1L = adconv()/4 ;
__delay_ms(10);
}
で、同じ意味です。
それが、No.1213で書いたArduinoの
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
です。こっちのdelay(10);も同じ意味で付けています。
> adconv();
> num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
> CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
> }
の部分ですけど、adconv();を2回しています。
これ自体、間違いで残ったのだと思うのですけど、実際に複数のアナログ入力ピンを切り替えて使う時に
この手法を取ったりします。(1回を読み捨てる)
つまり、切り替えてすぐに取得すると間違った値になりやすいからです。
で、前に、
> 意味不明な__delay_us(20); //アクィジョン時間 20usを無くし
と言っていますけど、これもチャンネルを切り替えた後に、その電圧が安定するまで少し待っている、という意味だと思います。
今回はチャンネルを切り替える訳では無いので、無くても大丈夫かな?と思っています。
> // 周期を1μ秒に設定(99 + 1μ秒)
> PR2 =100;
以前もおかしな記述(コメント)があったのですけど、取りあえず放っておきました。
それは
> // 周期を1μ秒に設定(249 + 1μ秒)
> PR2 =249;
というものです。
この時の周期は(249+1)*4[us]=1000[us]=1msです。
で、今回の場合は
(100+1)*4=404usです。
あと、PWMやADCのメカニズム?をちゃんと押さえておいた方が良いです。
PWMだと、
PR2が周期、つまりこの値が上限に当たります。
duty値がHIGH→LOWに切り替わる値です。(場合によっては逆側に切り変える設定があったりもします)
タイマーが0で出力がHIGHになり、タイマーがduty値でLOWに切り替わります。
「249」と、250に1を引いた数にするのは249をカウントして、次の0になった時にHIGHになるので1を引きます。
そうした仕組みなので、PR2を101(101/255)にして、ADCの値をそのまま入れてもボリュームの半分以下の部分しか
反応しない(つまり、101/255以上になると常にHIGH)になっていると想像します。
感覚的にはどうだったでしょうか?
ボリュームのフルレンジを使って、明るさを調節したいのなら、PR2は255が適当でしょう。
勿論、ADCの値に101/255をかけたものをduty値に設定しても良いですけど。
で、
while(1) { // アナログ値の変換と読込み処理
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。つまり、PWMの1回の点滅よりも多く取得して、
切り替え値を操作しています。
そう言った時は適度なディレイを入れた方が良いと思います。(そのままで駄目という訳では無いですけど)
つまり、
while(1) {
num = adconv();
CCPR1L = num/4 ;
__delay_ms(10);
}
程度にするのが適当かな、と思う次第です。
で、これを書き換えると(まぁ、書き換えなくても良いのですけど)
while(1) {
CCPR1L = adconv()/4 ;
__delay_ms(10);
}
で、同じ意味です。
それが、No.1213で書いたArduinoの
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
です。こっちのdelay(10);も同じ意味で付けています。
Re: ADRES

> adconv();
> num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
> CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
> }
の部分ですけど、adconv();を2回しています。
これ自体、間違いで残ったのだと思うのですけど、実際に複数のアナログ入力ピンを切り替えて使う時に
この手法を取ったりします。(1回を読み捨てる)
そのとおりです。1回を読み捨てるという内容です。
この意味合いで使いました。
であれば、OKですよね。
以前、間欠プログラムでも似たような以下や、あちこち見ていて、このアイデアを思いつきました。
可決プログラムから抜粋 ↓
// 永久ループ
while(1){
MODE=PORTA&0b11;
__delay_ms(50);
while(MODE!=(PORTA&0b11)){ //変化があればウェイト、無ければ確定
MODE=PORTA&0b11;
> 意味不明な__delay_us(20); //アクィジョン時間 20usを無くし
と言っていますけど、これもチャンネルを切り替えた後に、その電圧が安定するまで少し待っている、という意味だと思います。
今回はチャンネルを切り替える訳では無いので、無くても大丈夫かな?と思っています。
そうでしたか。チャタリング防止ですね。
> // 周期を1μ秒に設定(99 + 1μ秒)
> PR2 =100;
以前もおかしな記述(コメント)があったのですけど、取りあえず放っておきました。
この方法は、キットで遊ぼうの教科書にありました。
写真参照願います。
教科書では、周期を先に100μsとして、PR2を逆算しています。
ただ、プログラムの方は「周期を1μ・・・」となってしまっていましたので、間違えです。教科書では100μsです。
今回のように可変抵抗器で調光する場合、周期やPR2はどのように考えて決めるのでしょうか?
自分なりに思うと、
写真の公式によれば、
周期=(PR2+1)1/(動作処理速度÷4)
となっているので、
動作処理速度 1MHz(とりあえずにした値です)
100μs=(PR2+1)1/(1MHz÷4)
100×4/1-1=PR2
従って PR2=309μs
なので、動作を先に確認したかったので、PR2=100 としましたが、
本当は、309μsでした。
で、今直しました。
今のところ、LEDの状況を見ると、不満は、
VRを絞りると暗くなるけど、
明けきらない内に、頭打ちになって明るさが伴っていない感じです。
解消方法は有りますか?
以下に説明されていますが、文字だけでは、自分には良く分かりません。
もう少し、おいおい、なんとかしたいと思ってはいます。キットで遊ぼうの教科書を、今回のご指摘を踏まえて再度読み込んでみます。
1週間前まではチンプンカンプンでしたから、自分でもよくここまで来たかと思っているところです。
申し訳ありません。
ところで、250という数字が出てきますね。
255が適当でしょうとの事ですね。
周期を255にするPR2を逆算してプログに反映してみます。
> // 周期を1μ秒に設定(249 + 1μ秒)
> PR2 =249;
というものです。
この時の周期は(249+1)*4[us]=1000[us]=1msです。
で、今回の場合は
(100+1)*4=404usです。
>あと、PWMやADCのメカニズム?をちゃんと押さえておいた方が良いです。
PWMだと、
PR2が周期、つまりこの値が上限に当たります。
duty値がHIGH→LOWに切り替わる値です。(場合によっては逆側に切り変える設定があったりもします)
タイマーが0で出力がHIGHになり、タイマーがduty値でLOWに切り替わります。
「249」と、250に1を引いた数にするのは249をカウントして、次の0になった時にHIGHになるので1を引きます。
そうした仕組みなので、PR2を101(101/255)にして、ADCの値をそのまま入れてもボリュームの半分以下の部分しか
反応しない(つまり、101/255以上になると常にHIGH)になっていると想像します。
感覚的にはどうだったでしょうか?
ボリュームのフルレンジを使って、明るさを調節したいのなら、PR2は255が適当でしょう。
勿論、ADCの値に101/255をかけたものをduty値に設定しても良いですけど。
>で、
while(1) { // アナログ値の変換と読込み処理
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。つまり、PWMの1回の点滅よりも多く取得して、
切り替え値を操作しています。
そう言った時は適度なディレイを入れた方が良いと思います。(そのままで駄目という訳では無いですけど)
つまり、
while(1) {
num = adconv();
CCPR1L = num/4 ;
__delay_ms(10);
}
程度にするのが適当かな、と思う次第です。
で、これを書き換えると(まぁ、書き換えなくても良いのですけど)
分かりました。
>while(1) {
CCPR1L = adconv()/4 ;
__delay_ms(10);
}
で、同じ意味です。
それが、No.1213で書いたArduinoの
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
です。こっちのdelay(10);も同じ意味で付けています。
分かりました。
追記
周期255にしてPR2を1019にしたら、
調光の具合は良いです。
Re: ADRES
> そうでしたか。チャタリング防止ですね。
いいえ、そうではありません。
大抵のマイコンは、ADCの装置は1つで、それを複数のピンにADC装置の外側で、マルチプレクサで切り替えて使います。
それで複数のピンでADCを使えるようにしています。
で、接続ピンを切り替えるという事は異なる電圧に繋がる、という事です。その際の誤計測の防止の為に。1回読み飛ばしたり、ちょっと間を空けたりします。
そのテキストでは4MHz駆動=1MHzサイクル=1us/カウントでPR2が99なので
(99+1)*1us=100usです。
最初の例では1Mhz駆動=250kHzサイクル=4us/カウントで、PR2が249です。なので、
(249+1)*4=1000us=1msです。
今回のものも1Mhz駆動=250kHzサイクル=4us/カウントで、PR2は100です。なので
(100+1)*4=404us
となります。マイコンの速度は元になる駆動クロックから求まります。
※カウント数と1サイクル(カウント)時間をかけた方が分かりやすいと思います。
> 可変抵抗器で調光する場合、周期やPR2
まず、ボリュームによるADC入力と、PWM出力の間に関連性はありません。
「スイッチを押したらLEDを点灯させる」というものが、元々スイッチとLEDという無関係の物を
連動させているのと同じです。
LEDの適正周期に関してはマイコンとはあまり関係ありません。
人間にとって100Hz=10msよりも周期が長いとちらつきを感じると言われています。
なので、7セグ等はそれよりも短いものにします。
照明で利用するなら更に周期を短くする必要があるでしょう。
※周波数が高すぎて問題が起こることは殆どありません。ただし、周波数が高いほどノイズを誘発し易くなる事、
波形が乱れやすくなることがあります。
で、ボリュームの指している値、つまり、抵抗値の割合とPWMのduty比を一致させる、という考えもあります。
そうするとボリュームとLEDの電力が比例関係になります。
上で説明した通り、まず、カウンタの動作を考えてください。
カウンタがPR2の値になると0に戻ります。つまり、0→PR2を繰り返すカウンタです。
そして、duty値とカウント値を比較して、どちらが大きいかで、HIGH/LOW出力が決まります。。
duty値が大きすぎてPR2以上なら何も変化せずに、HIGH出力です。
時間や表示を最優先にしたいなら、周期(PR2+1)は10進数できりが良い値でしょうね。
100や250が適当だと思います。例えば%表示をしたい場合などはそのまま100でいいでしょう。
ADCが最大1023なので、
<ADC>*100/1023の計算をしてduty値のレジスタCCPR1Lに入れて、表示させることになります。
ただし、余りがでるのでちょっと嫌な感じがします。
※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
ボリュームとPWMを同期させるなら0xFFが良いです。
ADC値を4で割ればそのまま使えます。つまり、割り切れます。
> 周期255にしてPR2を1019にしたら、
意味が判りません。PR2レジスタは8ビットなので、そのまま
PR2=0xFF;//又はPR2=255;
です。
PR2=1019;
と、入りきらない値を書くと上位桁が消されて
PR2=251;//1019%256=251
と同じ意味になります。
※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
上記はプリスケーラが1:1のときはプリスケーラではなく、駆動クロックが代わりになるようです。
なので、10ビットのPWMになるのは変わらない、という事でした。
いいえ、そうではありません。
大抵のマイコンは、ADCの装置は1つで、それを複数のピンにADC装置の外側で、マルチプレクサで切り替えて使います。
それで複数のピンでADCを使えるようにしています。
で、接続ピンを切り替えるという事は異なる電圧に繋がる、という事です。その際の誤計測の防止の為に。1回読み飛ばしたり、ちょっと間を空けたりします。
そのテキストでは4MHz駆動=1MHzサイクル=1us/カウントでPR2が99なので
(99+1)*1us=100usです。
最初の例では1Mhz駆動=250kHzサイクル=4us/カウントで、PR2が249です。なので、
(249+1)*4=1000us=1msです。
今回のものも1Mhz駆動=250kHzサイクル=4us/カウントで、PR2は100です。なので
(100+1)*4=404us
となります。マイコンの速度は元になる駆動クロックから求まります。
※カウント数と1サイクル(カウント)時間をかけた方が分かりやすいと思います。
> 可変抵抗器で調光する場合、周期やPR2
まず、ボリュームによるADC入力と、PWM出力の間に関連性はありません。
「スイッチを押したらLEDを点灯させる」というものが、元々スイッチとLEDという無関係の物を
連動させているのと同じです。
LEDの適正周期に関してはマイコンとはあまり関係ありません。
人間にとって100Hz=10msよりも周期が長いとちらつきを感じると言われています。
なので、7セグ等はそれよりも短いものにします。
照明で利用するなら更に周期を短くする必要があるでしょう。
※周波数が高すぎて問題が起こることは殆どありません。ただし、周波数が高いほどノイズを誘発し易くなる事、
波形が乱れやすくなることがあります。
で、ボリュームの指している値、つまり、抵抗値の割合とPWMのduty比を一致させる、という考えもあります。
そうするとボリュームとLEDの電力が比例関係になります。
上で説明した通り、まず、カウンタの動作を考えてください。
カウンタがPR2の値になると0に戻ります。つまり、0→PR2を繰り返すカウンタです。
そして、duty値とカウント値を比較して、どちらが大きいかで、HIGH/LOW出力が決まります。。
duty値が大きすぎてPR2以上なら何も変化せずに、HIGH出力です。
時間や表示を最優先にしたいなら、周期(PR2+1)は10進数できりが良い値でしょうね。
100や250が適当だと思います。例えば%表示をしたい場合などはそのまま100でいいでしょう。
ADCが最大1023なので、
<ADC>*100/1023の計算をしてduty値のレジスタCCPR1Lに入れて、表示させることになります。
ただし、余りがでるのでちょっと嫌な感じがします。
※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
ボリュームとPWMを同期させるなら0xFFが良いです。
ADC値を4で割ればそのまま使えます。つまり、割り切れます。
> 周期255にしてPR2を1019にしたら、
意味が判りません。PR2レジスタは8ビットなので、そのまま
PR2=0xFF;//又はPR2=255;
です。
PR2=1019;
と、入りきらない値を書くと上位桁が消されて
PR2=251;//1019%256=251
と同じ意味になります。
※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
上記はプリスケーラが1:1のときはプリスケーラではなく、駆動クロックが代わりになるようです。
なので、10ビットのPWMになるのは変わらない、という事でした。
Re: ADRES
こんばんは。
今日も、MPLABさんのPWMとADコンバーターの記事を読んできました。
少しは分かったような感じがします。
No1229
>で、
@ while(1) { // アナログ値の変換と読込み処理
A num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
B CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。
280μsとは、@+A+Bのプログラム内容をPICがこなす時間ですか?
MPLABIDXのシュミレーター機能を使えば、プログラムの作業時間が分かるのかな?
やってみたけど、難しい。
尚、__delay_ms(10);は追加しました。
No1230
>※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
良く分からないので、PR2は249μsにしました。
PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
今は、これくらいにします。
No1230
>※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
この事ですか?
ともかく、ここまでプログラムを組めるようになったので嬉しいです。
今日も、MPLABさんのPWMとADコンバーターの記事を読んできました。
少しは分かったような感じがします。
No1229
>で、
@ while(1) { // アナログ値の変換と読込み処理
A num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
B CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。
280μsとは、@+A+Bのプログラム内容をPICがこなす時間ですか?
MPLABIDXのシュミレーター機能を使えば、プログラムの作業時間が分かるのかな?
やってみたけど、難しい。
尚、__delay_ms(10);は追加しました。
No1230
>※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
良く分からないので、PR2は249μsにしました。
PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
今は、これくらいにします。
No1230
>※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
この事ですか?
ともかく、ここまでプログラムを組めるようになったので嬉しいです。
Re: ADRES
「1回で」、というのはAとBという意味です。
勿論それは駆動クロックによって大きく変動します。
シミュレータで以前調べたのを利用して換算しました。
今回、直接調べた訳では無いです。
以前調べた時は4MHz動作の時にADC変換が68usだったと思います。
その4倍位です。Bは2サイクル=8us程度でしょう。
> PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
だから、PWMとADCは、全くの別物です。ADCは電圧計です。PWMは高速の点滅です。
今回はそれを組み合わせただけです。まぁ、そういった使い方もよくあるとは思いますけど。
ただ、今まで言いませんでしたけど、ADCや時間と比例関係で動作させるのではなく、累乗?のようにすることもあります。
つまり、8段階の明るさとして、均等の32、64、96、128…256ではなく、2,4,8,16…256のように進行させたりします。
均等割りだと急に明るくなり、明るい部分ではあまり変化が感じません。逆に暗い部分は急に変化しているように感じます。
100分割(%)の計算「<ADC>*100/1023」だと、判り難いのでこれを4分割の式にしてみると「<ADC>*4/1023」になります。
でもこの計算式だとADCの値が0〜255だと0です。256〜511が1です。512〜767が2です。768〜1022が3です。そして、1023だけが4になります。
まぁ、今回の事とはあまり関係ない話です。
> DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
> この事ですか?
そうです。その比較対象のタイマーの方です。
タイマーのその部分はTIMER2のカウント外です。なので、カウントしている数値は読み出せません。
プリスケーラのカウンタ部分が使われている、というのをうろ覚えしていたのですが、
1:1ということはプリスケーラが使われていません。
なので、TIMER2のみで比較しているのでは?と思ったのですが、駆動クロックが利用されている、という記述を見つけました。
duty値の下位2bitも有効です。
上限(周期)が上位8ビット指定で、比較(duty値)は10ビット指定、という点は判り難い構成ですね。
そして、本体となるタイマーも下位2ビットは見えない部分で作られています。
勿論それは駆動クロックによって大きく変動します。
シミュレータで以前調べたのを利用して換算しました。
今回、直接調べた訳では無いです。
以前調べた時は4MHz動作の時にADC変換が68usだったと思います。
その4倍位です。Bは2サイクル=8us程度でしょう。
> PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
だから、PWMとADCは、全くの別物です。ADCは電圧計です。PWMは高速の点滅です。
今回はそれを組み合わせただけです。まぁ、そういった使い方もよくあるとは思いますけど。
ただ、今まで言いませんでしたけど、ADCや時間と比例関係で動作させるのではなく、累乗?のようにすることもあります。
つまり、8段階の明るさとして、均等の32、64、96、128…256ではなく、2,4,8,16…256のように進行させたりします。
均等割りだと急に明るくなり、明るい部分ではあまり変化が感じません。逆に暗い部分は急に変化しているように感じます。
100分割(%)の計算「<ADC>*100/1023」だと、判り難いのでこれを4分割の式にしてみると「<ADC>*4/1023」になります。
でもこの計算式だとADCの値が0〜255だと0です。256〜511が1です。512〜767が2です。768〜1022が3です。そして、1023だけが4になります。
まぁ、今回の事とはあまり関係ない話です。
> DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
> この事ですか?
そうです。その比較対象のタイマーの方です。
タイマーのその部分はTIMER2のカウント外です。なので、カウントしている数値は読み出せません。
プリスケーラのカウンタ部分が使われている、というのをうろ覚えしていたのですが、
1:1ということはプリスケーラが使われていません。
なので、TIMER2のみで比較しているのでは?と思ったのですが、駆動クロックが利用されている、という記述を見つけました。
duty値の下位2bitも有効です。
上限(周期)が上位8ビット指定で、比較(duty値)は10ビット指定、という点は判り難い構成ですね。
そして、本体となるタイマーも下位2ビットは見えない部分で作られています。
Re: ADRES

122μsだと出ました。
while(1) {
// アナログ値の変換と読込み処理
@ adconv();
Anum = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
BCCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
__delay_ms(10);
それで話しついでですが、
猛牛ロックさんが言われていることで、PICの欠点ですが、
少し分かってきました。
ADコンバーターには速さの設定が有ります。
合成写真右側です。
早い方が良いと思いますが、一番早いのは、2Toscですか?
単純に2Toscを選んでよいのでしょうか?
Re: ADRES
> 早い方が良いと思いますが、一番早いのは、2Toscですか?
勿論、早い方が良いと言えますけど、信頼性との兼ね合いもあります。
また、計測の回数が増えるほど、他の作業の邪魔になる場合もあります。
まぁ、今回の場合は、
> while(ADCON0bits.GO_DONE); //変換完了待ち
とウェイトしていますから、早い方が良いですね。
今回のようなウェイトする場合と、完了→割り込みをする場合があります。
で、今回(1MHz駆動)の場合、設定上一番早いのは、2Toscになります。
もし、4MHz駆動にした場合は8Toscです。
だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
勿論、早い方が良いと言えますけど、信頼性との兼ね合いもあります。
また、計測の回数が増えるほど、他の作業の邪魔になる場合もあります。
まぁ、今回の場合は、
> while(ADCON0bits.GO_DONE); //変換完了待ち
とウェイトしていますから、早い方が良いですね。
今回のようなウェイトする場合と、完了→割り込みをする場合があります。
で、今回(1MHz駆動)の場合、設定上一番早いのは、2Toscになります。
もし、4MHz駆動にした場合は8Toscです。
だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
Re: ADRES
>だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
分かりました。
今は、内部クロックが1Mhzです、
これをもっと早くすれば、全体の速度は早くなるのですか?
それと、No1223の写真にあるデータシートの注意書きをグーグル翻訳をしたら ↓
注1:RCソースの標準TAD時間は4μsですが、2~6μsの間で変化する可能性があります。
あるサイトで見つけました。
TAD=(PICクロックの周期)×(Tosc)
今回は、OSCCON = 0x40; //クロック周波数を1MHzに設定 →1μs
Toscは、現在、2Toscなので、No1223写真のデータシートにより
1.25Mhz → 0.8μs
Tosc=1μs × 0.8μs
Tosc=0.8 となってOkでしょうか?
とおもったら、
Operationの方を掛けるのですか?
私自身は測定出来るぎりぎりのラインは使いません。
分かりました。
今は、内部クロックが1Mhzです、
これをもっと早くすれば、全体の速度は早くなるのですか?
それと、No1223の写真にあるデータシートの注意書きをグーグル翻訳をしたら ↓
注1:RCソースの標準TAD時間は4μsですが、2~6μsの間で変化する可能性があります。
あるサイトで見つけました。
TAD=(PICクロックの周期)×(Tosc)
今回は、OSCCON = 0x40; //クロック周波数を1MHzに設定 →1μs
Toscは、現在、2Toscなので、No1223写真のデータシートにより
1.25Mhz → 0.8μs
Tosc=1μs × 0.8μs
Tosc=0.8 となってOkでしょうか?
とおもったら、
Operationの方を掛けるのですか?
Re: ADRES
御免なさい。間違った事を書いてしまいました。
基本は1.6us〜6.4usの間になるように選択する、と言う事みたいです。
なので、1MHz駆動(1us周期)の場合は、2TOSCか4TOSCのどちらかにする、と言う事になると思います。
より推奨されるのが4usになる4TOSCなのでしょうけど、2TOSCでも全く問題はないと思います。
※つまり、2TOSCの選択はギリギリという事ではなく、推奨される範囲の値、という事。
また、一番下のRCを選択しておくのが無難だとも感じました。これはおおよそ4usになるRCです。
これを選択しておけば、1MHz駆動のソースでも、4MHz駆動のソースでも共通で済みます。
また、sleepという機能を使った時にADC変換を行いたい時にはこのRCをを選択している必要があります。
※自分なりにデータシートを読み取った感想ですが、確証はありません。
基本は1.6us〜6.4usの間になるように選択する、と言う事みたいです。
なので、1MHz駆動(1us周期)の場合は、2TOSCか4TOSCのどちらかにする、と言う事になると思います。
より推奨されるのが4usになる4TOSCなのでしょうけど、2TOSCでも全く問題はないと思います。
※つまり、2TOSCの選択はギリギリという事ではなく、推奨される範囲の値、という事。
また、一番下のRCを選択しておくのが無難だとも感じました。これはおおよそ4usになるRCです。
これを選択しておけば、1MHz駆動のソースでも、4MHz駆動のソースでも共通で済みます。
また、sleepという機能を使った時にADC変換を行いたい時にはこのRCをを選択している必要があります。
※自分なりにデータシートを読み取った感想ですが、確証はありません。
Re: ADRES
パソコンでも、クロック数は、大きい方、2Mhより4Mhzといった具合。
PICも内蔵クロック数を大きく設定した方が、PICの動作速度が速くなるのでしょうか?
今は、1Mhzに設定。
TAD=(PICクロックの周期)×(Tosc)
この式は合っていたようですね。
追記
時期PICはやはり、日本語のデータシートがあった方が良いですね。
16F1823 これに近い奴16F1827
http://akizukidenshi.com/catalog/g/gI-04430/
であれば、
TAD=1μs×2Tosc
TAD=2μs
でもRCといった選択肢があったわけですね。
有難うございます。
日本語のデータシートがあるPIC16F1823が欲しい。
PICも内蔵クロック数を大きく設定した方が、PICの動作速度が速くなるのでしょうか?
今は、1Mhzに設定。
TAD=(PICクロックの周期)×(Tosc)
この式は合っていたようですね。
追記
時期PICはやはり、日本語のデータシートがあった方が良いですね。
16F1823 これに近い奴16F1827
http://akizukidenshi.com/catalog/g/gI-04430/
であれば、
TAD=1μs×2Tosc
TAD=2μs
でもRCといった選択肢があったわけですね。
有難うございます。
日本語のデータシートがあるPIC16F1823が欲しい。
Re: ADRES
速度の考え方は、PCと同じです。
通常、4MHz駆動なら1MHz駆動の4倍速く動いていますから、1MHz→4MHzとすると
動作時間が1/4になります。
ただし、今回のようにADCは、ADCの変換速度はある範囲内、つまり一定にしてほしい、と言う事です。
> TAD=(PICクロックの周期)×(Tosc)
> この式は合っていたようですね。
いいえ、
(PICクロックの周期)と(Tosc)は同一です。
Fosc「周波数」=1/Tosc です。
で、
TADを「1.6us〜6.4usの間になるように」倍率を選択する、と言う事です。
1MHz駆動ならToscは1usです。そして設定できる倍率は、1,2,4,8、…なので、
2か4を選択する事になります。
RCはただ選択するだけです。計算はありません。おおよそ4usが標準値になるように作られているけども、
2〜6usのアバウトの範囲としか保証しない、と言う事です。
追記
16F1827だと、ちょっと性能が上がって、推奨範囲が広がっています。
数値は見つけられなかったですけど、例えば、0.8us〜6.4usのように速い速度のほうが広がったのだと思います。
なので、TADが1usも推奨範囲になっていますね。
そして、RCの標準値も1.6usになっています。
通常、4MHz駆動なら1MHz駆動の4倍速く動いていますから、1MHz→4MHzとすると
動作時間が1/4になります。
ただし、今回のようにADCは、ADCの変換速度はある範囲内、つまり一定にしてほしい、と言う事です。
> TAD=(PICクロックの周期)×(Tosc)
> この式は合っていたようですね。
いいえ、
(PICクロックの周期)と(Tosc)は同一です。
Fosc「周波数」=1/Tosc です。
で、
TADを「1.6us〜6.4usの間になるように」倍率を選択する、と言う事です。
1MHz駆動ならToscは1usです。そして設定できる倍率は、1,2,4,8、…なので、
2か4を選択する事になります。
RCはただ選択するだけです。計算はありません。おおよそ4usが標準値になるように作られているけども、
2〜6usのアバウトの範囲としか保証しない、と言う事です。
追記
16F1827だと、ちょっと性能が上がって、推奨範囲が広がっています。
数値は見つけられなかったですけど、例えば、0.8us〜6.4usのように速い速度のほうが広がったのだと思います。
なので、TADが1usも推奨範囲になっていますね。
そして、RCの標準値も1.6usになっています。
Duty比を変えるプログラム
以下のプログラムでLEDが暗くなる、明るくなるを繰り返すと思ったのですが、
点灯したままに見えます。
何処が悪いのでしょうか?
dutyを変える数値の50と1000はいい加減です。
動作確認したかったので、適当な数値です。
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
// LEDをPWM制御
// デューテー比は10%100%の制御にする
while(1) {
// 5% -> 100%の制御
for(duty=50; duty<=1000; duty++) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
}
点灯したままに見えます。
何処が悪いのでしょうか?
dutyを変える数値の50と1000はいい加減です。
動作確認したかったので、適当な数値です。
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
// LEDをPWM制御
// デューテー比は10%100%の制御にする
while(1) {
// 5% -> 100%の制御
for(duty=50; duty<=1000; duty++) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
}
Re: Duty比を変えるプログラム
上手くいきました。
動作しなかった理由が分かりました。
それは、Configでした。
色々と調べたので、関数の使い方など、ちょっとは分かるようになりました。
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
// メイン関数
void main (void)
{
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0x20;
TRISB = 0x00;
OSCCON = 0x40; //クロック周波数を1MHzに設定
ANSEL = 0x00; //すべてのピンをデジタルモードに設定
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
unsigned short duty;// PWMのデューティーサイクル
while(1){
// 5% -> 100%の制御
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
}
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(249 + 1μ秒)
PR2 =249;
// デューティーサイクルを0.5ms
CCPR1L = 500/4;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
動作しなかった理由が分かりました。
それは、Configでした。
色々と調べたので、関数の使い方など、ちょっとは分かるようになりました。
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
// メイン関数
void main (void)
{
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0x20;
TRISB = 0x00;
OSCCON = 0x40; //クロック周波数を1MHzに設定
ANSEL = 0x00; //すべてのピンをデジタルモードに設定
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
unsigned short duty;// PWMのデューティーサイクル
while(1){
// 5% -> 100%の制御
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
}
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(249 + 1μ秒)
PR2 =249;
// デューティーサイクルを0.5ms
CCPR1L = 500/4;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
Re: Duty比を変えるプログラム
TRさん,猛牛ロックさん,こんばんは。
TRさん,頑張ってますね。
PWMモード復習してみました。
TRさんの書かれたプログラムですが,
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
の
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビッ
部分ですが,ビットの操作なので,0か1がはいるのですが,
10進数を代入するとどうなるのでしょうね。
ちゃんと,下位の1ビット目と2ビットめがそれぞれ入る
のでしょうか。
ちょっと分からないところです。
もしかして,
CCP1CONbits.CCP1X=((duty>>1)&1);// dutyの1ビット目代入
CCP1CONbits.CCP1Y=(duty&1);// dutyの0ビット目を代入
みたいにしないとだめかもしれません。
TRさん,頑張ってますね。
PWMモード復習してみました。
TRさんの書かれたプログラムですが,
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
の
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビッ
部分ですが,ビットの操作なので,0か1がはいるのですが,
10進数を代入するとどうなるのでしょうね。
ちゃんと,下位の1ビット目と2ビットめがそれぞれ入る
のでしょうか。
ちょっと分からないところです。
もしかして,
CCP1CONbits.CCP1X=((duty>>1)&1);// dutyの1ビット目代入
CCP1CONbits.CCP1Y=(duty&1);// dutyの0ビット目を代入
みたいにしないとだめかもしれません。
Re: Duty比を変えるプログラム
maboさん、レス有難うございます。
私も、10進数を=にしてよいものかと感じていますが、
ツールラボさんの解説を貼り付けます。
さて、500をわざわざ2進数に変換して、その上位8ビットや下位2ビットを求めるのは面倒ですので、楽な方法でプログラムを書くことにしましょう。
まず、上位8ビットの求め方です。2進数表現では、2で割ると1桁右に移動します。一番右の桁は無くなります。
Pic app 4 binary calculation
さらに2で割ればもう1桁右に移動します。ということは、500を4で割れば上位8ビットを取り出すことができます。
ということで、CCPR1Lに上位8ビットを設定するには、
CCPR1L = 500/4;
と書けばよいことになります。
私も、10進数を=にしてよいものかと感じていますが、
ツールラボさんの解説を貼り付けます。
さて、500をわざわざ2進数に変換して、その上位8ビットや下位2ビットを求めるのは面倒ですので、楽な方法でプログラムを書くことにしましょう。
まず、上位8ビットの求め方です。2進数表現では、2で割ると1桁右に移動します。一番右の桁は無くなります。
Pic app 4 binary calculation
さらに2で割ればもう1桁右に移動します。ということは、500を4で割れば上位8ビットを取り出すことができます。
ということで、CCPR1Lに上位8ビットを設定するには、
CCPR1L = 500/4;
と書けばよいことになります。
Re: Duty比を変えるプログラム
TRさん,猛牛ロックさん,おはようございます。
>上位8ビット
を取り出すにには,右に,2ビット分シフトですから,
意味的には,4で割ることと同意ですよね。
このへんの操作が面倒ですよね。
下位ビットの設定には,
CCP1CONbits.DC1B
がTRさんのおっしゃるツールラボでは使われてましたね。
CCP1CONbits.DC1B = 500
あるいは,
CCP1CONbits.DC1B = 500 && 0b11;
のように書いてありました。
CCP1CONbits.DC1B
だと,
CCP1CONbits.CCP1X
CCP1CONbits.CCP1Y
を使わなくても一度ですむみたいですね。
PWMはすっかり忘れていました。
>上位8ビット
を取り出すにには,右に,2ビット分シフトですから,
意味的には,4で割ることと同意ですよね。
このへんの操作が面倒ですよね。
下位ビットの設定には,
CCP1CONbits.DC1B
がTRさんのおっしゃるツールラボでは使われてましたね。
CCP1CONbits.DC1B = 500
あるいは,
CCP1CONbits.DC1B = 500 && 0b11;
のように書いてありました。
CCP1CONbits.DC1B
だと,
CCP1CONbits.CCP1X
CCP1CONbits.CCP1Y
を使わなくても一度ですむみたいですね。
PWMはすっかり忘れていました。
Re: Duty比を変えるプログラム
10進数をレジスタに=にしてもOKみたいですね!
CCP1CONbits.DC1B 
これですが、16F88の場合、無いようでしたので、1バイトづつとじました。
本当に忘れるほうが早い (笑)
CCP1CONbits.DC1B 
これですが、16F88の場合、無いようでしたので、1バイトづつとじました。
本当に忘れるほうが早い (笑)
Re: Duty比を変えるプログラム
ツールラボの話は別のマイコンの話です。
CCP1CONbits.DC1Bは2bitの器です。
なので、4で割った余りが入ります。
CCP1CONbits.CCP1X=500;
CCP1CONbits.CCP1Y=500;
なら、当然、両方とも0です。
1bitの器(が2つ)しかない所にそれよりも多い数を入れると、繰り上がった分は消えます。
簡単に言えば2で割った余りです。
duty比が1なら判るでしょう?
CCP1CONbits.CCP1X=1;
CCP1CONbits.CCP1Y=1;
と書いて、Xの方が0になる筈ないですよね?
MABOさんの言う通りの処理が必要です。
まぁ、端数なので気付かない可能性が大きいですけど。
…と思ったら
> CCP1CONbits.DC1B = 500 && 0b11;
これは駄目です。そのサイトに書いてあったんですか?
これでは500の部分が0以外なら、CCP1CONbits.DC1Bには0b01が入ります。
0の場合は0です。
(本当は「&&」では無く「&」)
CCP1CONbits.DC1Bは2bitの器です。
なので、4で割った余りが入ります。
CCP1CONbits.CCP1X=500;
CCP1CONbits.CCP1Y=500;
なら、当然、両方とも0です。
1bitの器(が2つ)しかない所にそれよりも多い数を入れると、繰り上がった分は消えます。
簡単に言えば2で割った余りです。
duty比が1なら判るでしょう?
CCP1CONbits.CCP1X=1;
CCP1CONbits.CCP1Y=1;
と書いて、Xの方が0になる筈ないですよね?
MABOさんの言う通りの処理が必要です。
まぁ、端数なので気付かない可能性が大きいですけど。
…と思ったら
> CCP1CONbits.DC1B = 500 && 0b11;
これは駄目です。そのサイトに書いてあったんですか?
これでは500の部分が0以外なら、CCP1CONbits.DC1Bには0b01が入ります。
0の場合は0です。
(本当は「&&」では無く「&」)
Re: Duty比を変えるプログラム
猛牛ロックさんへ
>簡単に言えば2で割った余りです。
なんだろうと思って、検索したら、 2で割った、余りの1を下から並べると、
2進数表現になるとありました。
でも、10進数に対応する2進数の上位8ビットを取り出すために
10進数÷4 とすればOKというプログラムは、、、
何か不思議な感じです。
オシログラフで確認できたらいいのでしょうけど、自分のレベルでは持っていません。
ただ、実験してみると、次第に明るくなって、次第に暗くなりました。
・10進数÷4 とすればOKというプログラム
CCPR1L = 500/4;
・参考したURL
https://arigato-lucky.at.webry.info/200710/article_17.html
※
今回も、一つ覚えたので、良かったです。
でなんですが、
PWM機能を使うために、ADRESというレジスタ
があるようなのですが、
これは、16F88にはあるのでしょうか?
自分としては見つかりませんでした。
良かったら、教えて下さい。
>簡単に言えば2で割った余りです。
なんだろうと思って、検索したら、 2で割った、余りの1を下から並べると、
2進数表現になるとありました。
でも、10進数に対応する2進数の上位8ビットを取り出すために
10進数÷4 とすればOKというプログラムは、、、
何か不思議な感じです。
オシログラフで確認できたらいいのでしょうけど、自分のレベルでは持っていません。
ただ、実験してみると、次第に明るくなって、次第に暗くなりました。
・10進数÷4 とすればOKというプログラム
CCPR1L = 500/4;
・参考したURL
https://arigato-lucky.at.webry.info/200710/article_17.html
※
今回も、一つ覚えたので、良かったです。
でなんですが、
PWM機能を使うために、ADRESというレジスタ
があるようなのですが、
これは、16F88にはあるのでしょうか?
自分としては見つかりませんでした。
良かったら、教えて下さい。
Re: Duty比を変えるプログラム
10進数、2進数、16進数などは単に人が識別するためのものです。
値としてはどれも同じです。
2進数「表現」とあるように、人にとって意味がある区別の仕方です。
> 10進数÷4 とすればOKというプログラムは、、、
10進数は隣の位が10倍、或いは1/10です。2進数の隣の位は同じ様に2倍、或いは1/2です。
○進数は○で、位が上がる、と言う事です。
> 実験してみると、次第に明るくなって、次第に暗くなりました。
10桁ある中で、下2桁ですから、ほとんど感じないでしょうね。
確認するならレジスタを読み込めば判りますよ。
> 自分としては見つかりませんでした。
検索すればすぐ判りますよ。
値としてはどれも同じです。
2進数「表現」とあるように、人にとって意味がある区別の仕方です。
> 10進数÷4 とすればOKというプログラムは、、、
10進数は隣の位が10倍、或いは1/10です。2進数の隣の位は同じ様に2倍、或いは1/2です。
○進数は○で、位が上がる、と言う事です。
> 実験してみると、次第に明るくなって、次第に暗くなりました。
10桁ある中で、下2桁ですから、ほとんど感じないでしょうね。
確認するならレジスタを読み込めば判りますよ。
> 自分としては見つかりませんでした。
検索すればすぐ判りますよ。
Re: Duty比を変えるプログラム

>CCP1CONbits.DC1B = 500 && 0b11;
マスク処理ですから,猛牛ロックさんのおっしゃるように,
&一つですね。
HPに書いてある記事は,なんの疑いもなく信じてしまうところがあるので,
気をつけないとだめですね。
https://tool-lab.com/make/pic-app-4/
2進数→←10進数の変換をよくエクセルを使ってやります。
ただ,今までは,小さな数でしたので,問題なくできましたが,
512だと備え付けの関数ではだめでした。
=DEC2BIN(N,n)
という関数ですが,
−513<N<512
1<=n<11
でした。符号も絡んでくるので,ちょっと頭痛くなります。
Re: Duty比を変えるプログラム
ADRESレジスターですが、
最初、ADRESで検索しても無かったので、
次に「A/D Result register」でも検索しましたが、
ADRESの表記は見当たりませんでした。
というのも、MPLAB IDEにADRESと書き込むと
エラーを起こしたので、
16F88の場合は、レジスターの名称が違うと思ったので、
探し始めました。
ADRESH、とADRESLの表記はあるのですが、
ADRESに対応する16F88版レジスターが見つかりません。
maboさん
2進数変換ですが、自分は下記サイトを利用しています。
https://hogehoge.tk/tool/number.html
最初、ADRESで検索しても無かったので、
次に「A/D Result register」でも検索しましたが、
ADRESの表記は見当たりませんでした。
というのも、MPLAB IDEにADRESと書き込むと
エラーを起こしたので、
16F88の場合は、レジスターの名称が違うと思ったので、
探し始めました。
ADRESH、とADRESLの表記はあるのですが、
ADRESに対応する16F88版レジスターが見つかりません。
maboさん
2進数変換ですが、自分は下記サイトを利用しています。
https://hogehoge.tk/tool/number.html
Re: Duty比を変えるプログラム

TRさん,2進数変換のHP紹介ありがとうございます。
>ADRESレジスターですが、
データシート見てみると,A/D変換の結果の格納で使われてるようですね。
ADRESH
ADRESL
の記述がデータしとの下の方にありました。
A/D変換の結果の格納に使うみたいですね。
http://physics.cocolog-nifty.com/weblog/2012/07/post-2147.html
にプログラムでてます。
Re: Duty比を変えるプログラム
maboさん有り難うございます
やはり、F88にはADRESレジスタが無かったようですね。
紹介先の管理人は、対処として、
unsigned int adconv() 関数を作っています。
でも、プログラムで
return (ADRESH<<8) + ADRESL
このように、+として10バイトを取り出せるのは、驚きました!?
16F88は面倒くさい。
もっと簡単なPICが欲しいです
やはり、F88にはADRESレジスタが無かったようですね。
紹介先の管理人は、対処として、
unsigned int adconv() 関数を作っています。
でも、プログラムで
return (ADRESH<<8) + ADRESL
このように、+として10バイトを取り出せるのは、驚きました!?
16F88は面倒くさい。
もっと簡単なPICが欲しいです
Re: Duty比を変えるプログラム
maboさん、こんばんは。
ご紹介くださった、頁のLチカを早速検証しました。
IDXの今バージョンに変更し、
「<」を半角にしたら、動作しました。
で、以下のプログラムですが、
今までと、ちと違う書き方をしています。
For文です。
{}内に、今までは、処理する内容を書いてきましたが、
空欄の場合は、単に++の足し算をするだけ、
つまり、時間を稼ぐだけのことになるのでしょうか?
http://physics.cocolog-nifty.com/weblog/2012/07/post-2147.html
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
ご紹介くださった、頁のLチカを早速検証しました。
IDXの今バージョンに変更し、
「<」を半角にしたら、動作しました。
で、以下のプログラムですが、
今までと、ちと違う書き方をしています。
For文です。
{}内に、今までは、処理する内容を書いてきましたが、
空欄の場合は、単に++の足し算をするだけ、
つまり、時間を稼ぐだけのことになるのでしょうか?
http://physics.cocolog-nifty.com/weblog/2012/07/post-2147.html
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
Re: Duty比を変えるプログラム
TRさん,こんばんは。
>空欄の場合は、単に++の足し算をするだけ、
>つまり、時間を稼ぐだけのことになるのでしょうか?
そうだと思います。
意味的には,
__delay_ms();
と同じような処理をすることになると思いますが,
確か,__delay_ms() では,括弧の中に変数は入れられなかった,ような気がします。
それで,こんな風にしたのかなと思います。
変数が入れられれば __delay_ms() 使えばいいのでしょうけど。
>空欄の場合は、単に++の足し算をするだけ、
>つまり、時間を稼ぐだけのことになるのでしょうか?
そうだと思います。
意味的には,
__delay_ms();
と同じような処理をすることになると思いますが,
確か,__delay_ms() では,括弧の中に変数は入れられなかった,ような気がします。
それで,こんな風にしたのかなと思います。
変数が入れられれば __delay_ms() 使えばいいのでしょうけど。
Re: Duty比を変えるプログラム
> F88にはADRESレジスタが無かったようですね。
え?まぁ、無いとも言えますけど、あるとも言えます。
ADRESHとADRESLがADRESです。
10ビット性能のADCなので、必然的に2つに分かれます。
> 16F88は面倒くさい。
いやいや、16F88は使いやすさで生き残っているマイコンですよ。
ウィンドウズで言えばXPみたいなものかなぁ。
最近のものは覚えることが2〜3倍程度あります。16F19155のデータシートなんて735ページまであります。
頭に来るのは、それ以外にエラッタのデータシートがあったり、データシートのバージョンで重要な事項が
載っていなかったりすることです。
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
はあまり参考にしない方が良いです。
動かしてみないとどうなるか判らないコードです。
本人も判っていなかったのでは?
やりたかったのは
while (1){
tmp = adconv();
RB3 = 1;
__delay_ms(tmp);
RB3 = 0;
__delay_ms(tmp);
}
のような事だと思いますけど、
(__delay_ms()の引数に変数を入れては駄目ですけど)
for(i = 0; i<tmp; i++){}
の部分は
volatile unsigned int i;
とiを宣言していないと省略される場合があります。
※内容が{}と空だからです。
そして、その次の
;
は単独です。for文とは関係ありません。
そして、
__delay_ms(2);
も単独です。
つまり、3つの何もしない命令を並べています。
通常は
RB3 = 1;
for(i = 0; i<tmp; i++)__delay_ms(1);
RB3 = 0;
for(i = 0; i<tmp; i++)__delay_ms(1);
です。
え?まぁ、無いとも言えますけど、あるとも言えます。
ADRESHとADRESLがADRESです。
10ビット性能のADCなので、必然的に2つに分かれます。
> 16F88は面倒くさい。
いやいや、16F88は使いやすさで生き残っているマイコンですよ。
ウィンドウズで言えばXPみたいなものかなぁ。
最近のものは覚えることが2〜3倍程度あります。16F19155のデータシートなんて735ページまであります。
頭に来るのは、それ以外にエラッタのデータシートがあったり、データシートのバージョンで重要な事項が
載っていなかったりすることです。
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
はあまり参考にしない方が良いです。
動かしてみないとどうなるか判らないコードです。
本人も判っていなかったのでは?
やりたかったのは
while (1){
tmp = adconv();
RB3 = 1;
__delay_ms(tmp);
RB3 = 0;
__delay_ms(tmp);
}
のような事だと思いますけど、
(__delay_ms()の引数に変数を入れては駄目ですけど)
for(i = 0; i<tmp; i++){}
の部分は
volatile unsigned int i;
とiを宣言していないと省略される場合があります。
※内容が{}と空だからです。
そして、その次の
;
は単独です。for文とは関係ありません。
そして、
__delay_ms(2);
も単独です。
つまり、3つの何もしない命令を並べています。
通常は
RB3 = 1;
for(i = 0; i<tmp; i++)__delay_ms(1);
RB3 = 0;
for(i = 0; i<tmp; i++)__delay_ms(1);
です。
Re: Duty比を変えるプログラム
因みに、そのサイトのプログラムをArduinoでやると
void setup() {
pinMode(13,OUTPUT);
}
void loop() {
word tmp=analogRead(A0)/4;
digitalWrite(13,HIGH);
delay(tmp);
digitalWrite(13,LOW);
delay(tmp);
}
だけです。データシートを見る必要はありません。
初心者はリファレンスのページは見る必要あるでしょうけど。
回路もLEDは13番ピンに付いているので、
ボリュームの配線だけ5V、A0ピン、GNDと繋げば直ぐに書き込んで試せます。
そう言えば、そのサイトの人も、今はArduinoに鞍替えした?のかも知れません。
http://physics.cocolog-nifty.com/weblog/2018/06/arduino14-392d.html
マルチボートも試していたようです。
ついでに、ボリュームに合わせて明るさを変えるスケッチだと更に簡単です。
void setup() {
}
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
3番ピン-LED、A0ピン-ボリュームです。
void setup() {
pinMode(13,OUTPUT);
}
void loop() {
word tmp=analogRead(A0)/4;
digitalWrite(13,HIGH);
delay(tmp);
digitalWrite(13,LOW);
delay(tmp);
}
だけです。データシートを見る必要はありません。
初心者はリファレンスのページは見る必要あるでしょうけど。
回路もLEDは13番ピンに付いているので、
ボリュームの配線だけ5V、A0ピン、GNDと繋げば直ぐに書き込んで試せます。
そう言えば、そのサイトの人も、今はArduinoに鞍替えした?のかも知れません。
http://physics.cocolog-nifty.com/weblog/2018/06/arduino14-392d.html
マルチボートも試していたようです。
ついでに、ボリュームに合わせて明るさを変えるスケッチだと更に簡単です。
void setup() {
}
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
3番ピン-LED、A0ピン-ボリュームです。
Re: Duty比を変えるプログラム
TRさん,猛牛ロックさん,こんにちは。
猛牛ロックさん,いろいろありがとうございます。
私もちょっとだけ,Arduinoいじりましたが,
PICに比べると親切過ぎる感じですね。
それから,
analogRead(A0)/4
ですが,たんに,4で割るのか,それとも上位の4ビットを
とりだすのでしょうか。
この辺の仕様がちょっと分からないかなと思います。
猛牛ロックさん,いろいろありがとうございます。
私もちょっとだけ,Arduinoいじりましたが,
PICに比べると親切過ぎる感じですね。
それから,
analogRead(A0)/4
ですが,たんに,4で割るのか,それとも上位の4ビットを
とりだすのでしょうか。
この辺の仕様がちょっと分からないかなと思います。
Re: Duty比を変えるプログラム
> たんに,4で割るのか,それとも上位の4ビットを
> とりだすのでしょうか。
単に4で割っています。元が10ビットなので、上位8ビットを取り出しています。
※analogWriteは8ビット指定、analogReadは10ビット読出し
> とりだすのでしょうか。
単に4で割っています。元が10ビットなので、上位8ビットを取り出しています。
※analogWriteは8ビット指定、analogReadは10ビット読出し
Re: Duty比を変えるプログラム
みなさんこんばんは。
ADRESですが、
ようやく分かりました。
やはりレジスタとしては存在していないそうです。
でも、
12F1822には、ADRESとすれば、10ビットとして取り扱うことが出来るそうです。
PIC12F1822にはある機能ですが、88や627Aにはなさそうです。
PIC12F1822日本語バージョンのデータシート149頁上の4行目に
ADRES<9:2>と表記されています。
この独特の表記方法からすると、
9から2ビットまでをひとくくりにする、、、←自分的理解ですが。
それから、maboさんのご紹介下さったプログラムですが、
以下のように変えました。
動作しています。
気になった点は、
char str[7]; ← 配列、プログラムの何処にもないので不要。
RB3 = 1;
for(i = 0; i<tmp; i++){};
上の2行です。RB3を{}の中に入れました
後、猛牛ロックさんの言われるように ; も不要ですね。
話は戻りますが、この管理人さんも88を使っていますが、
12F1822にすれば、電圧読み取りの下記プログラム
記
return (ADRESH<<8) + ADRESL;
上記を
return(ADRES); と出来るはずです。
拘る理由
教科書では
return(○○)となっているからです。
本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
変更後
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;};
__delay_ms(2);
}
}
unsigned int adconv()
{
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (ADRESH<<8)+ ADRESL;
}
ADRESですが、
ようやく分かりました。
やはりレジスタとしては存在していないそうです。
でも、
12F1822には、ADRESとすれば、10ビットとして取り扱うことが出来るそうです。
PIC12F1822にはある機能ですが、88や627Aにはなさそうです。
PIC12F1822日本語バージョンのデータシート149頁上の4行目に
ADRES<9:2>と表記されています。
この独特の表記方法からすると、
9から2ビットまでをひとくくりにする、、、←自分的理解ですが。
それから、maboさんのご紹介下さったプログラムですが、
以下のように変えました。
動作しています。
気になった点は、
char str[7]; ← 配列、プログラムの何処にもないので不要。
RB3 = 1;
for(i = 0; i<tmp; i++){};
上の2行です。RB3を{}の中に入れました
後、猛牛ロックさんの言われるように ; も不要ですね。
話は戻りますが、この管理人さんも88を使っていますが、
12F1822にすれば、電圧読み取りの下記プログラム
記
return (ADRESH<<8) + ADRESL;
上記を
return(ADRES); と出来るはずです。
拘る理由
教科書では
return(○○)となっているからです。
本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
変更後
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;};
__delay_ms(2);
}
}
unsigned int adconv()
{
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (ADRESH<<8)+ ADRESL;
}
Re: Duty比を変えるプログラム
10ビットのADRESが「ADRES」で通じるかどうかはコンパイラの仕様です。
ハードとは関係ありません。
※実際にはハードには何の名前もありません。ADRESLやPORTAも単にそのアドレスにあるレジスタの説明上付けた名前です。
その名前で、そのアドレスの内容を制御できるようにコンパイラが関連付けています。
なので、データシートに載っているレジスタ名がそのコンパイラで通じるかさえも実際は判りません。
そのコンパイラが通じる仕様で、提供してくれれば使えばいいですし、
そうでなければ自分で処理する事になります。
勿論、コンパイラの仕様ですから、コンパイラバージョンでも変わるかもしれません。
> ADRES<9:2>
これは、ADRESの第9ビット〜第2ビットまで、と言う事です。
逆に言えば、ADRESの第0、第1ビットは別の所にある、と言う事です。
普通、8ビットしか使わない場合は左詰め(でHIGHしか読まない)、10ビット使う場合は右詰めにします。
> for(i = 0; i<tmp; i++){RB3=1;}
> __delay_ms(2);
> for(i = 0; i<tmp; i++){RB3=0;};
> __delay_ms(2);
> }
これ自体は間違いでは無いです。勿論、元もプログラムでも間違いではありません。
ただ、思っている動作を記述しているのかどうか?です。
普通、この時にやりたいことは
RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
という考えで作りますよね?
※なぜそうするかというと、__delay_ms(tmp*2);と__delay_ms関数では変数を引数に出来ない仕様だからです。
でも今回のだと、
最初のfor文ではtmp回RB3=1が実行されます。(それでfor文終わり)
次に__delay_ms(2);が1回行われます。
次のfor文でtmp回RB3=1が実行されます。(それでfor文終わり)
(取り忘れでしょうけど「;」が1回行われる?)
で、次に__delay_ms(2);が1回行われます。
つまり、時間の増減に__delay_ms(2);は関わっていません。
{}内にRB3=が入っているので、コンパイラに省略される事は無いでしょう。
> 教科書では
> return(○○)となっているからです。
> 本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
return文には()は要りません。つまり、関数で使うmain「()」の意味とは違います。こちらは引数を入れる場所、という役割があります。
return文に付いているのは、単純に計算時に使う()の意味です。必要以上に付けてもエラーではありません。
return (1+2+3)/4;
と書いても、6/4=「1」が返ります。
「unsigned int」 adconv()
と戻り値が負号なし16ビット整数なので、
return (ADRESH<<8)+ ADRESL;
で、ちゃんと16ビットの整数が返ります。
※勿論、変換結果を右詰めした場合です。
後は、その受け入れ先もそれが入るだけの大きさが必要です。
もし入らない大きさなら、あふれ出た桁は消えます。
ハードとは関係ありません。
※実際にはハードには何の名前もありません。ADRESLやPORTAも単にそのアドレスにあるレジスタの説明上付けた名前です。
その名前で、そのアドレスの内容を制御できるようにコンパイラが関連付けています。
なので、データシートに載っているレジスタ名がそのコンパイラで通じるかさえも実際は判りません。
そのコンパイラが通じる仕様で、提供してくれれば使えばいいですし、
そうでなければ自分で処理する事になります。
勿論、コンパイラの仕様ですから、コンパイラバージョンでも変わるかもしれません。
> ADRES<9:2>
これは、ADRESの第9ビット〜第2ビットまで、と言う事です。
逆に言えば、ADRESの第0、第1ビットは別の所にある、と言う事です。
普通、8ビットしか使わない場合は左詰め(でHIGHしか読まない)、10ビット使う場合は右詰めにします。
> for(i = 0; i<tmp; i++){RB3=1;}
> __delay_ms(2);
> for(i = 0; i<tmp; i++){RB3=0;};
> __delay_ms(2);
> }
これ自体は間違いでは無いです。勿論、元もプログラムでも間違いではありません。
ただ、思っている動作を記述しているのかどうか?です。
普通、この時にやりたいことは
RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
という考えで作りますよね?
※なぜそうするかというと、__delay_ms(tmp*2);と__delay_ms関数では変数を引数に出来ない仕様だからです。
でも今回のだと、
最初のfor文ではtmp回RB3=1が実行されます。(それでfor文終わり)
次に__delay_ms(2);が1回行われます。
次のfor文でtmp回RB3=1が実行されます。(それでfor文終わり)
(取り忘れでしょうけど「;」が1回行われる?)
で、次に__delay_ms(2);が1回行われます。
つまり、時間の増減に__delay_ms(2);は関わっていません。
{}内にRB3=が入っているので、コンパイラに省略される事は無いでしょう。
> 教科書では
> return(○○)となっているからです。
> 本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
return文には()は要りません。つまり、関数で使うmain「()」の意味とは違います。こちらは引数を入れる場所、という役割があります。
return文に付いているのは、単純に計算時に使う()の意味です。必要以上に付けてもエラーではありません。
return (1+2+3)/4;
と書いても、6/4=「1」が返ります。
「unsigned int」 adconv()
と戻り値が負号なし16ビット整数なので、
return (ADRESH<<8)+ ADRESL;
で、ちゃんと16ビットの整数が返ります。
※勿論、変換結果を右詰めした場合です。
後は、その受け入れ先もそれが入るだけの大きさが必要です。
もし入らない大きさなら、あふれ出た桁は消えます。
- JoyfulNote v6.02 -
++ Edited by Hamel ++