UIAPduinoでADC
先日,UIAPduinoでserialとi2cを使ったら,flashの容量オーバーになってしまった. 一方,serialとADCを使った適当なプログラムをコンパイルしたら,9kバイト程度だったので,これなら十分にflashに書き込めた. 少し戸惑ったのがピンの指定で,例えば,analogRead(1)だと,ピン番号が1のA0では無くA1になるようだ. analogRead(A1)などとしても動くのだろうが,内部ではA1は0xc1などと表されているようだ.
ADCを使うプログラムなら,容量が小さくできるch32funやAlexanderManderaさんの環境を使わずにすむのだが,それらではどの位のサイズになるのか知りたくなるものである. しかし,AlexanderManderaさんの環境では,ADCは実装されていない. 仕方が無いので,自分で書いてみた.
以下の内容の~/.arduino15/packages/alexandermandera/hardware/wch/0.0.2/cores/arduino/wiring_analog.cppというファイルを作る.
#include "Arduino.h"
#include "ch32v003fun.h"
#include "wiring_private.h"
static int write_resolution = 8;
static int read_resolution = 10;
void analogWrite(pin_size_t pin, int val){}
void analogWriteResolution(int bits){}
int analogRead(pin_size_t pin)
{
uint8_t adc2gpio[] = {0,0,2,3,3,3,3,3};
uint8_t adc2pin[] = {2,1,4,2,3,5,6,4};
uint8_t gpio = adc2gpio[pin & 0x07];
GPIO_TypeDef* port = gpioRegister(gpio);
uint8_t p = adc2pin[pin & 0x07];
// ADCCLK = 24 MHz => RCC_ADCPRE = 0: divide by 2
RCC->CFGR0 &= ~(0x1F<<11);
// Enable GPIO* and ADC
RCC->APB2PCENR |= (0x04 << gpio) | RCC_APB2Periph_ADC1;
// P*p is analog input chl n
port->CFGLR &= ~(0xf<<(4*p)); // CNF = 00: Analog, MODE = 00: Input
// Reset the ADC to init all regs
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
// Set up single conversion on chl n
ADC1->RSQR1 = 0;
ADC1->RSQR2 = 0;
ADC1->RSQR3 = pin; // 0-9 for 8 ext inputs and two internals
// set sampling time for chl n
ADC1->SAMPTR2 &= ~(ADC_SMP0<<(3*pin));
ADC1->SAMPTR2 |= 7<<(3*pin); // 0:7 => 3/9/15/30/43/57/73/241 cycles
// turn on ADC and set rule group to sw trig
ADC1->CTLR2 |= ADC_ADON | ADC_EXTSEL;
// Reset calibration
ADC1->CTLR2 |= ADC_RSTCAL;
while(ADC1->CTLR2 & ADC_RSTCAL);
// Calibrate
ADC1->CTLR2 |= ADC_CAL;
while(ADC1->CTLR2 & ADC_CAL);
// start sw conversion (auto clears)
ADC1->CTLR2 |= ADC_SWSTART;
// wait for conversion complete
while(!(ADC1->STATR & ADC_EOC));
// get result
return ADC1->RDATAR;
}
void analogReadResolution(int bits){read_resolution = bits;}
int getAnalogReadResolution(){return read_resolution;}
そして,wiring_digital.cpp中で定義されているgpioRegisterをwiring_private.hに移した. gpioForPinやgpioPinも移しても良い気がするけど,今回は必要無いので,そのままにしておいた. このコードは,ch32funのadc_polled.cを元に作ったものなのだが,動作は確認したが,十分な検討をしていないので,何か不具合はあるかも知れない. ちなみにこの環境では,A1はPA1のことを意味しており,0番ピンという意味になって,analogRead(A1)とすると,A0の値が読まれるというようなことになるだろうから,A1などの表記は使わない方が良いだろう. また,pinに7よりも大きい数字を指定した場合には,変な動作になる可能性があったので,pin&0x07としておいた. pin%9などの方が良いかも.
先と同じコードをコンパイルしたら,2.4kバイトになった. やはり,1/4程度である. UIAPduinoを使っていると,この環境は有用に思えてくるのだが,以前も書いたように,ADCやPWMやSPI が使えず,i2cはWireでない手法でのみ使える. 今回,ADCが一応使えるようになったので,欠点は少し減ったかも知れないが,まだまだ改良の余地がある. 自分が新たな機能を使う度に,書き足して行けばいいのかな. Arduinoとch32funに詳しい人が,参入してくれないかな.