liangquan 发表于 2016-11-17 20:30:52

Arduino引脚读PWM信号???

我把Arduino的3号引脚和A0引脚用线连接起来,然后编写下面的程序,就是让3号引脚发出模拟电压,用A0号引脚读入,为什么会读不出呢?



另:
我买了一个绝对编码器,编码器的引脚是这样的:
红色:3.3V
黑色:GND
黄色:CSN
绿色:CLK
蓝色:D0
白色:PWM

我尝试用Arduino的A0引脚来读取PWM的值,结果也读不出!
电路图如图:

结果如图:

我觉得我的两个问题是一致的,为什么会读不出值呢???哪里出问题了???我觉得是接线的问题!!!

275891381 发表于 2016-11-17 21:00:02

本帖最后由 275891381 于 2016-11-17 22:00 编辑

肯定不行呀,至少弄个积分电路吧 把pwm转成模拟量




读编码器要用中断

164335413 发表于 2016-11-18 09:47:50

楼上正解,PWM 属于脉宽调制输出,ArduinoPWM其输出的电压量依然是0V 和5V,只是通过脉冲不同的占空比来改变输出的功率。
另外,你的编码器用的3.3V电压,编码器输出的脉冲也是3.3V和0V的脉冲,自然得到的是660 (1024/5 *3.3)当然,会有一点误差。
实际这两种模块输出的都是非连续变化的脉冲。所以可以用用旋钮电阻。

croma 发表于 2016-11-18 10:33:35

如果只是要讀取 PWM 信號,直接使用計時器計數 PWM 的脈衝寬度會不會比較容易一點

liangquan 发表于 2016-11-18 15:03:23

275891381 发表于 2016-11-17 21:00
肯定不行呀,至少弄个积分电路吧 把pwm转成模拟量




事实上在用A0引脚读PWM脉冲之前,我就是尝试用中断来读PWM信号的!

由于我着急,匆忙,以为编码器不同的角度对应PWM信号的不同频率,所以我参考“AVR单片机嵌入式系统原理与应用实践.pdf”第11章的例子,编写了程序,是读频率信号的频率的,其原理如下:
1. 使用两个定时计数器。T/C0工作在计数器方式,对外部T0引脚输入的脉冲信号计数(上升沿触发)。

2. T/C2工作在CTC方式,每隔1ms中断一次,每一次T/C2的中断中,都首先记录下T/C0寄存器TCNT0当前的计数值,因此前后两次TCNT0的差值(time0_new - time0_old)或(256 - time0_old + time0_new)就是1ms时间内T0脚输入的的脉冲个数(这个表达式要理解,其原理是溢出了,超过了256)。为了提高测量精度,程序对100个1ms的脉冲个数进行了累计(在变量freq中),即已知限定的时间为100ms。

下面是我的程序:

unsigned int time_1ms_ok;
unsigned int time0_old, time0_new, freq_time;
unsigned long freq;

void setup() {
// 初始化
freq = 0;
freq_time = 0;
time_1ms_ok = 0;
time0_old = 0;   

DDRD = 0x00;// 端口PD4的第二功能,T/C0外部计数器输入
TCCR0A = 0;
TCCR0B = 1 << CS02 | 1 << CS01 | 0 << CS00; // 外部T0引脚下降沿触发计数,普通模式;
//TCNT0 = 0x00;   // 初始化计数值

TCCR2A = 1 << WGM21;
TCCR2B = 1 << CS22;   // 内部时钟,64分频(16M/64 = 0.25MHz),CTC模式
TCNT2 = 0x00;   // 初始化计数值

/*
    0.25MHz/1000Hz = 250
*/
OCR2A = 0xF9;// OCR2 = 0xF9(249) ,(249+1)*(1/0.25MHz) = 1ms

TIMSK2 = 1 << OCIE2A;// 允许T/C2比较匹配中断
TIFR2 = 1 << OCF2A;

Serial.begin(9600);
}

void loop() {
if (time_1ms_ok)
{
    if (time0_new >= time0_old)
      freq = freq + (time0_new - time0_old);
    else
      freq = freq + (256 - time0_old + time0_new);

    time0_old = time0_new;

    freq_time += 1;
   
    if (freq_time >= 100)
    {
      // 显示计数值
      Serial.println(freq);
      freq_time = 0;    // 100ms到
      freq = 0;
    }
    time_1ms_ok = 0;
}
}

// 中断
ISR(TIMER2_COMPA_vect)
{
time0_new = TCNT0;// 1ms到,记录当前T/C0的计数值
time_1ms_ok = 1;
}


但后来我发现,这个程序只能读出PWM信号的频率,但以我的理解,绝对编码器的不同角度,对应不同的PWM信号占空比,所以光读频率还不行,得读占空比,对不?

如果是这样的话,问题就来了:在上面这个读频率的程序中,第1步要求把T/C0设置为上升沿计数还是下降沿计数,可如果要读PWM的占空比,需要既读上升沿、又要读下降沿,这可怎么设置呢???

liangquan 发表于 2016-11-18 15:07:56

croma 发表于 2016-11-18 10:33
如果只是要讀取 PWM 信號,直接使用計時器計數 PWM 的脈衝寬度會不會比較容易一點

您能给我提供一个都PWM信号占空比的编程思路么?

事实上在用A0引脚读PWM脉冲之前,我就是尝试用中断来读PWM信号的!

由于我着急,匆忙,以为编码器不同的角度对应PWM信号的不同频率,所以我参考“AVR单片机嵌入式系统原理与应用实践.pdf”第11章的例子,编写了程序,是读频率信号的频率的,其原理如下:
1. 使用两个定时计数器。T/C0工作在计数器方式,对外部T0引脚输入的脉冲信号计数(上升沿触发)。

2. T/C2工作在CTC方式,每隔1ms中断一次,每一次T/C2的中断中,都首先记录下T/C0寄存器TCNT0当前的计数值,因此前后两次TCNT0的差值(time0_new - time0_old)或(256 - time0_old + time0_new)就是1ms时间内T0脚输入的的脉冲个数(这个表达式要理解,其原理是溢出了,超过了256)。为了提高测量精度,程序对100个1ms的脉冲个数进行了累计(在变量freq中),即已知限定的时间为100ms。

下面是我的程序:

unsigned int time_1ms_ok;
unsigned int time0_old, time0_new, freq_time;
unsigned long freq;

void setup() {
// 初始化
freq = 0;
freq_time = 0;
time_1ms_ok = 0;
time0_old = 0;   

DDRD = 0x00;// 端口PD4的第二功能,T/C0外部计数器输入
TCCR0A = 0;
TCCR0B = 1 << CS02 | 1 << CS01 | 0 << CS00; // 外部T0引脚下降沿触发计数,普通模式;
//TCNT0 = 0x00;   // 初始化计数值

TCCR2A = 1 << WGM21;
TCCR2B = 1 << CS22;   // 内部时钟,64分频(16M/64 = 0.25MHz),CTC模式
TCNT2 = 0x00;   // 初始化计数值

/*
    0.25MHz/1000Hz = 250
*/
OCR2A = 0xF9;// OCR2 = 0xF9(249) ,(249+1)*(1/0.25MHz) = 1ms

TIMSK2 = 1 << OCIE2A;// 允许T/C2比较匹配中断
TIFR2 = 1 << OCF2A;

Serial.begin(9600);
}

void loop() {
if (time_1ms_ok)
{
    if (time0_new >= time0_old)
      freq = freq + (time0_new - time0_old);
    else
      freq = freq + (256 - time0_old + time0_new);

    time0_old = time0_new;

    freq_time += 1;
   
    if (freq_time >= 100)
    {
      // 显示计数值
      Serial.println(freq);
      freq_time = 0;    // 100ms到
      freq = 0;
    }
    time_1ms_ok = 0;
}
}

// 中断
ISR(TIMER2_COMPA_vect)
{
time0_new = TCNT0;// 1ms到,记录当前T/C0的计数值
time_1ms_ok = 1;
}


但后来我发现,这个程序只能读出PWM信号的频率,但以我的理解,绝对编码器的不同角度,对应不同的PWM信号占空比,所以光读频率还不行,得读占空比,对不?

如果是这样的话,问题就来了:在上面这个读频率的程序中,第1步要求把T/C0设置为上升沿计数还是下降沿计数,可如果要读PWM的占空比,需要既读上升沿、又要读下降沿,这可怎么设置呢???

croma 发表于 2016-11-18 16:56:34

本帖最后由 croma 于 2016-11-18 17:13 编辑

如果不在乎資源的消耗的話 最容易實現的方式

假設以 1 Byte 表示 PWM 信號 也就有 256 個狀態 取 250 個來表示
PWM 信號的週期如果為 25ms 除以 250 每一個變化是 100us

那麼設定一個計時器每 100us 去檢查一次這個 PWM 信號
當由零變一時歸零 計數值 每次檢查信號為一時 計數值遞增 直到一變零的那一刻就根據計數值去刷新 PWM 值


164335413 发表于 2016-11-18 19:02:41

不清楚你用的哪款绝对型编码器,绝对型编码器读的是相位差,或者光栅码。一般舵机上用的是质量好一些的旋转电阻器。
另外,你一开始的问题比较模糊,容易给人误解,反而不能更好的帮你。
用外部中断,触发方式改成边沿触发,加上定时器计时。这样能算出脉宽和频率,但还是我说的,不太清楚你需要干什么。

liangquan 发表于 2016-11-18 19:16:17

本帖最后由 liangquan 于 2016-11-18 19:29 编辑

164335413 发表于 2016-11-18 19:02
不清楚你用的哪款绝对型编码器,绝对型编码器读的是相位差,或者光栅码。一般舵机上用的是质量好一些的旋转 ...

用的是这款编码器。
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.TlHVwH&id=532121303239&_u=iiluasi7341
它有两种输出信号形式:
SSI信号(CSN、CLK、DO三根信号线)、PWM(一根信号线)
前一个不会用,稍明白点后一个,所以想读PWM信号,获得角度值。

就是想读编码器的输出信号,获得角度值。应该怎样读这个PWM信号?


如图所示,不是要么上升沿、要么下降沿,也没有边沿触发呀?

croma 发表于 2016-11-18 20:37:42

本帖最后由 croma 于 2016-11-18 20:51 编辑

liangquan 发表于 2016-11-18 19:16
用的是这款编码器。
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.TlHVwH&id=532121303239&_u=i ...

如果你的接腳有餘裕的話~ 我建議你還是讀SSI 信號吧~ 比較簡單 精度也比較高
PWM 信號要得到同樣解析度的成本要高得多了


產品上有附給你 SSI 的時序圖了

三線
CSn <- 致能
CLK <- 時脈
D0<- 資料

CSn 和 CLK 先送高電位 1us
CSn 送 低電位 1us

接下來 CLK 間隔 1us 送 10 個正負變化
在 CLK 發送 負源 信號的時候 從 D0 收取一個 bit 的資料 每收一個位元就左移一位
這部分是角度資料解析度是 1024

接下來 CLK 間隔 1us 送 6 個正負變化
在 CLK 發送 負源 信號的時候 從 D0 收取一個 bit 的資料 每收一個位元就左移一位
這部分是狀態字和同位符號



croma 发表于 2016-11-19 04:28:00

liangquan 发表于 2016-11-18 19:16
用的是这款编码器。
https://item.taobao.com/item.htm?spm=a1z09.2.0.0.TlHVwH&id=532121303239&_u=i ...

如果你非要透過中斷觸發,那就把 pwm 信號連到兩個中斷上,一個處理正緣 啟動 16 bit 的計時器順便計算週期,另一個處理負緣抓 pwm 的時間

zuzuzu 发表于 2016-11-20 13:33:19

谢谢,深受启发
页: [1]
查看完整版本: Arduino引脚读PWM信号???