ラベル C言語 の投稿を表示しています。 すべての投稿を表示
ラベル C言語 の投稿を表示しています。 すべての投稿を表示

2019/08/19

GDグラフィックライブラリ(GD Graphics Library)の使い方

C言語でpng画像を生成したいと思い、その方法を調査したので備忘録として以下に示します。


試験環境


いつもの如くOSはdebianを使用します。


GDグラフィックライブラリとは


- Wikipediaより抜粋

GDグラフィックライブラリ(GD Graphics Library)はトーマス・ボーテル(Thomas Boutell)他によって作られたイメージを動的に操作するライブラリである。本来のプログラミング言語はANSI Cであるが、他の多くのプログラミング言語とのインタフェースが作成されている。GIF、JPEG、PNG、WBMP を生成できる。


インストール方法


debian環境では、以下のコマンドでインストールできます。

# apt update
# apt install libgd-dev

サンプルプログラム


256x256ピクセルのpng画像を生成します。ピクセル毎に着色し、赤成分を横方向に0から255まで、緑成分を縦方向に0から255まで変化させます。


リスト1(main.c)
#include <stdio.h>
#include <gd.h>

int main() {
  gdImagePtr im;
  FILE *pngout;

  int x, y;

  im = gdImageCreateTrueColor(256, 256);

  for (x = 0; x < 256; x++) {
    for (y = 0; y < 256; y++) {
      gdImageSetPixel(im, x, y, (x << 16) + (y << 8));
    }
  }

  pngout = fopen("test.png", "wb");
  gdImagePng(im, pngout);
  fclose(pngout);
  gdImageDestroy(im);

  return 0;
}

GDグラフィックライブラリ関数の仕様はこちらを参照ください。


リスト2 コンパイル&実行
$ cc -o ex1 -lgd -O3 -Wall main.c
$ ./ex1

図1 実行結果(test.png)

2019/08/18

C言語とgnuplotでマンデルブロ集合を表示する(2)

本稿は、以前投稿した「C言語とgnuplotでマンデルブロ集合を表示する」のCソースの一部を複素数型に変更したものです。

gnuplotを使うと結構簡単にマンデルブロ集合を表示することができます。OSはdebianを使用しています。
今回は、複素数型の変数を宣言して計算しています。複素数型の変数を使うとCコードがより簡潔になります。

リスト1 mandelblot.c
/*
  マンデルブロ集合計算プログラム
*/
#include <stdio.h>
#include <math.h>
#include <complex.h>

#define C0r     -0.743  // 計算する場所の中心座標(実数部)
#define C0i      0.1145 // 計算する場所の中心座標(虚数部)
#define VS       0.003  // 計算する場所の中心座標からの範囲(±VS)

#define NMAX     3000   // 計算の繰り返し上限
#define STEP     1600   // 計算する刻み

// "Coloring Dynamical Systems in the Complex Plane"
// http://math.unipa.it/~grim/Jbarrallo.PDF


float mandelbrot(const double complex c) {
  double complex z;
  int n;

  z = c;
  for (n = 0; n < NMAX; ++n) {
    //    if (cabs(z) > 2) { // より簡潔な記法(ただし遅い)
    if (creal(z) * creal(z) + cimag(z) * cimag(z) > 4) {
      return (n - (logf(logf(cabsf(z))) / logf(2))); // 発散
    }
    z = z * z + c;
  }
  return 0; // 計算の繰り返し上限到達
}

int main() {
  double a, b;

  for (a = C0r - VS; a < C0r + VS; a += 2 * VS / STEP) {
    for (b = C0i - VS; b < C0i + VS; b += 2 * VS / STEP) {
      printf("%e %e %e\n", a, b, mandelbrot(a + b * I) / NMAX);
    }
    printf("\n"); // これがないとgnuplotでエラーが出る
  }
  return 0;
}

リスト2 png.gnuplot (gnuplot実行スクリプト)
set term png size 900, 900
set output "mandelbrot.png"
set grid
set pm3d map
set size ratio -1
unset colorbox
unset key
set title 'Mandelbrot set'
set xlabel 'Re'
set ylabel 'Im'
set palette defined (0 "#000000", 1 "#d00000", 5 "#e0e000", 9 "#ffffff")
splot "data.txt"

リスト3 コンパイル&実行
$ cc -o mandelbrot -lm -O4 -Wall mandelbrot.c
$ ./mandelbrot > data.txt
$ gnuplot png.gnuplot

【補足】gnuplotがないと言われる場合は、下記手順でインストールしてください。
# apt update
# apt install gnuplot

図1 実行結果(mandelbrot.png)

2017/11/12

Raspberry PiでAVR開発

 以前にdebianのAVR開発環境を紹介しましたが、Raspberry Piでも同じ手順でAVR開発環境が作れます。

■Raspberry Pi(Raspbian)でAVR開発環境を構築する手順

# apt update
# apt upgrade
# apt install gcc-avr binutils-avr avr-libc avrdude
 Raspberry PiでAVR開発するメリットは、「ソース修正→ISP書き込み」が気軽にどこでもできるということに尽きます。Raspberry Piに移行する前はノートPCのAVR開発環境にAVRISPmkIIを接続してデバッグしていたのですが、この場合、開発対象とノートPCをAVRISPmkIIのケーブルが届く範囲に留める必要があり、結局のところ、開発対象の近くにノートPCを持っていくしかありませんでした。
 Raspberry PiにAVR開発環境を置く場合、ノートPCは作業机に、Raspberry Piは開発対象の近くに設置し、ノートPCとRaspberry Pi間はイーサネット(無線LANでもOK)で接続し、SSHでRaspberry Piにログインして作業することができます。

2016/03/20

Raspberry Pi2で円周率の計算をやってみた

今度はRaspberry Pi2で計算してみます。(前回はここ

(1)実行環境


 マシン:Raspberry Pi2
 O S:Raspbian

(2)円周率計算プログラムについて


前回と同じく、GMPの「Compute billions of digits of π using GMP!」にあるgmp-chudnovsky.cを使用させて頂きました。

(3)GMPライブラリと円周率計算プログラムのダウンロード、コンパイル


作業フォルダ"pi"を作成し、そこにGMPライブラリと円周率計算プログラムをダウンロード、コンパイルします。

$ mkdir pi
$ cd pi
$ wget https://gmplib.org/download/gmp/gmp-6.1.0.tar.xz
$ tar xvf gmp-6.1.0.tar.xz
$ cd gmp-6.1.0/
$ ./configure
$ make -j4
$ make -j4 check
$ cd ..
$ wget https://gmplib.org/download/misc/gmp-chudnovsky.c
$ cc -Wall -static -O2 -o gmp-chudnovsky gmp-chudnovsky.c -Igmp-6.1.0 -Lgmp-6.1.0/.libs -lgmp -lm

(4)円周率計算プログラムの実行

・円周率1,000桁

円周率表示ありで0.01秒以下でした。

$ time -p ./gmp-chudnovsky 1000 1
#terms=70, depth=8
sieve   time =  0.000
...................................................

bs      time =  0.000
   gcd  time =  0.000
div     time =  0.000
sqrt    time =  0.000
mul     time =  0.000
total   time =  0.000
   P size=1334 digits (1.334000)
   Q size=1327 digits (1.327000)
pi(0,70)=
0.314159265358979 …省略
real 0.00
user 0.00
sys 0.01

・円周率100万桁

円周率表示なしの計算のみで6.15秒でした。

$ time -p ./gmp-chudnovsky 1000000
#terms=70513, depth=18
sieve   time =  0.070
..................................................

bs      time =  4.480
   gcd  time =  0.000
div     time =  0.790
sqrt    time =  0.420
mul     time =  0.300
total   time =  6.060
   P size=1455608 digits (1.455608)
   Q size=1455601 digits (1.455601)
real 6.15
user 6.06
sys 0.09

(5)pi2とB+の比較


    【条件】   
  • 円周率表示なし   
  • gmp-6.1.0   
  • 円周率プログラム https://gmplib.org/download/misc/gmp-chudnovsky.c   
  • コンパイルオプション $ cc -Wall -static -O2 -o gmp-chudnovsky gmp-chudnovsky.c -Igmp-6.1.0 -Lgmp-6.1.0/.libs -lgmp -lm
桁   Pi2  B+
=======
1e3  0.01 0.02
1e4  0.03 0.05
1e5  0.36 0.70
1e6  6.14 13.25
1e7 105.93 235.95
1e8 1,721.64 -(GNU MP: Cannot allocate memory)
・Pi2はB+に比べ2倍程度早いです。
・B+はメモリ不足で1億桁の計算ができませんでした。

2015/04/11

Raspberry Piで円周率の計算をやってみた

ラズベリー・パイで円周率πの計算(100万桁)をしてみました。

(1)実行環境について


マシン:Raspberry Pi B+
O S:RASPBIAN(2015-02-16版)

(2)円周率計算プログラムについて


GMPの「Compute billions of digits of π using GMP!」にあるgmp-chudnovsky.cを使用させて頂きました。

(3)GMPライブラリと円周率計算プログラムのダウンロード、コンパイル


作業フォルダ"pi"を作成し、そこにGMPライブラリと円周率計算プログラムをダウンロード、コンパイルします。

$ mkdir pi
$ cd pi
$ wget https://ftp.gnu.org/gnu/gmp/gmp-6.0.0a.tar.xz
$ tar xvf gmp-6.0.0a.tar.xz
$ cd gmp-6.0.0/
$ ./configure
$ make -j4
$ make -j4 check
$ cd ..
$ wget https://gmplib.org/download/misc/gmp-chudnovsky.c
$ cc -Wall -static -o gmp-chudnovsky gmp-chudnovsky.c -Igmp-6.0.0 -Lgmp-6.0.0/.libs -lgmp -lm
raspbianデフォルトの状態では"m4"が入っていないためconfigureがエラー終了します。その場合は"sudo apt-get install m4"でm4をインストールします。その後、もう一度./configureを実行します。

GMPのコンパイルは小一時間かかります。(Raspberry Pi B+の場合)

(4)円周率計算プログラムの実行


・円周率1,000桁

円周率表示ありで0.03秒でした。

$ time -p ./gmp-chudnovsky 1000 1
#terms=70, depth=8
sieve   time =  0.000
...................................................

bs      time =  0.010
   gcd  time =  0.000
div     time =  0.000
sqrt    time =  0.000
mul     time =  0.000
total   time =  0.010
   P size=1334 digits (1.334000)
   Q size=1327 digits (1.327000)
pi(0,70)=
0.314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019894e1
real 0.03
user 0.01
sys 0.00
・円周率100万桁
円周率表示なしの計算のみで14.74秒でした。

$ time -p ./gmp-chudnovsky 1000000
#terms=70513, depth=18
sieve   time =  0.150
..................................................

bs      time = 10.910
   gcd  time =  0.000
div     time =  1.800
sqrt    time =  0.970
mul     time =  0.730
total   time = 14.580
   P size=1455608 digits (1.455608)
   Q size=1455601 digits (1.455601)
real 14.74
user 14.58
sys 0.09

2014/10/25

JJYシミュレータの試作(AVRマイコン)

 すこし前に購入した電波時計モジュール(40KHz) [MAS6180-40KHZ]を使って電波時計を作ってみることにしました。電波時計モジュールから出力されるパルス信号をAVRマイコンでデコードして7segLEDかLCDに表示するつもりです。
 ところが実際にデバッグを始めたところ困ったことが発生。電波時計モジュールから出力されるパルス信号がノイズだらけでデコードできないようなのです。電波時計モジュールとAVRマイコンを窓際に持っていくと正しいパルスが出てくるのですが、マイコンデバッグ用PCに接続するためにPCが設置されている場所まで持っていくと出力パルスが怪しくなります。原因として考えられるのは以下の2点です。

 1)電波が弱いため窓際でないと正しく受信できない。
 2)PCが出すノイズに邪魔される。

 PCの電源を落とすとだいぶ改善されるので2)の要因が大きいのだと思います。

 PCなしではマイコンのデバッグができないので、電波時計モジュールのパルス出力を疑似出力する「JJYシミュレータ」なるツールをAVRマイコンで試作することにしました。

構成


  • いつものAVRマイコン(ATmega168/328マイコンボードキット)を使用します。
  • ATmega168/328マイコンボードキットのPB5ピンにはLEDが実装されているので、このピンをパルス出力ピンにします。
  • パルス出力のフォーマットはNICTの長波JJY送信方法を参考にしました。
  • 時刻情報はタイマ1で生成した10Hzから生成します。
  • 時刻合わせ機能はありません(プログラム上で初期値を定義し、常に初期値からスタート)
  • タイマ0で40kHzを生成し、PD6(OC0A)ピンに出力します。このときパルス出力で送信OFF/ONを制御します。
  • パルス出力の状況はシリアル(RS232C)に出力します。
今回はパルス出力だけで良かったのですが、遊びで40kHz出力もつけてみました。

使ってみて


 まだ本来の目的(パルス出力を使って電波時計のデバッグ)は試していません。
 替わりに40kHz出力を試してみたところ見事に動きました。PD6ピンに15cmぐらいの線材を接続し市販電波時計に近づけてみたところ同期してくれました。10cmぐらい離しても同期します。
 本物のJJYの電波形式(A1B)とはちょっと違う40kHzの矩形波のON/OFFですがうまく動きました。
 頭では分かっているけど、実際に動いている所をみると電波は面白いというか不思議ですね。

シリアル出力例


<<< JJY Simulator >>>
 Copyright (c) 2014 Being. Ver0.1

00M 2016/02/29 23:14
M00100101M001000011M000000110M000000110M_________M000000000M 2016/02/29 23:15
M00100110M001000011M000000110M000000110M000010110M001000000M 2016/02/29 23:16
M00100111M001000011M000000110M000000100M000010110M001000000M 2016/02/29 23:17
M00101000M001000011M000000110M000000100M000010110M001000000M 2016/02/29 23:18
M00101001M001000011M000000110M000000110M000010110M001000000M 2016/02/29 23:19
M01000000M001000011M000000110M000000110M000010110M001000000M 2016/02/29 23:20

2014/05/25

Deep Mandelbrotの紹介

Deep Mandelbrot」というソフトを紹介します。

■ソフトの目的


マンデルブロ集合を表示するソフトです。

■特徴


  • マルチスレッド対応
  • libgmpを用いた任意精度計算
  • PNG形式の画像を出力
  • 画像品質を向上させるオーバサンプリング
  • 簡単な動画作成機能(*1)
 *1: 私はまだ試したことがありません。

■入手先


 ページの最後のほうにある"Download"から"mandelbrot-1.0.tar.gz"を入手ずる。

■コンパイル方法


 基本的には"mandelbrot-1.0.tar.gz"の中に入っている"INSTAL"ファイルに記載されている手順を実行すればOKです。私はいつものごとくdebian/wheezy上でコンパイルしました。

(1) ライブラリの準備
 コンパイルには以下のライブラリが必要とのこと。

  • libgd2-xpm-dev
  • libgmp3-dev
 自分のdebianに入っていない場合は以下のコマンドでインストールします。
# apt-get install libgd2-xpm-dev
# apt-get install libgmp3-dev

(2) makeする
 makeが成功すると./docの下に"mandelbrot"という実行ファイルが出来ます。

# cmake .      (cmakeがない場合はapt-get install cmakeを実行)
# make
# make install (必要な場合)

■使い方


 引数"-?"を付けてコマンドを実行すると使用方法が表示されます。
$ mandelbrot -?
Deep Mandelbrot v1.0, (C)2008-2010 by Herbert Haas and Tim Horton
 Started: Sat May 24 19:46:25 2014
Deep Mandelbrot v1.0, (C)2008-2011 by Herbert Haas and Tim Horton

USAGE: mandelbrot [OPTIONS] 

 -?                 print this help
 -v                 be verbose during execution
 -V                 be even more verbose during execution
 -d                 create distance data file for further analysis
 -H                 create histogram file for further analysis
 -g                 create a PNG version with a white grid
 -i        maximum iteration depth (1-999999) [default: 200]
 -t    number of threads to be used (1-30) [default: 4]
 -o        filename for computed image (no file extension needed)
 -f         create PNG with data from this file (no computation)
                    (e.g. the result of a distance file (-d option) can be used)
 -s   amount of oversampling (1-8) [default: 2]
 -w          image pixel width (10-10000) [default: 500]
 -h         image pixel height (10-10000) [default: 500]
 -c     specify color mode (0-255) [default: 0] or 'auto'
 -m      color of Mandelbrot set (if not black) as six hexadecimal
                    digits, e. g. ffa500.

 -M          create movie using  frames
 -x       target of movie on x-axis in percent of current image
 -y       target of movie on y-axis in percent of current image
                 NOTE: valid x/y-parameters: {-100..+100}, with respect to the
                 center of the current frame {0,0}
 -r         radius of last movie frame in percent of first frame

 NOTE:
   Command line parameters overwrite the correspondig settings in .
   If only -w OR only -h is given, then the other parameter is assigned the same value.

■使用例


(1) デフォルト
 引数を何もつけずに実行するとマンデルブロ集合の全体図を出力します。

$ mandelbrot
$ ls
mandelbrot.png
$
図1 mandelbrot.png

(2) コンフィグファイルを指定して実行
 コンフィグファイルのサンプルが./doc/input_files/にあります。

$ ls doc/input_files/
README  neck  satellite  seahorse
$
リスト1 seahorse (コンフィグファイル)
# seahorse
centerx = -0.7435669
centery = 0.1314023
radius = 0.0011439
maxiter = 500
  • 表示したい位置の中心座標(centerx,cenery)
  • 半径(radius)
  • 計算の繰り返し上限値(maxiter)
$ mandelbrot seahorse
$ ls
seahorse.png
図2 seahorse.png

(3) コンフィグファイルと引数の組み合わせ例

$ namdelbrot -g seahorse       // PNG画像に座標目盛を付けます
$ namdelbrot -w 2000 seahorse  // PNG画像を2000x2000ピクセルで出力します
$ namdelbrot -i 3000 seahorse  // 計算の繰り返し上限値を3000にします(*2)
$ namdelbrot -s 3 seahorse     // オーバサンプリング値を3にします
 *2 : コンフィグファイルのmaxiter値と競合しますが、引数の値が優先されます

2014/04/12

PWM制御でLED点滅

 今度はLED点滅にPWM(Pulse Width Modulation)を使ってみます。

ゴール


  • LEDの点滅周期は約2秒(点灯1秒、消灯1秒)
  • PWM制御で「ふわっと点灯」、「ふわっと消灯」を実現する

ふわっと点灯、ふわっと消灯


 「ふわっと」を実現するため、LEDの明るさを制御します。LED輝度制御方法として今回はPWMを使います。PWMは変調方式の一つで、パルス波のDuty比(点灯と消灯の比率)を変化させることにより変調を行う方式です。簡単に言うと、
  • LEDを超高速点滅させる
  • 点滅時のDuty比を変えることにより明るさを調節する
となります。
 「超高速点滅」とは、例えば1秒間に1,000回点滅のように点滅してることが人間に認識できないほど速くします。
 「Duty比を変えることにより明るさを調節」というのは、例えばDuty比=90%(点灯90%,消灯10%)とDuty比=10%(点灯10%,消灯10%)ではDuty比=90%のほうが明るく見えるということです。

AVRでのPWM制御について


 AVRはタイマ/カウンタをPWMモードにすることにより、PWM制御を行うことができます。8bitタイマと16bitタイマがありますが、Duty比の指定は8bit(=256段階)あれば十分なので8bitタイマであるTimer/Counter0を使います。タイマの動作モードはFast PWM Mode (WGM2:0=3)を使います。このモードはパルス波周期が256 timer clock固定で、Duty比はOCR0Aレジスタで指定できます。Timer clockを1MHzにすると、LEDの点滅回数は1秒間に3,900回ぐらいになります。タイマで作ったPWM信号はOC0A(PD6ピン)に出力されます。

LEDとPD6ピンの接続


 参考文献2を見るとマイコンのPB5端子の先にLEDが接続されています。PD6端子でLEDを制御するため、PD6端子とPB5端子を接続する必要があります。どちらの端子もマイコンボードのコネクタに接続されているので、コネクタ上で接続するのが簡単です。
図1 コネクタ上の接続(PB5端子とPD6端子)

 なお、PD6端子とPB5端子がショートしないようにプログラム上でPB5端子をInputに設定しておきます。

プログラム


 例によってdebian環境(avr-gcc+avr-libc)で作成しています。

リスト1 main.c

/*
 * LED点滅プログラム(PWM制御)
 *
 * For AE-ATmega borad
 *
 * Ver0.1 2013/04/12
 */

/*
 * AE-ATmega boardはPORTB bit5にLEDが接続されている。
 * このLEDをPWM制御で点灯させるため、タイマ出力であるPORTD Bit6を
 * LEDに接続する。(ショートしないようにPORTB bit5は入力モードに
 * しておく)
 */

#include <avr/io.h>
#include <util/delay.h>

int main() {
  unsigned char i = 0;

  DDRB = 0x00;          // PORTB bit5を入力モードにする
  DDRD = _BV(DD6);      // PORTD bit6を出力モードにする

  /* Set 8-bit Timer Register */
  TCCR0A = _BV(COM0A1) | _BV(WGM01) | _BV(WGM00);
  TCCR0B = _BV(CS00);

  /* タイマ設定
   * clkI/O      : 1MHz (= 内蔵OSC 8MHz + CLKDIV8)
   * プリスケーラ: なし
   * モード      : Mode3 (高速PWM/0xFF)
   * OC0A端子    : 非反転モードで使用
   */

  while(1) {
    while(i<0xFF) {     // 2mSec間隔で輝度を上げていく
      OCR0A = ++i;
      _delay_ms(2);
    }
    _delay_ms(1000);    // 最高輝度で1秒保持

    while(i>0x00) {     // 2mSec間隔で輝度を下げていく
      OCR0A = --i;
      _delay_ms(2);
    }
    _delay_ms(1000);    // 最低輝度で1秒保持
  }
}

動画1 点滅の様子

参考文献


  1. Atmel ATmega48P/V 88P/V 168P/V datasheet
  2. ATmega168/328マイコンボード回路図

2014/03/30

タイマ割込みでLED点滅(AVR)

 前回の記事はLED点滅タイミングに_delay_ms()関数を使いましたが、今回はAVR内蔵タイマを使ってみたいと思います。ついでに外部クロックも使ってみたいと思います。

ゴール


     
  • LEDの点滅周期は1秒(点灯0.1秒、消灯0.9秒)
  • 点灯、および消灯のタイミングをタイマ割込みで行う  
  • 外部クロック(20MHz X'tal)を使う

タイマ割込みについて


 ATmega168は8bitタイマと16bitタイマを持っていますが、秒単位の長い時間を測るには8bitでは足りないので16bitタイマを使うことにします。タイマをピンに出力し直接LEDを制御しても良いのですが、ここではタイマ割込みを使います。
 タイマ/カウンタのブロック図が参考文献1のFigure16-1(16-bit Timer/Counter block Diagram)にあります。眺めてみると、タイマ値TCNTnと比較レジスタOCRnAが一致した時に割込みを発生させることができるようです。同様にOCRnBとの一致でも割込みを発生できるようです。タイマのクリアは"Control Logic"で行うようですが、この"Control Logic"に"TOP"という入力があり、OCRnAと一致した時に発生できるようです。
 TOPになるとタイマ値をクリアできれば指定した時間周期での割込みが発生できそうです。このような使い方はタイマをMode=4(CTC/OCR1A)にするとできるようです。このモードはOCR1Aレジスタに設定した値になるとカウンタが0に戻ります。(詳細は参考文献1の16.9.2章を参照のこと)
 これで2種類のタイマ割込みが用意できました。OCR1Aを1秒にOCR1Bを0.1秒に設定すればそれぞれの割込みでLED点灯、LED消灯ができます。
 ではOCR1A/OCR1Bにどんな値を設定すれば良いでしょうか? それにはタイマの1カウントがどのくらいの時間かを知る必要があります。タイマの1カウントは1クロック分なのでタイマに入力するクロックを調べる必要があります。

タイマに入力するクロックについて


 前述のタイマ/カウンタのブロック図を引き続き眺めてみます。"Control Logic"の入力に"clkTn"があります。ここがタイマに入力するクロックのようです。外部ピン入力とプリスケーラ入力から選択できるようです。ここではプリスケーラ入力を選びます。プリスケーラは参考文献1のTable 16-5(Clock Select Bit Description)によるとclkI/Oを1/1024まで分周できるようです。clkI/Oは参考文献1のFigure9-1(Clock Distribution)によると"AVR Clock Control Unit"の出力です。
 "AVR Clock Control Unit"の入力であるSystem ClockはFuseビットで設定します。Fuseビットの設定はAVRISPmkIIで書き込みをする時に設定します。Fuse設定は以下の3点を変更しました。
  • BODLVEL=4V3
  • SUT_CKSEL=EXTFSXTAL_1KCK_14CK_0MS
  • CKDIV8にチェックを入れる
 ここだけはAtmel Studioを使って書き込みをしています。この設定により外部クロック(20MHz X'tal)を使い、内部で1/8(つまり2.5MHz)にしてからタイマにclkI/Oが供給されます。
 プリスケーラの設定(参考文献1のTable16-5)を幾つにするかですが1/64にしました。これでタイマのクロックは39.0625KHz(=2.5MHz/64)になります。1秒タイマにするにはOCR1A=0x9897(=39,063)にします。

LEDについて


 LEDは前回と同じくPORT Bのbit5に接続されているLEDを使います。

プログラムについて


 例によってdebian環境(avr-gcc+avr-libc)で作成しています。

リスト1 main.c

/*
 * LED点滅プログラム(タイマ割込み使用)
 *
 * For AE-ATmega board
 *
 * Ver. 1.0 2014/03/30
 */
#include <avr/io.h>
#include <avr/interrupt.h>

void init_timer1(void) {

  /*
   * タイマ設定
   * 外部クロック   : 20MHz (X'tal)
   * CKDIV8=1       : 20MHz / 8 = 2.5MHz
   * プリスケーラ   : 2.5MHz / 64 = 39,062.5 Hz
   * 比較レジスタ1A : 39,063 ( 39,063/ 39,062.5 = 1.000013 Hz)
   * 比較レジスタ1B :  3,906
   * 波形発生モード : CTC(OCR1A)
   */

  /*
   * 比較レジスタ1Aで1秒毎に割込みを発生させる。→LED ON
   * 比較レジスタ1Bは1A割込み発生の0.1秒後に割込みが発生。→LED OFF
   */

  TCCR1A = 0x00;
  TCCR1B = _BV(WGM12) | _BV(CS11) | _BV(CS10);
  TCCR1C = 0x00;
  OCR1AH = 0x98;        // 16bitレジスタなので上位バイトを先にライトする
  OCR1AL = 0x97;        // OCR1A=39,063=0x9897
  OCR1BH = 0x0F;        // OCR1B=3,906=-0x0F42
  OCR1BL = 0x42;
  TIMSK1 = ( _BV(OCIE1B) | _BV(OCIE1A) );
  sei();                // Enable all interrupts
}

ISR(TIMER1_COMPA_vect) {
  PORTB |= _BV(PB5);    // LED ON
}

ISR(TIMER1_COMPB_vect) {
  PORTB &= !(_BV(PB5)); // LED OFF
}

int main(void) {

  DDRB  = _BV(DDB5);    // PORTB bit5を出力に設定

  init_timer1();        // タイマ初期化
  while(1);             // 無限ループ(割込み待ち)
}

参考文献


  1. Atmel ATmega48P/V 88P/V 168P/V datasheet
  2. avr libcのinterrupt.h解説

2014/03/22

AVR開発環境インストールからLED点滅まで(AVRマイコンボード)

 AVRの開発環境として通常は"Atmel Studio"を使うと思いますが、ここではdebianで使える物を選択してみました。Linux用のCコンパイラを探してみたところ、avr-gcc + avr-libcが使えるようです。debinの場合、以下のコマンドでインストールできます。
# apt-get install gcc-avr
# apt-get install binutils-avr
# apt-get install avr-libc
# apt-get install avrdude

LED点滅プログラムについて


 ATmega168/328マイコンボード回路図を見るとマイコンのPB5端子の先にLEDが接続されています。PB5にHを出力すればLEDが点灯します。このLEDを点滅するプログラムを書いてみます。

リスト1 blink_LED.c

/*
 * LED 点滅プログラム
 *
 *  K-04590 (ATMEGA168/328用マイコンボード)
 *
 */
#include <avr/io.h>
#include <util/delay.h>

#define PB5_LED   5             // H: LED ON | L: LED OFF

int main(void) {

  DDRB   = _BV(PB5_LED);        // Port B bit 5の方向を出力に設定
  PORTB |= _BV(PB5_LED);        // Port B bit 5にHを出力(LED点灯)

  while (1) {
    _delay_ms(500);             // 0.5秒待つ
    PORTB ^= _BV(PB5_LED);      // Port B bit5を0/1反転
  }
}
 0.5秒毎にLED点灯、LED消灯を繰り返すプログラムです。_delay_ms()や_BV()はavr-libcで定義されています。詳細は"AVR Libc Home Page"に説明があります。

F_CPUについて


 _delay_ms()を使うには事前にマクロ"F_CPU"を定義しておく必要があります。F_CPUにはCPUのクロック周波数を設定します。ソースコードの中に書いても良いのですが、ここではMakefileの中に定義しました。マイコンのクロック周波数は1MHzです。これは工場出荷時はクロックソースとしてマイコン内蔵オシレータ(8MHz)が選択されており、さらにCKDIV8オプションが設定されているため、1MHz(=8MHz÷8)になっているためです。したがってF_CPU=1000000を設定します。

リスト2 Makefile

PROG=blink_LED
OBJS=${PROG}.o
MCU=atmega168p
F_CPU=1000000UL # internal 8MHz clock & CKDIV8 fuse bit enabled
#--------

ELF=$(PROG)
IHEX=$(PROG).ihex

CC=avr-gcc
CFLAGS=-g2 -O1 -mmcu=$(MCU) -DF_CPU=$(F_CPU) -Wall
LDFLAGS=-g2 -mmcu=$(MCU)
OBJCOPY=avr-objcopy

all: $(IHEX)

$(IHEX): $(ELF)

$(ELF): $(OBJS)

%.ihex: %
        $(OBJCOPY) -j .text -j .data -O ihex $< $@

clean:
        -$(RM) $(ELF) $(IHEX) $(OBJS)

flash:
        sudo avrdude -c avrispmkII -P usb -p atmega168p -U flash:w:$(IHEX)

.PHONY: all clean
 Makefileを書くのは正直苦手で適当に書いています。(すみません)

リスト3 コンパイル&マイコン書き込み

$ make
avr-gcc -g2 -O1 -mmcu=atmega168p -DF_CPU=1000000UL  -Wall   -c -o blink_LED.o blink_LED.c
avr-gcc -g2 -mmcu=atmega168p  blink_LED.o   -o blink_LED
avr-objcopy -j .text -j .data -O ihex blink_LED blink_LED.ihex

$ make flash
sudo avrdude -c avrispmkII -P usb -p atmega168p -U flash:w:blink_LED.ihex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e940b
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "blink_LED.ihex"
avrdude: input file blink_LED.ihex auto detected as Intel Hex
avrdude: writing flash (166 bytes):

Writing | ################################################## | 100% 0.05s

avrdude: 166 bytes of flash written
avrdude: verifying flash memory against blink_LED.ihex:
avrdude: load data flash data from input file blink_LED.ihex:
avrdude: input file blink_LED.ihex auto detected as Intel Hex
avrdude: input file blink_LED.ihex contains 166 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.05s

avrdude: verifying ...
avrdude: 166 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.

$
 debianマシンにAVRISPmkII経由でAVRマイコンボードを接続し、"make flash"と入力すればマイコンにプログラムが書き込まれます。

2013/10/19

C言語とgnuplotでマンデルブロ集合を表示する

gnuplotを使うと結構簡単にマンデルブロ集合を表示することができます。OSはdebianを使用しています。

リスト1 mandelblot.c
/*
  マンデルブロ集合計算プログラム

 */
#include <stdio.h>
#include <math.h>

#define C0r     -0.743  // 計算する場所の中心座標(実数部)
#define C0i      0.1145 // 計算する場所の中心座標(虚数部)
#define VS       0.003  // 計算する場所の中心座標からの範囲(±VS)

#define NMAX     20000  // 計算の繰り返し上限
#define STEP     800.0  // 計算する刻み


double mandelbrot(double a, double b){
  double x = 0.0;
  double y = 0.0;
  double x1, y1;

  int n;

  for (n = 1; n <= NMAX; n++) {
    x1 = x * x - y * y + a;
    y1 = 2.0 * x * y + b;
    if ( x1 * x1 + y1 * y1 > 4.0) return log(n); // 発散
    x = x1;
    y = y1;
  }
  return 0; // 計算の繰り返し上限到達
}


int main() {
  double a, b;

  for (a = C0r-VS; a < C0r+VS; a += 2.0*VS/STEP) {
    for (b = C0i-VS; b < C0i+VS; b += 2.0*VS/STEP) {
      printf("%1.14e %1.14e %1.14e\n", a, b, mandelbrot(a, b));
    }
    printf("\n"); // これがないとgnuplotでエラーが出る
  }
  return 0;
}

リスト2 png.gnuplot (gnuplot実行スクリプト)
set term png size 900, 900
set output "mandelbrot.png"
set grid
set pm3d map
set size square
set palette defined (0 "#000000", 2 "#c00000", 7 "#ffff00", 9 "#ffffff")
splot "data.txt"

リスト3 コンパイル&実行
# cc -o mandelbrot -lm -O4 -Wall mandelbrot.c
# ./mandelbrot > data.txt
# gnuplot png.gnuplot

図1 実行結果(mandelbrot.png)
  • 計算する場所はリスト1のdefine文の数値を変えることにより可能です。
  • 色の変化を分かりやすくするため、Logスケールで表示しています。
C言語のほかにPythonとR言語で記述した記事もあります。詳しくは マンデルブロ集合 をご覧ください。

バーニングシップフラクタル

bsf.knit 今回は『バーニングシップフラクタル』についての備忘録です。 バーニングシップフラクタルとは? Wikipedia ...