(これ以下のプログラムはMPLAB(R) XC8 C Compile V1.00コンパイラ仕様にて記述しています)
#include <xc.h>
#include <stdlib.h>
main()
{
int d ;
d = rand() ; // この行を実行する度に乱数が発生する
}
ですがぁ.....
#include <stdlib.h>
static long randx;
static char randf;
void srand(unsigned x)
{
randx = x;
randf = 1;
}
rand(void)
{
if(!randf) srand(1); // ここが問題の行です
return((int)((randx = randx*1103515245L + 12345) >> 16) & 077777);
}
ってなっています。
rand(void)
{
if(!randf) srand(ここにアナログ値をセット);
return((int)((randx = randx*1103515245L + 12345) >> 16) & 077777);
}
こんな感じです、接続されていないアナログ端子の電圧は不定(ノイズ利用)なので丁度よさげですね。if(!randf) srand(analogRead(0)) ; // アナログ0番ピンより読込むに書き換えて2回再実行させた結果が下の図です。

rand(int min,int max)
{
int ans ;
if(!randf) srand(1);
ans = (int)(((randx = randx*1103515245L + 12345) >> 16) & 077777) ;
return min + ans % (max - min + 1) ;
}
って書き換えてやればOKです。
d = rand(1,10) ; // この行を実行する度に乱数が発生する
と行えば1〜10までの範囲で乱数が発生するわけです。
[疑似乱数発生関数:random] *1)
(以下をコピー&ペーストしてファイル名random.cで保存しましょう)
----random.c-----------------------------------------------------
#include <xc.h>
#include "random.h"
static long randomx ;
// 疑似乱数の初期値を設定する処理
void randomSeed(long val)
{
unsigned int temp;
if (val == 0) {
while(1) {
GO_nDONE = 1 ; // PICにアナログ値読取り開始を指示
while(GO_nDONE) ; // PICが読取り完了するまで待つ
temp = ( ADRESH << 8 ) | ADRESL ; // 10ビットの分解能力です
if (temp > 0) break ;
}
randomx = temp ;
} else randomx = val ; // 指定数値をそのまま初期値とする
}
// 疑似乱数を発生させる処理
long random(long min,long max)
{
long d ;
d = ((randomx = randomx*1103515245L + 12345) >> 16) & 077777 ;
return min + d % (max - min + 1) ;
}
-----------------------------------------------------------------
(以下をコピー&ペーストしてファイル名random.hで保存しましょう) ----random.h----------------------------------------------------- #ifndef _RANDOM_H_ #define _RANDOM_H_ void randomSeed(long val) ; long random(long min,long max) ; #endif -----------------------------------------------------------------下記サンプルプログラムはPIC16F1827での使用例です。
-----------------------------------------------------------------
#include <xc.h>
#include "random.h"
#define _XTAL_FREQ 8000000 // delay用に必要(クロック8MHzを指定)
// コンフィギュレーション1の設定
// CLKOUTピンをRA6ピンで使用する(CLKOUTEN_OFF):内部クロックを使用する(FOSC_INTOSC)
// 外部クロック監視しない(FCMEN_OFF):外部・内部クロックの切替えでの起動はなし(IESO_OFF)
// 電源電圧降下常時監視機能ON(BOREN_ON):電源ONから64ms後にプログラムを開始する(PWRTEN_ON)
// ウオッチドッグタイマー無し(WDTE_OFF):
// 外部リセット信号は使用せずにデジタル入力(RA5)ピンとする(MCLRE_OFF)
// プログラムメモリーを保護しない(CP_OFF):データメモリーを保護しない(CPD_OFF)
__CONFIG(CLKOUTEN_OFF & FOSC_INTOSC & FCMEN_OFF & IESO_OFF & BOREN_ON &
PWRTE_ON & WDTE_OFF & MCLRE_OFF & CP_OFF & CPD_OFF) ;
// コンフィギュレーション2の設定
// 動作クロックを32MHzでは動作させない(PLLEN_OFF)
// スタックがオーバフローやアンダーフローしたらリセットをする(STVREN_ON)
// 低電圧プログラミング機能使用しない(LVP_OFF)
// Flashメモリーを保護しない(WRT_OFF):電源電圧降下常時監視電圧(2.5V)設定(BORV_HI)
__CONFIG(PLLEN_OFF & STVREN_ON & WRT_OFF & BORV_HI & LVP_OFF) ;
// メインの処理
void main()
{
long ans ;
OSCCON = 0b01110010 ; // 内部クロックは8MHzとする
ANSELA = 0b00000000 ; // AN0-AN4は使用しない全てデジタルI/Oとする
ANSELB = 0b00000000 ; // AN5-AN11は使用しない全てデジタルI/Oとする
TRISA = 0b00000000 ; // ピン(RA)は全て出力に割当てる(RA5は入力のみとなる)
TRISB = 0b00000000 ; // ピン(RB)は全て出力に割当てる
PORTA = 0b00000000 ; // RA出力ピンの初期化(全てLOWにする)
PORTB = 0b00000000 ; // RB出力ピンの初期化(全てLOWにする)
// A/Dの設定(疑似乱数の初期値として利用する)
ADCON1 = 0b10010000 ; // 読取値は右寄せ、A/D変換クロックはFOSC/8、VDDをリファレンスに
ADCON0 = 0b00000101 ; // アナログ変換情報設定(AN1から読込む)
__delay_us(5) ; // アナログ変換情報が設定されるまでとりあえず待つ
// PWM(CCP3)の設定(LEDの点灯制御に利用する)
CCPTMRS = 0b00010000 ; // CCP3機能はTimer4を使用する
CCP3CON = 0b00001100 ; // PWM機能(モジュール)を使用する
T4CON = 0b00000010 ; // TMR4プリスケーラ値を16倍に設定
CCPR3L = 0 ; // デューティ値は0で初期化
CCPR3H = 0 ;
TMR4 = 0 ; // タイマー4カウンターを初期化
PR4 = 124 ; // PWMの周期を設定(1000Hzで設定)
TMR4ON = 1 ; // TMR4(PWM)スタート
randomSeed(0) ; // 乱数の初期値はアナログAN1を読みだして設定する
// 0以外の数値ならその数値を初期値として設定する
while(1) {
ans = random(1,255) ;// 1〜255の範囲内で乱数を発生させる
CCPR3L = (char)ans ; // 乱数値でPWMのデュティ比を可変させる
__delay_ms(500) ; // 0.5秒後に繰り返し
}
}
-----------------------------------------------------------------
乱数の初期値をアナログから読み込む場合は、レジスタANSELx/TRISx/ADCON0でrandomSeed関数の変更(*1) 2016/04/14
【きむ茶工房ガレージハウス】
Copyright (C) 2006-2016 Shigehiro Kimura All Rights Reserved.