オシレータの音量調節ができるようになった
[main.cpp]
#include "mbed.h"
#define MAIN
#include "global.h"
#define ADD 0x2000000
/*#define ADD 0x20000*/
/*static unsigned int gLEDOn=0x0;*/
static Channel* gChannel[2];
DigitalOut myled1(LED1);
DigitalOut myled2(LED2);
DigitalOut myled3(LED3);
DigitalOut myled4(LED4);
//static void tick();
int main() {
gChannel[0]=new Channel(INTERRUPT_TIME_US);
gChannel[0]->setOmega(ADD);
gChannel[1]=new Channel(INTERRUPT_TIME_US);
gChannel[1]->setOmega(ADD+0x100000);
int volume=0x1000;
while(1) {
wait(0.1);
gChannel[1]->setVolume(volume);
gChannel[0]->setVolume(volume);
volume-=0x100;
if (volume < 0)
{
volume=0x1000;
}
}
}
[global.h]
#define INTERRUPT_TIME_US 22
#include "Channel.h"
extern DigitalOut myled1;
extern DigitalOut myled2;
extern DigitalOut myled3;
extern DigitalOut myled4;
[Channel.h]
#ifndef _CHANNEL_H_INCL
#define _CHANNEL_H_INCL
#define CHANNEL_PART_NUM 16
class Channel
{
public:
/**
* Constructor(only)
* to construct after mbed has started.
* this constructor handles interval timer automatically.
* @value interrupt_us interrupt span (micro second). it work at first time only.
*/
Channel(long interrupt_us);
/**
* Destructor
* this destructor anatach the interval timer automatically.
*/
~Channel();
/**
* Set angle speed(0x100000000 per 1 round)
* frequency is depend on interrupt_us
*/
void setOmega(long speed);
/**
* Set volume(0x10000 max volume)
* @param volume
*/
void setVolume(int volume);
/**
* advance the Angle and caliculate value this span.
* it was called by automatically from interval timer.
* @return value on this tick(max 0xffff0000)
*/
virtual unsigned long tick();
long m_Angle;
long m_Omega;
int m_Volume;
};
#endif
[Channel.cpp]
#include "mbed.h"
#include "global.h"
#include "Channel.h"
static long interrupt_us;
static int part_num=0;
static Channel* part[CHANNEL_PART_NUM];
static Ticker sTicker;
static long total_value_in_this_span;
static AnalogOut sAnalogOut(p18);
/* interrupt handler */
/*static void tick(uint32_t id);*/
static void tick();
Channel::Channel(long interrupt_us)
{
myled1=1;
::interrupt_us=interrupt_us;
if(part_num==0)
{
sTicker.attach_us(::tick,interrupt_us);
}
part_num++;
for(int i=0;i<CHANNEL_PART_NUM;i++)
{
if(part[i]==NULL)
{
part[i]=this;
break;
}
}
m_Volume=0x10000;
}
Channel::~Channel()
{
part_num--;
for(int i=0;i<CHANNEL_PART_NUM;i++)
{
if(part[i]==this)
{
part[i]=NULL;
break;
}
}
if(part_num<=0)
{
sTicker.detach();
}
}
void Channel::setOmega(long speed)
{
m_Omega=speed;
myled2=1;
}
void Channel::setVolume(int volume)
{
m_Volume=volume;
}
unsigned long Channel::tick()
{
m_Angle+=m_Omega;
if (m_Angle & 0x80000000)
{
myled3 = 1;
return (0xffff*m_Volume) >> 16;
}
else
{
myled3 = 0;
return 0;
}
}
void ::tick()
{
long long value=0;
for (int i=0;i<CHANNEL_PART_NUM;i++)
{
if (part[i]!=NULL)
{
value+=total_value_in_this_span + part[i]->tick();
}
}
value = value>=0x10000 ? 0xffff : value;
sAnalogOut.write_u16(value);
myled4=value > 0x80000000;
}
肝はAnalogOut#write_u16()。今まで=で電圧を与えてたんだけど、=はfloatでの電圧値だったみたい。32bitの上位10bitが有効って、どこで見た情報やら。write_u16で16bitの上位10bitを取るようにした。
仕方なく掛け算は使ってるけど割り算はシフト、まあ1tickにトラック分の掛け算くらいなら大丈夫だろう。