单片机补充案例--I2C和AD使用PCF8591

举报
zhangrelay 发表于 2021/07/15 01:33:35 2021/07/15
【摘要】 效果如下所示,AD转换结果用数码管显示: 上电位器:从大变小!(2.49-1.64-0.42-0.33)另一个不变(0.01附近) 下电位器:从小变大!(0.01-0.93-1.26-1.97-2.12)另一个不变(2.49) 源程序是keil,转为Linux_SDCC,如下: adtest.c #include <8052.h> unsigne...

效果如下所示,AD转换结果用数码管显示:

上电位器:从大变小!(2.49-1.64-0.42-0.33)另一个不变(0.01附近)

下电位器:从小变大!(0.01-0.93-1.26-1.97-2.12)另一个不变(2.49)

源程序是keil,转为Linux_SDCC,如下:

adtest.c


  
  1. #include <8052.h>
  2. unsigned char flag1s = 1; //1s定时标志
  3. unsigned char T0RH = 0; //T0重载值的高字节
  4. unsigned char T0RL = 0; //T0重载值的低字节
  5. unsigned char LedBuff[4] ={0xFF,0xFF,0xFF,0xFF}; //显示缓冲区
  6. unsigned __code char smgcode[]={
  7. 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
  8. 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
  9. };
  10. #define LSA P1_5 //LED位选译码地址引脚A
  11. #define LSB P1_6 //LED位选译码地址引脚B
  12. #define LSC P1_7 //LED位选译码地址引脚C
  13. #define SCL P3_7
  14. #define SDA P3_6
  15. void I2cDelay()
  16. {
  17. ;
  18. }
  19. void Timer0() __interrupt 1;
  20. void ConfigTimer0(unsigned int ms);
  21. void ValueToBuff(unsigned char val);
  22. /*******************************************************************************
  23. * 函数名 : I2cStart()
  24. * 函数功能 : 起始信号:在SCL时钟信号在高电平期间SDA信号产生一个下降沿
  25. * 输入 : 无
  26. * 输出 : 无
  27. * 备注 : 起始之后SDA和SCL都为0
  28. *******************************************************************************/
  29. void I2cStart()
  30. {
  31. SDA = 1;
  32. SCL = 1;
  33. I2cDelay();
  34. SDA = 0; //先拉低SDA
  35. I2cDelay();
  36. SCL = 0; //再拉低SCL
  37. I2cDelay();
  38. }
  39. /*******************************************************************************
  40. * 函数名 : I2cStop()
  41. * 函数功能 : 终止信号:在SCL时钟信号高电平期间SDA信号产生一个上升沿
  42. * 输入 : 无
  43. * 输出 : 无
  44. * 备注 : 结束之后保持SDA和SCL都为1;表示总线空闲
  45. *******************************************************************************/
  46. void I2cStop()
  47. {
  48. SDA = 0;
  49. SCL = 0;
  50. I2cDelay();
  51. SCL = 1; //先拉高SCL
  52. I2cDelay();
  53. SDA = 1; //再拉高SDA
  54. I2cDelay();
  55. }
  56. /*******************************************************************************
  57. * 函数名 : I2cWriteByte(unsigned char dat)
  58. * 函数功能 : 通过I2C发送一个字节。在SCL时钟信号高电平期间,保持发送信号SDA保持稳定
  59. * 输入 : dat
  60. * 输出 : 从机的应答值
  61. * 备注 : 发送完一个字节SCL=0,SDA=1
  62. *******************************************************************************/
  63. unsigned char I2cWriteByte(unsigned char dat)
  64. {
  65. unsigned char i = 0;
  66. unsigned char ack;
  67. for(i = 0;i < 8;i++) //要发送8位,从最高位开始
  68. {
  69. SDA = dat >> 7; //起始信号之后SCL=0,所以可以直接改变SDA信号
  70. dat = dat << 1;
  71. I2cDelay();
  72. SCL = 1; //拉高SCL
  73. I2cDelay();
  74. SCL = 0; //再拉低SCL,完成一个位周期
  75. I2cDelay();
  76. }
  77. SDA = 1; //8位数据发送完以后主机释放SDA,以检测从机应答
  78. I2cDelay();
  79. SCL = 1; //拉高SCL
  80. ack = SDA; //读取此时的SDA值,即为从机的应答值
  81. I2cDelay();
  82. SCL = 0; //再拉低SCL完成应答位,并保持住总线
  83. return ack; //应答位取反以符合逻辑习惯:0=不存在
  84. //或忙或失败,1=存在且空闲或写入成功
  85. }
  86. /*******************************************************************************
  87. * 函数名 : I2cReadByte()
  88. * 函数功能 : 使用I2c读取一个字节
  89. * 输入 : ack,1:发送无应答信号,0:发送应答信号
  90. * 输出 : dat
  91. * 备注 : 接收完一个字节SCL=0,SDA=1.
  92. *******************************************************************************/
  93. unsigned char I2cReadByte(unsigned char ack)
  94. {
  95. unsigned char i = 0,dat = 0;
  96. SDA = 1; //起始和发送一个字节之后SCL都是0
  97. I2cDelay();
  98. for(i = 0;i < 8;i++) //从高位到地位接收8位
  99. {
  100. SCL = 1;
  101. I2cDelay();
  102. dat <<= 1;
  103. dat |= SDA;
  104. I2cDelay();
  105. SCL = 0;
  106. I2cDelay();
  107. }
  108. SDA = ack; //8位数据发送完以后,发送应答或非应答信号
  109. I2cDelay();
  110. SCL = 1; //拉高SCL
  111. I2cDelay();
  112. SCL = 0; //再拉低SCL完成应答或非应答位,并保持住总线
  113. return dat;
  114. }
  115. /*A/D转换程序*/
  116. unsigned char GetADCValue(unsigned char chn)
  117. {
  118. unsigned char val;
  119. I2cStart();
  120. if(I2cWriteByte(0x48<<1)!=0) //寻址PCF8591,若未应答,则停止操作并返回0
  121. {
  122. I2cStop();
  123. return 0;
  124. }
  125. I2cWriteByte(0x40 | chn); //写控制字节,选择转换通道
  126. I2cStart();
  127. I2cWriteByte(0x48<<1 | 0x01); //寻址PCF8591,指定后续为读操作
  128. I2cReadByte(0); //先空读一个字节,提供采样转换时间
  129. val = I2cReadByte(1); //读取刚刚转换的值
  130. I2cStop();
  131. return val;
  132. }
  133. void main()
  134. {
  135. unsigned char val; //AD转换后的数字量
  136. unsigned char channel = 0;
  137. EA = 1; //开总中断
  138. ConfigTimer0(2); //配置T0定时2ms
  139. while (1)
  140. {
  141. if (flag1s)
  142. {
  143. flag1s = 0; //显示通道0的电压
  144. val = GetADCValue(channel++); //获取ADC通道0的转换值
  145. ValueToBuff(val); //转为字符串格式的电压值
  146. channel %= 2; //保证channel为0或1
  147. }
  148. }
  149. }
  150. /* 取出数字量的百十个位,保存到显示缓冲区 */
  151. void ValueToBuff(unsigned char val)
  152. {
  153. val = (val*250)/255; //val放大100倍
  154. LedBuff[0] = smgcode[(val%10)]; //取个位数字
  155. LedBuff[1] = smgcode[(val/10)%10];//取十位数字
  156. LedBuff[2] = smgcode[(val/100)]; //取百位数字
  157. LedBuff[2] &= 0x7F; //小数点设置在百位,再缩小100倍
  158. }
  159. /* 配置并启动T0,ms:T0定时时间 */
  160. void ConfigTimer0(unsigned int ms)
  161. {
  162. unsigned long tmp; //临时变量
  163. tmp = 11059200 / 12; //定时器计数频率
  164. tmp = (tmp * ms) / 1000; //计算所需的计数值
  165. tmp = 65536 - tmp; //计算定时器重载值
  166. tmp = tmp + 18; //补偿中断响应延时造成的误差
  167. T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
  168. T0RL = (unsigned char)tmp;
  169. TMOD &= 0xF0; //清零T0的控制位
  170. TMOD |= 0x01; //配置T0为模式1
  171. TH0 = T0RH; //加载T0重载值
  172. TL0 = T0RL;
  173. ET0 = 1; //使能T0中断
  174. TR0 = 1; //启动T0
  175. }
  176. /* 数码管动态扫描刷新函数,需在定时中断中调用 */
  177. void LedScan()
  178. {
  179. static unsigned char i = 0; //动态扫描的索引
  180. P0 = 0xFF; //显示消隐
  181. switch (i)
  182. {
  183. case 0: LSC=0; LSB=0; LSA=1; i++; P0=LedBuff[0]; break;
  184. case 1: LSC=0; LSB=1; LSA=0; i++; P0=LedBuff[1]; break;
  185. case 2: LSC=0; LSB=1; LSA=1; i++; P0=LedBuff[2]; break;
  186. case 3: LSC=1; LSB=0; LSA=0; i=0; P0=LedBuff[3]; break;
  187. default: break;
  188. }
  189. }
  190. /* T0中断服务函数,执行LED动态显示和1s计时 */
  191. void Timer0() __interrupt 1
  192. {
  193. static unsigned int tmr1s = 0;
  194. TH0 = T0RH; //重新加载重载值
  195. TL0 = T0RL;
  196. tmr1s++;
  197. if (tmr1s >= 500) //定时1s,2us计了500次
  198. {
  199. tmr1s = 0;
  200. flag1s = 1;
  201. }
  202. LedScan();
  203. }

接下来两步即可:

  1. sdcc -mmcs51 adtest.c
  2. stcgal -P stc89 adtest.ihx

附keil程序,注意对比差异性!

main.c


  
  1. #include <reg52.h>
  2. #include "PCF8591.h"
  3. bit flag1s = 1; //1s定时标志
  4. unsigned char T0RH = 0; //T0重载值的高字节
  5. unsigned char T0RL = 0; //T0重载值的低字节
  6. unsigned char LedBuff[4] ={0xFF,0xFF,0xFF,0xFF};//显示缓冲区
  7. unsigned char code smgcode[]={
  8. 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
  9. 0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
  10. };
  11. sbit LSA = P1^5; //LED位选译码地址引脚A
  12. sbit LSB = P1^6; //LED位选译码地址引脚B
  13. sbit LSC = P1^7; //LED位选译码地址引脚C
  14. void ConfigTimer0(unsigned int ms);
  15. void ValueToBuff(unsigned char val);
  16. void main()
  17. {
  18. unsigned char val; //AD转换后的数字量
  19. unsigned char channel = 0;
  20. EA = 1; //开总中断
  21. ConfigTimer0(2); //配置T0定时2ms
  22. while (1)
  23. {
  24. if (flag1s)
  25. {
  26. flag1s = 0;
  27. //显示通道0的电压
  28. val = GetADCValue(channel++); //获取ADC通道0的转换值
  29. ValueToBuff(val); //转为字符串格式的电压值
  30. channel %= 2; //保证channel为0或1
  31. }
  32. }
  33. }
  34. /* 取出数字量的百十个位,保存到显示缓冲区 */
  35. void ValueToBuff(unsigned char val)
  36. {
  37. val = (val*250)/255; //val放大100倍
  38. LedBuff[0] = smgcode[(val%10)]; //取个位数字
  39. LedBuff[1] = smgcode[(val/10)%10];//取十位数字
  40. LedBuff[2] = smgcode[(val/100)]; // 取百位数字
  41. LedBuff[2] &= 0x7F; //小数点设置在百位,再缩小100倍
  42. }
  43. /* 配置并启动T0,ms:T0定时时间 */
  44. void ConfigTimer0(unsigned int ms)
  45. {
  46. unsigned long tmp; //临时变量
  47. tmp = 11059200 / 12; //定时器计数频率
  48. tmp = (tmp * ms) / 1000; //计算所需的计数值
  49. tmp = 65536 - tmp; //计算定时器重载值
  50. tmp = tmp + 18; //补偿中断响应延时造成的误差
  51. T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
  52. T0RL = (unsigned char)tmp;
  53. TMOD &= 0xF0; //清零T0的控制位
  54. TMOD |= 0x01; //配置T0为模式1
  55. TH0 = T0RH; //加载T0重载值
  56. TL0 = T0RL;
  57. ET0 = 1; //使能T0中断
  58. TR0 = 1; //启动T0
  59. }
  60. /* 数码管动态扫描刷新函数,需在定时中断中调用 */
  61. void LedScan()
  62. {
  63. static unsigned char i = 0; //动态扫描的索引
  64. P0 = 0xFF; //显示消隐
  65. switch (i)
  66. {
  67. case 0: LSC=0; LSB=0; LSA=1; i++; P0=LedBuff[0]; break;
  68. case 1: LSC=0; LSB=1; LSA=0; i++; P0=LedBuff[1]; break;
  69. case 2: LSC=0; LSB=1; LSA=1; i++; P0=LedBuff[2]; break;
  70. case 3: LSC=1; LSB=0; LSA=0; i=0; P0=LedBuff[3]; break;
  71. default: break;
  72. }
  73. }
  74. /* T0中断服务函数,执行LED动态显示和1s计时 */
  75. void InterruptTimer0() interrupt 1
  76. {
  77. static unsigned int tmr1s = 0;
  78. TH0 = T0RH; //重新加载重载值
  79. TL0 = T0RL;
  80. tmr1s++;
  81. if (tmr1s >= 500) //定时1s,2us计了500次
  82. {
  83. tmr1s = 0;
  84. flag1s = 1;
  85. }
  86. LedScan();
  87. }

PCF8591.h


  
  1. #ifndef __PCF8591_H_
  2. #define __PCF8591_H_
  3. unsigned char GetADCValue(unsigned char chn);
  4. #endif

PCF8591.c


  
  1. #include "PCF8591.h"
  2. #include "i2c.h"
  3. /*A/D转换程序*/
  4. unsigned char GetADCValue(unsigned char chn)
  5. {
  6. unsigned char val;
  7. I2cStart();
  8. if(!I2cWriteByte(0x48<<1)) //寻址PCF8591,若未应答,则停止操作并返回0
  9. {
  10. I2cStop();
  11. return 0;
  12. }
  13. I2cWriteByte(0x40 | chn); //写控制字节,选择转换通道
  14. I2cStart();
  15. I2cWriteByte(0x48<<1 | 0x01); //寻址PCF8591,指定后续为读操作
  16. I2cReadByte(0); //先空读一个字节,提供采样转换时间
  17. val = I2cReadByte(1); //读取刚刚转换的值
  18. I2cStop();
  19. return val;
  20. }

I2C.h


  
  1. #ifndef __I2C_H_
  2. #define __I2C_H_
  3. #include <reg52.h>
  4. sbit SCL = P3^7;
  5. sbit SDA = P3^6;
  6. void I2cStart();
  7. void I2cStop();
  8. bit I2cWriteByte(unsigned char dat);
  9. unsigned char I2cReadByte(bit ACK);
  10. #endif

I2C.c


  
  1. #include"i2c.h"
  2. #include <intrins.h>
  3. #define I2cDelay() {_nop_();_nop_();_nop_();_nop_();}
  4. /*******************************************************************************
  5. * 函数名 : I2cStart()
  6. * 函数功能 : 起始信号:在SCL时钟信号在高电平期间SDA信号产生一个下降沿
  7. * 输入 : 无
  8. * 输出 : 无
  9. * 备注 : 起始之后SDA和SCL都为0
  10. *******************************************************************************/
  11. void I2cStart()
  12. {
  13. SDA = 1;
  14. SCL = 1;
  15. I2cDelay();
  16. SDA = 0; //先拉低SDA
  17. I2cDelay();
  18. SCL = 0; //再拉低SCL
  19. I2cDelay();
  20. }
  21. /*******************************************************************************
  22. * 函数名 : I2cStop()
  23. * 函数功能 : 终止信号:在SCL时钟信号高电平期间SDA信号产生一个上升沿
  24. * 输入 : 无
  25. * 输出 : 无
  26. * 备注 : 结束之后保持SDA和SCL都为1;表示总线空闲
  27. *******************************************************************************/
  28. void I2cStop()
  29. {
  30. SDA = 0;
  31. SCL = 0;
  32. I2cDelay();
  33. SCL = 1; //先拉高SCL
  34. I2cDelay();
  35. SDA = 1; //再拉高SDA
  36. I2cDelay();
  37. }
  38. /*******************************************************************************
  39. * 函数名 : I2cWriteByte(unsigned char dat)
  40. * 函数功能 : 通过I2C发送一个字节。在SCL时钟信号高电平期间,保持发送信号SDA保持稳定
  41. * 输入 : dat
  42. * 输出 : 从机的应答值
  43. * 备注 : 发送完一个字节SCL=0,SDA=1
  44. *******************************************************************************/
  45. bit I2cWriteByte(unsigned char dat)
  46. {
  47. unsigned char i = 0;
  48. bit ack;
  49. for(i = 0;i < 8;i++) //要发送8位,从最高位开始
  50. {
  51. SDA = dat >> 7; //起始信号之后SCL=0,所以可以直接改变SDA信号
  52. dat = dat << 1;
  53. I2cDelay();
  54. SCL = 1; //拉高SCL
  55. I2cDelay();
  56. SCL = 0; //再拉低SCL,完成一个位周期
  57. I2cDelay();
  58. }
  59. SDA = 1; //8位数据发送完以后主机释放SDA,以检测从机应答
  60. I2cDelay();
  61. SCL = 1; //拉高SCL
  62. ack = SDA; //读取此时的SDA值,即为从机的应答值
  63. I2cDelay();
  64. SCL = 0; //再拉低SCL完成应答位,并保持住总线
  65. return ~ack; //应答位取反以符合逻辑习惯:0=不存在
  66. //或忙或失败,1=存在且空闲或写入成功
  67. }
  68. /*******************************************************************************
  69. * 函数名 : I2cReadByte()
  70. * 函数功能 : 使用I2c读取一个字节
  71. * 输入 : ack,1:发送无应答信号,0:发送应答信号
  72. * 输出 : dat
  73. * 备注 : 接收完一个字节SCL=0,SDA=1.
  74. *******************************************************************************/
  75. unsigned char I2cReadByte(bit ack)
  76. {
  77. unsigned char i = 0,dat = 0;
  78. SDA = 1; //起始和发送一个字节之后SCL都是0
  79. I2cDelay();
  80. for(i = 0;i < 8;i++) //从高位到地位接收8位
  81. {
  82. SCL = 1;
  83. I2cDelay();
  84. dat <<= 1;
  85. dat |= SDA;
  86. I2cDelay();
  87. SCL = 0;
  88. I2cDelay();
  89. }
  90. SDA = ack; //8位数据发送完以后,发送应答或非应答信号
  91. I2cDelay();
  92. SCL = 1; //拉高SCL
  93. I2cDelay();
  94. SCL = 0; //再拉低SCL完成应答或非应答位,并保持住总线
  95. return dat;
  96. }

 

 

文章来源: zhangrelay.blog.csdn.net,作者:zhangrelay,版权归原作者所有,如需转载,请联系作者。

原文链接:zhangrelay.blog.csdn.net/article/details/110352069

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。