Arduinoの良いところは、たくさんのライブラリが公開されており、ハードの面倒な仕様が隠蔽されているところだが、 DAC等を高速に駆動したい場合などはポートを直接いじる必要がある。MCP4922はMicrochipのSPI 12bit DACで、 Settling Timeが4.5µsと比較的高速で、1個数百円程度で入手可能である。ArduinoにはSPIライブラリがあるので それを使用して駆動できるが、この場合はせいぜい数10kHz程度でしか駆動できない。
8(LDAC)、10(SS)、11(MOSI)、12(MISO)、13(SCK)
#include <SPI.h>
const int PIN_CS = 8;
const int GAIN_1 = 0x1;
const int GAIN_2 = 0x0;
#define MCP4922_DAC_A 0x00
#define MCP4922_DAC_B 0x80
#define MCP4922_VREF_BUF 0x40
#define MCP4922_GAIN_1X 0x20
#define MCP4922_GAIN_2X 0x00
#define MCP4922_SHDN 0x10
void setup()
{
pinMode(PIN_CS, OUTPUT);
SPI.begin();
SPI.setBitOrder(MSBFIRST) ;
SPI.setClockDivider(SPI_CLOCK_DIV2);
SPI.setDataMode(SPI_MODE0);
}
void setOutput(unsigned int val)
{
byte highByte = ((val >> 8) & 0xf) | MCP4922_GAIN_1X | MCP4922_VREF_BUF | MCP4922_SHDN;
digitalWrite(PIN_CS,HIGH);
digitalWrite(SS,LOW);
SPI.transfer(highByte);
SPI.transfer(val & 0xff);
digitalWrite(SS,HIGH);
digitalWrite(PIN_CS,LOW); // Latch Vout
}
void loop()
{
TIMSK0 &= ~_BV(TOIE0); // disable Timer0 overflow interrupt
TIMSK2 &= ~_BV(TOIE2); // disable Timer2 overflow interrupt
while(1)
{
setOutput(1024);
setOutput(2048);
}
}
#include <SPI.h>
#define LDAC 8
#define MCP4922_DAC_A 0x00
#define MCP4922_DAC_B 0x80
#define MCP4922_VREF_BUF 0x40
#define MCP4922_GAIN_1X 0x20
#define MCP4922_GAIN_2X 0x00
#define MCP4922_SHDN 0x10
void setup() {
pinMode(LDAC,OUTPUT) ;
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV2);
SPI.setDataMode(SPI_MODE0);
}
inline void setDAC(uint16_t i){
PORTB &= ~_BV(2); // CS(10)_Low
PORTB |= _BV(0); // LDAC(8) -> high
SPDR = (i >> 8) | MCP4922_GAIN_1X | MCP4922_VREF_BUF | MCP4922_SHDN;
while(!(SPSR & _BV(SPIF)));
SPDR = i & 0xff;
while(!(SPSR & _BV(SPIF)));
PORTB |= _BV(2); // CS_High
PORTB &= ~_BV(0); // LDAC -> low
}
void loop() {
while(1){
setDAC(1024);
setDAC(2048);
}
}
レジスタ直操作でも、SPSRを待機して2バイト転送するのは意外と時間がかかるので、さらに高速にするためには、 待機中に別の演算を行うのが良い。但し、下記の方法の場合、関数を3回呼ばないと始めの値がlatch outされない。 100kHzで出力可能で、125kHzでもなんとか可能であった。この方法ではそれ以上の速度は難しかったが、 ATmega328pとBU9480Fで44.1kHzのSDカードプレーヤを作った人はいる様なので、USARTを使用してXCK等を使う別の方法はあるかもしれない。
#include <TimerOne.h>
#include <SPI.h>
#include <math.h>
#define LDAC 8 // ラッチ動作出力ピン
#define MCP4922_DAC_A 0x00
#define MCP4922_DAC_B 0x80
#define MCP4922_VREF_BUF 0x40
#define MCP4922_GAIN_1X 0x20
#define MCP4922_GAIN_2X 0x00
#define MCP4922_SHDN 0x10
#define DAC_FS 10 // [us] = 100kHz
uint8_t regH = 0, regL = 0;
inline void latchDAC() {
// Latch DAC output (It takes three call times to latch out.)
PORTB &= ~_BV(0); // LDAC(8) -> Low
PORTB |= _BV(0); // LDAC(8) -> High
}
inline void setDAC(uint16_t i, uint8_t option) {
// 1. save to regH/L.
// 2. clock out to SPI register.
// 3. latch out.
latchDAC();
// Start SPI transfer (DACA)
PORTB &= ~_BV(2); // CS(10) -> Low
// 1. transfer previous high register
SPDR = regH;
regH = (i >> 8) | option;
while(!(SPSR & _BV(SPIF))); // wait SPI transfer
// 2. transfer previous low register
SPDR = regL;
regL = i & 0xff;
while(!(SPSR & _BV(SPIF))); // wait SPI transfer
PORTB |= _BV(2); // CS_High
}
#define LENGTH_FLAT 100
volatile uint16_t flat[LENGTH_FLAT];
volatile uint8_t count;
volatile uint16_t * p_flat;
void updateDAC() {
uint16_t t;
latchDAC();
PORTB &= ~_BV(2); // CS(10) -> Low
SPDR = regH;
t = *p_flat;
count ++; p_flat ++;
regH = (t >> 8) | MCP4922_DAC_A | MCP4922_GAIN_1X | MCP4922_VREF_BUF | MCP4922_SHDN;
while(!(SPSR & _BV(SPIF))); // wait SPI transfer
SPDR = regL;
regL = t & 0xff;
if (count >= LENGTH_FLAT) {
count = 0;
p_flat = flat;
}
while(!(SPSR & _BV(SPIF))); // wait SPI transfer
PORTB |= _BV(2); // CS_High
}
void setTable(int khz) {
float _khz = khz;
for(int i = 0;i < LENGTH_FLAT;i ++) {
flat[i] = float2dac(sin(khz*2.f*M_PI*(float)i/(float)LENGTH_FLAT));
}
}
void setup() {
setTable(1);
pinMode(LDAC,OUTPUT) ;
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV2);
SPI.setDataMode(SPI_MODE0);
Serial.begin(9600);
Timer1.initialize();
p_flat = flat;
}
inline uint16_t float2dac(float v) {
return (uint16_t)(1000.f*(v*2.+2.08f));
}
#define MAX_CMD_STRING 16
void loop() {
uint8_t cmdString[MAX_CMD_STRING]; uint8_t cmdC = 0;
uint16_t cf_param = 1;
TIMSK0 &= ~_BV(TOIE0); // disable Timer0 overflow interrupt
TIMSK2 &= ~_BV(TOIE2); // disable Timer2 overflow interrupt
// noInterrupts();
Timer1.attachInterrupt(updateDAC, 10); // background
// tiny command line interpreter
while (1) {
if (Serial.available() > 0) {
cmdString[cmdC] = Serial.read();
if (!(cmdString[cmdC] == 0x0d||cmdString[cmdC] == 0x0a)) {
cmdC ++;
if (MAX_CMD_STRING == cmdC) { // reject over maximum length
Serial.print("\r\n> ");
cmdC = 0;
continue;
}
Serial.print((char)cmdString[cmdC-1]); // echo back only
continue;
}
}
}
}
ArduFgxでは、もう少し変えて、メインの割り込み部分は下記の様なコードになっている。DACの設定に必要なビットはテーブル作成時に予め立てておく方が早い。
// Three calls are needed to latch out.
// 1. save to regH/L.
// 2. clock out to SPI register.
// 3. latch out with timer output trigger (LDAC).
void updateDAC_cont() {
uint8_t regL, count_copy; uint16_t smpl;
PORTB |= _BV(2); // end SPI transfer : CS(10) -> High
PORTB &= ~_BV(2); // start SPI transfer: CS(10) -> Low
SPDR = prev_High; // >- SPI write (1) MSB
regL = prev_Low;
smpl = *p_flat; // load table (do heavy task until the SPI transfer ends)
prev_High = smpl >> 8;
prev_Low = smpl & 0xff;
count_copy = count;
count_copy --;
if (count_copy == 0) { count = LENGTH_FLAT, p_flat = g_flat; }
else { p_flat ++; count = count_copy; }
while (!(SPSR & _BV(SPIF))); // <- wait SPI transfer
SPDR = regL; // >- SPI write (2) LSB
// while (!(SPSR & _BV(SPIF))); // <- wait SPI transfer
return;
}
Let's start by acknowledging one external factor that does affect the
popularity of a programming language. To become popular, a programming
language has to be the scripting language of a popular system. Fortran and
Cobol were the scripting languages of early IBM mainframes. C was the
scripting language of Unix, and so, later, was Perl. Tcl is the scripting
language of Tk. Java and Javascript are intended to be the scripting languages
of web browsers.
Lisp is not a massively popular language because it is not the scripting
language of a massively popular system. What popularity it retains dates back
to the 1960s and 1970s, when it was the scripting language of MIT. A lot of
the great programmers of the day were associated with MIT at some point. And
in the early 1970s, before C, MIT's dialect of Lisp, called MacLisp, was one
of the only programming languages a serious hacker would want to use.
-- Paul Graham
-- Being Popular ( http://www.paulgraham.com/popular.html )
<yacoob> a quickie: are 'adjustments layers' planned to be implemented
in gimp?
<nomis> yacoob: at some point in the future, yes.
<yacoob> nomis: dare to estimate how far this future is?
<nomis> yacoob: no.
<rindolf> yacoob: faster if you contribute.
* nomis waits for the "oh, I cannot program at all".
<rindolf> nomis: faster if he learns how to program, and then
contributes.
<nomis> :)
<yacoob> rindolf: you wouldn't like me to contribute, believe me ;)
-- Faster, faster!
-- #gimp, GimpNet