极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 37995|回复: 21

Arduino引脚读PWM信号???

[复制链接]
发表于 2016-11-17 20:30:52 | 显示全部楼层 |阅读模式
我把Arduino的3号引脚和A0引脚用线连接起来,然后编写下面的程序,就是让3号引脚发出模拟电压,用A0号引脚读入,为什么会读不出呢?



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

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

结果如图:

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复

使用道具 举报

发表于 2016-11-17 21:00:02 | 显示全部楼层
本帖最后由 275891381 于 2016-11-17 22:00 编辑

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




读编码器要用中断

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 2016-11-18 09:47:50 | 显示全部楼层
楼上正解,PWM 属于脉宽调制输出,ArduinoPWM其输出的电压量依然是0V 和5V,只是通过脉冲不同的占空比来改变输出的功率。
另外,你的编码器用的3.3V电压,编码器输出的脉冲也是3.3V和0V的脉冲,自然得到的是660 (1024/5 *3.3)当然,会有一点误差。
实际这两种模块输出的都是非连续变化的脉冲。所以可以用用旋钮电阻。
回复 支持 反对

使用道具 举报

发表于 2016-11-18 10:33:35 | 显示全部楼层
如果只是要讀取 PWM 信號,直接使用計時器計數 PWM 的脈衝寬度會不會比較容易一點
回复 支持 反对

使用道具 举报

 楼主| 发表于 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。

下面是我的程序:

  1. unsigned int time_1ms_ok;
  2. unsigned int time0_old, time0_new, freq_time;
  3. unsigned long freq;

  4. void setup() {
  5.   // 初始化
  6.   freq = 0;
  7.   freq_time = 0;
  8.   time_1ms_ok = 0;
  9.   time0_old = 0;   

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

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

  17.   /*
  18.     0.25MHz/1000Hz = 250
  19.   */
  20.   OCR2A = 0xF9;  // OCR2 = 0xF9(249) ,(249+1)*(1/0.25MHz) = 1ms
  21.   
  22.   TIMSK2 = 1 << OCIE2A;  // 允许T/C2比较匹配中断
  23.   TIFR2 = 1 << OCF2A;

  24.   Serial.begin(9600);
  25. }

  26. void loop() {
  27.   if (time_1ms_ok)
  28.   {
  29.     if (time0_new >= time0_old)
  30.       freq = freq + (time0_new - time0_old);
  31.     else
  32.       freq = freq + (256 - time0_old + time0_new);

  33.     time0_old = time0_new;

  34.     freq_time += 1;
  35.    
  36.     if (freq_time >= 100)
  37.     {
  38.       // 显示计数值
  39.       Serial.println(freq);
  40.       freq_time = 0;    // 100ms到
  41.       freq = 0;
  42.     }
  43.     time_1ms_ok = 0;
  44.   }
  45. }

  46. // 中断
  47. ISR(TIMER2_COMPA_vect)
  48. {
  49.   time0_new = TCNT0;  // 1ms到,记录当前T/C0的计数值
  50.   time_1ms_ok = 1;
  51. }
复制代码


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

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

使用道具 举报

 楼主| 发表于 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。

下面是我的程序:

  1. unsigned int time_1ms_ok;
  2. unsigned int time0_old, time0_new, freq_time;
  3. unsigned long freq;

  4. void setup() {
  5.   // 初始化
  6.   freq = 0;
  7.   freq_time = 0;
  8.   time_1ms_ok = 0;
  9.   time0_old = 0;   

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

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

  17.   /*
  18.     0.25MHz/1000Hz = 250
  19.   */
  20.   OCR2A = 0xF9;  // OCR2 = 0xF9(249) ,(249+1)*(1/0.25MHz) = 1ms
  21.   
  22.   TIMSK2 = 1 << OCIE2A;  // 允许T/C2比较匹配中断
  23.   TIFR2 = 1 << OCF2A;

  24.   Serial.begin(9600);
  25. }

  26. void loop() {
  27.   if (time_1ms_ok)
  28.   {
  29.     if (time0_new >= time0_old)
  30.       freq = freq + (time0_new - time0_old);
  31.     else
  32.       freq = freq + (256 - time0_old + time0_new);

  33.     time0_old = time0_new;

  34.     freq_time += 1;
  35.    
  36.     if (freq_time >= 100)
  37.     {
  38.       // 显示计数值
  39.       Serial.println(freq);
  40.       freq_time = 0;    // 100ms到
  41.       freq = 0;
  42.     }
  43.     time_1ms_ok = 0;
  44.   }
  45. }

  46. // 中断
  47. ISR(TIMER2_COMPA_vect)
  48. {
  49.   time0_new = TCNT0;  // 1ms到,记录当前T/C0的计数值
  50.   time_1ms_ok = 1;
  51. }
复制代码


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

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

使用道具 举报

发表于 2016-11-18 16:56:34 | 显示全部楼层
本帖最后由 croma 于 2016-11-18 17:13 编辑

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

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

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


回复 支持 反对

使用道具 举报

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

使用道具 举报

 楼主| 发表于 2016-11-18 19:16:17 | 显示全部楼层
本帖最后由 liangquan 于 2016-11-18 19:29 编辑
164335413 发表于 2016-11-18 19:02
不清楚你用的哪款绝对型编码器,绝对型编码器读的是相位差,或者光栅码。一般舵机上用的是质量好一些的旋转 ...


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

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


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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

发表于 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 的資料 每收一個位元就左移一位
這部分是狀態字和同位符號



回复 支持 反对

使用道具 举报

发表于 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 的時間
回复 支持 反对

使用道具 举报

发表于 2016-11-20 13:33:19 | 显示全部楼层
谢谢,深受启发
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊

GMT+8, 2026-6-10 04:03 , Processed in 0.051221 second(s), 23 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表