songjiahaonanna 发表于 2015-6-18 16:14:03

Arduino 怎么使用定时器产生 微妙级的中断

想通过 IO 口定时器模拟出 PWM 波,但现在所知定时器只有毫秒级的,想产生 500HZ 的方波。。

幻生幻灭 发表于 2015-6-19 08:09:17

本帖最后由 幻生幻灭 于 2015-6-19 08:16 编辑

之前做过一个方波发生器,参考下

// Set CS10&CS11 bit for 64 prescaler
//OCR1A = 255;
//Pe = 2, pu = 1 cycle about 2 ms freq = 490Hz
//Pe = 4, pu = 2 cycle about 4 ms freq = 247Hz
//Pe = 6, pu = 3 cycle about 6 ms freq = 165Hz
//Pe = 7, pu = 4 cycle about 7 ms freq = 140Hz
//Pe = 8, pu = 4 cycle about 8 ms freq = 125Hz
//OCR1A = 30;
//Pe = 2, pu = 1 cycle about 0.1 ms freq = 4.03kHz
//Pe = 4, pu = 2 cycle about 0.5 ms freq = 2.00kHz
//Pe = 6, pu = 3 cycle about 0.8 ms freq = 1.35kHz
//Pe = 8, pu = 4 cycle about 1 ms freq = 1.00kHz
//OCR1A = 25;
//Pe = 2, pu = 1 cycle about 0.2 ms freq = 4.83kHz
//Pe = 10, pu = 5 cycle about 1 ms freq = 960Hz
//Pe = 23, pu = 11 cycle about 2.2 ms freq = 420Hz
//Pe = 40, pu = 20 cycle about 4 ms freq = 244Hz(****)
//Pe = 50, pu = 25 cycle about 5.2 ms freq = 193Hz
//Pe = 100, pu = 50 cycle about 10 ms freq = 100Hz
//Pe = 1000, pu = 500 cycle about 100 ms freq = 9.7Hz
//Pe = 2000, pu = 1000 cycle about 200 ms freq = 5Hz
//Pe = 5000, pu = 2500 cycle about 510 ms freq = 1.9Hz(****)

//Hardware connection
//R10Kx2 + 1.5K
//R20K(电位器) +1.5K
//Measure Range: 4.651V(0 - 950)
//Period Range: 40 - 5000
//Calculation: Period = 40 + A0*5(Range: 40 - 4790)
//Calculation: PulseWidth = int((A1/950)*period)

//Hardware Define
int pinFreq = A2;
int pinPulseWidth = A1;
int pinFreqR = A3;
int pinPulseWidthR = A0;
int pinLed = 3;
int pinPWM = 4;
int pinKey = 5;
int pinKeyV = 6;
int pinKeyG = 7;
int pinKeyVisu = 13;
//variables for PW pot monitoring
float pulseWidth;
int pulseWidthScaled;
int PWCurrent;
byte PWTolerance = 2;//adjust this to increase/decrease stability of PW measurement

//variables for freq pot monitoring
int frequency;
int freqCurrent;
byte freqTolerance = 2;//adjust this to increase/decrease stability of frequency measurement
unsigned int freqscaled;

int t;
int period;

int testMode = 0; //Default is 0;
int freqPreTest;
int PWPreTest;
//int cycleISR = 4; //unit us
//定义一个comdata字符串变量,赋初值为空值
String comdata = "";
//numdata是分拆之后的数字数组
int numdata = {
0};
int mark = 0;

void setup() {
Serial.begin(9600);
//set port/pinmode
pinMode(pinLed, OUTPUT);   
pinMode(pinPWM, OUTPUT);
pinMode(pinKeyV, OUTPUT);
pinMode(pinKeyG, OUTPUT);
pinMode(pinKeyVisu, OUTPUT);
pinMode(pinKey, INPUT);
digitalWrite(pinKeyV, HIGH);
digitalWrite(pinKeyG, LOW);
digitalWrite(pinKeyVisu, LOW);


//TIMER INTERRUPT SETUP
cli();//disable interrupts
//timer 1:
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
//set compare match register- 100khz to start
OCR1A = 25; //period = 0.1ms
//turn on CTC mode
TCCR1B |= (1 << WGM12);
// Set CS10&CS11 bit for 64 prescaler
TCCR1B |= (1 << CS10) | (1 << CS11);
// enable timer compare interrupt
TIMSK1 |= (1 << OCIE1A);

//initialize variables 200Hz
t = 0;
period = 48;
pulseWidthScaled = 24;

sei();//enable interrupts

if(testMode == 1){
    Serial.println(" ");
    Serial.println("Test Mode Actived!");
}
}


//Leo checked
ISR(TIMER1_COMPA_vect){//timer 1 interrupt
//increment t and reset each time it reaches period
t += 1;
if (t >= period){
    t = 0;
}
if (pulseWidthScaled <= t) {
    //PORTD = 0xff;
    digitalWrite(pinPWM, HIGH);
    digitalWrite(pinLed, HIGH);
}
else{
    //PORTD = 0x00;
    digitalWrite(pinPWM, LOW);
    digitalWrite(pinLed, LOW);
}
}

//Leo checked
void loop() {
if(testMode == 1) SerialInput();
checkFreq();
checkPW();
if(digitalRead(pinKey) == HIGH){
    digitalWrite(pinKeyVisu, HIGH);
}
else{
    digitalWrite(pinKeyVisu, LOW);
}
}

//Leo checked
void checkFreq() {
if(digitalRead(pinKey) == HIGH){
    freqCurrent = analogRead(pinFreq);
}
else{
    freqCurrent = analogRead(pinFreqR);
}
if(testMode == 1) freqCurrent = freqPreTest;
if (abs(freqCurrent-frequency)>freqTolerance){//if reading from pot exceeds tolerance
    frequency = freqCurrent;
    period = 40 + frequency*5; //Range: 40 - 4790
}
}

//Leo checked
void checkPW() {
if(digitalRead(pinKey) == HIGH){
    PWCurrent = analogRead(pinPulseWidth);
}
else{
    PWCurrent = analogRead(pinPulseWidthR);
}
if(testMode == 1) PWCurrent = PWPreTest;
if (abs(PWCurrent-pulseWidth)>PWTolerance){//if reading from pot exceeds tolerance
    pulseWidth = PWCurrent;//new pulse width, val between 0 and 1023
    if(digitalRead(pinKey) == HIGH){
      pulseWidthScaled = int((pulseWidth/930)*period);
    }
    else{
      pulseWidthScaled = int((pulseWidth/1023)*period);
    }
}
}

//Leo checked
void SerialInput(){
//j是分拆之后数字数组的位置记数
int j = 0;

//不断循环检测串口缓存,一个个读入字符串,
while (Serial.available() > 0)
{
    //读入之后将字符串,串接到comdata上面。
    comdata += char(Serial.read());
    //延时一会,让串口缓存准备好下一个数字,不延时会导致数据丢失,
    delay(2);
    //标记串口读过数据,如果没有数据的话,直接不执行这个while了。
    mark = 1;
}

if(mark == 1)//如果接收到数据则执行comdata分析操作,否则什么都不做。
{
    //显示刚才输入的字符串(可选语句)
    //Serial.println(comdata);
    //显示刚才输入的字符串长度(可选语句)
    //Serial.println(comdata.length());

    /*******************下面是重点*******************/
    //以串口读取字符串长度循环,
    for(int i = 0; i < comdata.length() ; i++)
    {
      //逐个分析comdata字符串的文字,如果碰到文字是分隔符(这里选择逗号分割)则将结果数组位置下移一位
      //即比如11,22,33,55开始的11记到numdata;碰到逗号就j等于1了,
      //再转换就转换到numdata;再碰到逗号就记到numdata;以此类推,直到字符串结束
      if(comdata == ',')
      {
      j++;
      }
      else
      {
      //如果没有逗号的话,就将读到的数字*10加上以前读入的数字,
      //并且(comdata - '0')就是将字符'0'的ASCII码转换成数字0(下面不再叙述此问题,直接视作数字0)。
      //比如输入数字是12345,有5次没有碰到逗号的机会,就会执行5次此语句。
      //因为左边的数字先获取到,并且numdata等于0,
      //所以第一次循环是numdata = 0*10+1 = 1
      //第二次numdata等于1,循环是numdata = 1*10+2 = 12
      //第三次是numdata等于12,循环是numdata = 12*10+3 = 123
      //第四次是numdata等于123,循环是numdata = 123*10+4 = 1234
      //如此类推,字符串将被变成数字0。
      numdata = numdata * 10 + (comdata - '0');
      }
    }
    //comdata的字符串已经全部转换到numdata了,清空comdata以便下一次使用,
    //如果不请空的话,本次结果极有可能干扰下一次。
    comdata = String("");

    period = numdata;
    pulseWidthScaled = numdata;
    Serial.println("P----------------");
    if(digitalRead(pinKey) == HIGH){
      Serial.println("IN2 & IN3 is actived! ");
    }
    else{
      Serial.println("IN1 & IN4 is actived! ");
    }
    Serial.print("period: ");
    Serial.println(period);
    Serial.print("pulseWidthScaled: ");
    Serial.println(pulseWidthScaled);
    //循环输出numdata的内容,并且写到PWM引脚
    for(int i = 0; i < 2; i++)
    {
      numdata = 0;
    }
    //输出之后必须将读到数据的mark置0,不置0下次循环就不能使用了。
    mark = 0;
}
}





Ref:
http://www.instructables.com/id/Arduino-Waveform-Generator-Shield/?ALLSTEPS
Arduino学习笔记A6(补充) - 在串口读取多个字符串,并且转换为数字数组
http://www.geek-workshop.com/thread-260-1-1.html

wdjkzym 发表于 2015-6-19 13:27:18

不是有US级的么

seagatecm 发表于 2015-6-19 22:51:35

@幻生幻灭,为什么在中断程序里面没有重置TCNT1的值?

songjiahaonanna 发表于 2015-6-23 09:13:16

wdjkzym 发表于 2015-6-19 13:27 static/image/common/back.gif
不是有US级的么

毫秒级的不够么,如果我要产生 500 HZ 的方波,就得 2MS 一个周期,还怎么去控制电机的具体转速。。。。

suoma 发表于 2015-6-23 18:46:26

songjiahaonanna 发表于 2015-6-23 09:13 static/image/common/back.gif
毫秒级的不够么,如果我要产生 500 HZ 的方波,就得 2MS 一个周期,还怎么去控制电机的具体转速。。。。

            US、微秒

songjiahaonanna 发表于 2015-6-26 15:07:00

suoma 发表于 2015-6-23 18:46 static/image/common/back.gif
US、微秒

定时器 1 有毫秒级别的,,,,找到了。。
页: [1]
查看完整版本: Arduino 怎么使用定时器产生 微妙级的中断