当前位置:   article > 正文

蓝桥杯单片机的几个模块

蓝桥杯单片机

        本文较少涉及硬件与时序,主要从代码层面记录蓝桥杯单片机对DS1302、DS18B20、PCF8591、AT24C02模块的使用

目录

一、DS1302

二、DS18B20

三、IIC

四、PCF8591

五、AT24C02

六、NE555

七、PWM

八、超声波

九、串口


一、DS1302

硬件连接我们仅关注三个引脚:SCK(时钟信号线)、SDA(数据线、RST(芯片使能)。

寄存器:

时序图:

ds1302.c底层驱动:

  1. #include "ds1302.h"
  2. #include <reg52.h>
  3. #include <intrins.h>
  4. sbit SCK = P1^7;
  5. sbit SDA = P2^3;
  6. sbit RST = P1^3;
  7. //写字节,上升沿有效
  8. void Write_Ds1302(unsigned char temp)
  9. {
  10. unsigned char i;
  11. for (i=0;i<8;i++)
  12. {
  13. SCK = 0;
  14. SDA = temp&0x01;//从低位一位一位向右移动
  15. temp>>=1;
  16. SCK=1;
  17. }
  18. }
  19. //向DS1302寄存器写入数据
  20. void Write_Ds1302_Byte( unsigned char address,unsigned char dat )
  21. {
  22. RST=0; _nop_();
  23. SCK=0; _nop_();
  24. RST=1; _nop_();
  25. Write_Ds1302(address); //传入地址(一个字节)
  26. Write_Ds1302(dat); //后传入数据(一个字节)
  27. RST=0;
  28. }
  29. //从DS1302寄存器读出数据
  30. unsigned char Read_Ds1302_Byte ( unsigned char address )
  31. {
  32. unsigned char i,temp=0x00;
  33. RST=0; _nop_();
  34. SCK=0; _nop_();
  35. RST=1; _nop_();
  36. Write_Ds1302(address);//写入地址
  37. for (i=0;i<8;i++) //写入数据,上升沿有效,数据先来 上升沿后来
  38. {
  39. SCK=0;
  40. temp>>=1;
  41. if(SDA)
  42. temp|=0x80; //从高位到低位,一位一位右移
  43. SCK=1;
  44. }
  45. RST=0; _nop_();
  46. SCK=0; _nop_();
  47. SCK=1; _nop_();
  48. SDA=0; _nop_();
  49. SDA=1; _nop_();
  50. return (temp);
  51. }

需要我们在DS1302.c写的函数:

  1. //向芯片写入时分秒
  2. void Set_Rtc(unsigned char* ucRtc)
  3. {
  4. unsigned char i;
  5. Write_Ds1302_Byte(0x8e,0x00);//关闭写保护:向0x8E写入0x00
  6. for(i=0;i<3;i++)//依次写入时分秒
  7. Write_Ds1302_Byte(0x84-2*i,ucRtc[i]);//地址依次为0x84,0x82,0x80
  8. Write_Ds1302_Byte(0x8e,0x80);//打开写保护:向0x8E写入0x80
  9. }
  10. //读出芯片的时分秒
  11. void Read_Rtc(unsigned char* ucRtc)
  12. {
  13. unsigned char i;
  14. for(i=0;i<3;i++)//依次读出时分秒
  15. ucRtc[i] = Read_Ds1302_Byte(0x85-2*i);//地址依次为0x85,0x83,0x81
  16. }

主函数的处理:

  1. #include <ds1302.h>//时钟驱动专用头文件
  2. ...
  3. ...
  4. ...
  5. unsigned char ucRtc[3] = {0x23,0x59,0x55};//时钟数据存放数组 默认时间 23:59:55
  6. ...
  7. ...
  8. ...
  9. void Seg_Proc()
  10. {
  11. if(Seg_Slow_Down) return;
  12. Seg_Slow_Down = 1;//数码管减速程序
  13. /* 信息读取区域 */
  14. Read_Rtc(ucRtc);//实时读取时钟数据
  15. /* 数据处理区域 */
  16. Seg_Buf[0] = ucRtc[0] / 16;
  17. Seg_Buf[1] = ucRtc[0] % 16;
  18. Seg_Buf[2] = 11;//Seg_Dula[11]=0xbf,代表间隔符
  19. Seg_Buf[3] = ucRtc[1] / 16;
  20. Seg_Buf[4] = ucRtc[1] % 16;
  21. Seg_Buf[5] = 11;
  22. Seg_Buf[6] = ucRtc[2] / 16;
  23. Seg_Buf[7] = ucRtc[2] % 16;
  24. }
  25. void main()
  26. {
  27. System_Init();
  28. Timer0Init();
  29. Set_Rtc(ucRtc);//上电时设置时间
  30. while (1)
  31. {
  32. Key_Proc();
  33. Seg_Proc();
  34. Led_Proc();
  35. }
  36. }

注:由于DS1302存储数据的形式是8421BCD码,而数码管显示的形式是十进制(若第i个数码管显示9,Seg_Buf[i]=9),所以存在一个问题:若不进行转换,9(0000 1001)结束后显示16(0001 0000).

        8421BCD码转十进制:DEC=BCD/16*10+BCD%16(2位BCD)

        十进制转8421BCD码:BCD=DEC/10*16+DEC%10(2位BCD)

所以,在DS1302数据显示时,进行/16、%16操作;而在往DS1302写数据时ucRtc[]内的数据即为8421BCD码。

二、DS18B20

硬件连接我们仅关注DQ(数据通信线,外接上拉电阻)

高速暂存器(9个字节)

第0、1字节:

高五位为符号位,温度为正S=1;温度为负S=0。

配置寄存器:

初始化时序:

读写时序:

ds18b20.c底层代码:

  1. #include "onewire.h"
  2. #include "reg52.h"
  3. sbit DQ = P1^4;
  4. //单总线内部延时函数
  5. void Delay_OneWire(unsigned int t)
  6. {
  7. t*=12;//如果不写这一句的话,上电温度显示55.9
  8. while(t--);
  9. }
  10. //单总线写操作
  11. void Write_DS18B20(unsigned char dat)
  12. {
  13. unsigned char i;
  14. for(i=0;i<8;i++)
  15. {
  16. DQ = 0;
  17. DQ = dat&0x01;//从低位到高位一位一位写
  18. Delay_OneWire(5);
  19. DQ = 1;
  20. dat >>= 1;
  21. }
  22. Delay_OneWire(5);
  23. }
  24. //单总线读操作
  25. unsigned char Read_DS18B20(void)
  26. {
  27. unsigned char i;
  28. unsigned char dat;
  29. for(i=0;i<8;i++)
  30. {
  31. DQ = 0;
  32. dat >>= 1;
  33. DQ = 1;
  34. if(DQ)
  35. {
  36. dat |= 0x80;//从高位到低位一位一位写
  37. }
  38. Delay_OneWire(5);
  39. }
  40. return dat;
  41. }
  42. //DS18B20初始化
  43. bit init_ds18b20(void)
  44. {
  45. bit initflag = 0;
  46. DQ = 1;
  47. Delay_OneWire(12);
  48. DQ = 0;
  49. Delay_OneWire(80);
  50. DQ = 1;
  51. Delay_OneWire(10);
  52. initflag = DQ; //0表示初始化成功
  53. Delay_OneWire(5);
  54. return initflag;
  55. }

ds18b20.c需要我们写的函数:

  1. /*DS18B20温度读取函数*/
  2. float read_t()
  3. {
  4. unsigned char low,high;//返回温度数据的高低八位
  5. init_ds18b20();//初始化
  6. Write_DS18B20(0xcc);//跳过ROM
  7. Write_DS18B20(0x44);//进行温度转换
  8. init_ds18b20();//初始化
  9. Write_DS18B20(0xcc);//跳过ROM
  10. Write_DS18B20(0xbe);//读取温度
  11. low = Read_DS18B20();//读取低位
  12. high = Read_DS18B20();//读取高位
  13. return ((high << 8) | low) / 16.0;
  14. }

主函数处理:

  1. #include "onewire.h"
  2. ...
  3. ...
  4. ...
  5. float t;
  6. ...
  7. ...
  8. ...
  9. void Seg_Proc()
  10. {
  11. if(Seg_Slow_Down) return;
  12. Seg_Slow_Down = 1;//数码管减速程序
  13. t = read_t();//获取温度
  14. Seg_Buf[0] = (unsigned char)t / 10 % 10;//显示读取温度的十位
  15. Seg_Buf[1] = (unsigned char)t % 10;//显示读取温度的个位
  16. Seg_Buf[2] = (unsigned int)(t * 10) % 10;//显示读取温度的小数第一位
  17. }
  18. ...
  19. ...
  20. ...
  21. void main()
  22. {
  23. read_t();//上电读取温度,防止显示“85”
  24. Delay750ms();
  25. System_Init();
  26. Timer0Init();
  27. while (1)
  28. {
  29. Key_Proc();
  30. Seg_Proc();
  31. Led_Proc();
  32. }
  33. }

三、IIC

不同IC双向通信,串行数据线(SDA)和串行时钟线(SCL)外接上拉电阻。

底层代码:

  1. #include "iic.h"
  2. //总线引脚定义
  3. sbit SDA = P2^1; /* 数据线 */
  4. sbit SCL = P2^0; /* 时钟线 */
  5. void IIC_Delay(unsigned char i)
  6. {
  7. do{_nop_();}
  8. while(i--);
  9. }
  10. //总线启动条件
  11. void IIC_Start(void)
  12. {
  13. SDA = 1;
  14. SCL = 1;
  15. IIC_Delay(DELAY_TIME);
  16. SDA = 0;
  17. IIC_Delay(DELAY_TIME);
  18. SCL = 0;
  19. }
  20. //总线停止条件
  21. void IIC_Stop(void)
  22. {
  23. SDA = 0;
  24. SCL = 1;
  25. IIC_Delay(DELAY_TIME);
  26. SDA = 1;
  27. IIC_Delay(DELAY_TIME);
  28. }
  29. //发送应答
  30. void IIC_SendAck(bit ackbit)
  31. {
  32. SCL = 0;
  33. SDA = ackbit; // 0:应答,1:非应答
  34. IIC_Delay(DELAY_TIME);
  35. SCL = 1;
  36. IIC_Delay(DELAY_TIME);
  37. SCL = 0;
  38. SDA = 1;
  39. IIC_Delay(DELAY_TIME);
  40. }
  41. //等待应答
  42. bit IIC_WaitAck(void)
  43. {
  44. bit ackbit;
  45. SCL = 1;
  46. IIC_Delay(DELAY_TIME);
  47. ackbit = SDA;
  48. SCL = 0;
  49. IIC_Delay(DELAY_TIME);
  50. return ackbit;
  51. }
  52. //通过I2C总线发送数据
  53. void IIC_SendByte(unsigned char byt)
  54. {
  55. unsigned char i;
  56. for(i=0; i<8; i++)
  57. {
  58. SCL = 0;
  59. IIC_Delay(DELAY_TIME);
  60. if(byt & 0x80) SDA = 1;
  61. else SDA = 0;
  62. IIC_Delay(DELAY_TIME);
  63. SCL = 1;
  64. byt <<= 1;//从高位到低位,一位一位左移
  65. IIC_Delay(DELAY_TIME);
  66. }
  67. SCL = 0;
  68. }
  69. //从I2C总线上接收数据
  70. unsigned char IIC_RecByte(void)
  71. {
  72. unsigned char i, da;
  73. for(i=0; i<8; i++)
  74. {
  75. SCL = 1;
  76. IIC_Delay(DELAY_TIME);
  77. da <<= 1;
  78. if(SDA) da |= 1;从低位到高位一位一位左移
  79. SCL = 0;
  80. IIC_Delay(DELAY_TIME);
  81. }
  82. return da;
  83. }

在起始信号和结束信号中,SCL始终为高电平SDA跳变。所以在数据位传输期间当数据交换时需要把SCL拉低,否则当SCL=1,SDA的任何跳变都会被认为是总线信号的起始和暂停。

四、PCF8591

我们仅关注:地址线A0、A1、A2接地;ADC输入引脚AIN0、AIN1、AIN2、AIN3(AIN1接光敏电阻,AIN3接滑动变阻器);IIC通信引脚SCL、SDA。

地址:A2、A1、A0=0,读地址为0x91,写地址为0x90.

控制字:D1、D0为AD通道,00通道1、01通道1、10通道2、11通道3;D5、D4为模拟量输入选择:一般选择00(四路单输入);D2自动增益默认1;D6模拟输出允许默认1.

光敏电阻为0x41,滑动变阻器为0x43

IIC.c中需要我们写的AD、DA函数

  1. //函数名:ADC转换函数
  2. //入口参数:要进行转换的通道控制位
  3. //返回值:ADC转换的数值
  4. //函数功能:对指定的通道进行ADC转换,函数返回转换的数值
  5. unsigned char AD_Read(unsigned char channel_num_contrl)
  6. {
  7. unsigned char temp;
  8. /*读*/
  9. IIC_Start();//发送开启信号
  10. IIC_SendByte(0x90);//选择PCF8591芯片,确定写的模式
  11. IIC_WaitAck();//等待PCF8591反馈
  12. IIC_SendByte(channel_num_contrl);//确定要转换的通道(顺便,使能DA转换)
  13. IIC_WaitAck();//等待PCF8591反馈
  14. /*写*/
  15. IIC_Start();//发送开启信号
  16. IIC_SendByte(0x91);//选择PCF8591芯片,确定读的模式
  17. IIC_WaitAck();//等待PCF8591反馈
  18. temp = IIC_RecByte();//接收数据
  19. IIC_SendAck(1);//选择不应答
  20. IIC_Stop();//停止发送
  21. return temp;
  22. }
  23. //函数名:DAC转换函数
  24. //入口参数:要进行转换的数值
  25. //返回值:无
  26. //函数功能:对入口参数要转换的DA数据进行转换
  27. void DA_Write(unsigned char trans_dat)
  28. {
  29. IIC_Start();//发送开启信号
  30. IIC_SendByte(0x90);//选择PCF8591芯片,确定写的模式
  31. IIC_WaitAck();//等待PCF8591反馈
  32. IIC_SendByte(0x41);//使能DA转换(随便写通道编号,保证控制字D6为1即可,主要的功能是使能DA)
  33. IIC_WaitAck();//等待PCF8591反馈
  34. IIC_SendByte(trans_dat);//将待转换的数据发送出去
  35. IIC_WaitAck();//等待PCF8591反馈
  36. IIC_Stop();//停止发送
  37. }

主函数的处理:

  1. #include <iic.h>//数模转换底层驱动专属头文件
  2. ...
  3. ...
  4. ...
  5. float Voltage;//实时读取电压值,AD
  6. ...
  7. ...
  8. ...
  9. void Seg_Proc()
  10. {
  11. if(Seg_Slow_Down) return;
  12. Seg_Slow_Down = 1;//数码管减速程序
  13. /* 数据读取区域 */
  14. Voltage = AD_Read(0x43) / 51.0;//读取实时电压值
  15. DA_Write(127);//实时输出电压值
  16. /* 信息显示区域 */
  17. //AD读取值,数码管显示x.xx
  18. Seg_Buf[0] = (unsigned char)Voltage;
  19. Seg_Buf[1] = (unsigned int)(Voltage * 100) / 10 % 10;
  20. Seg_Buf[2] = (unsigned int)(Voltage * 100) % 10;
  21. }

若AD1读取光敏电阻(0x41)的值,AD2读取滑动变阻器(0x43)的值,在main.c中不写

AD1=Ad_Read(0x41);AD2=Ad_Read(0x43);

而是写:AD1=Ad_Read(0x43);AD2=Ad_Read(0x41);

五、AT24C02

硬件连接我们仅关注:地址线E0、E1、E2均为0,写保护WE=0,IIC通信线SCL、SDA。

设备地址字:该EERROM存储空间为2K,A2=A1=A0=0。读地址为0xA1,写地址为0xA0。

需要在iic.c写的EEPROM的函数:

  1. //函数名:写EEPROM函数
  2. //入口参数:需要写入的字符串,写入的地址(务必为8的倍数),写入数量
  3. //返回值:无
  4. //函数功能:向EERPOM的某个地址写入字符串中特定数量的字符。
  5. void EEPROM_Write(unsigned char* EEPROM_String, unsigned char addr, unsigned char num)
  6. {
  7. IIC_Start();//发送开启信号
  8. IIC_SendByte(0xA0);//选择EEPROM芯片,确定写的模式
  9. IIC_WaitAck();//等待EEPROM反馈
  10. IIC_SendByte(addr);//写入要存储的数据地址
  11. IIC_WaitAck();//等待EEPROM反馈
  12. while(num--)
  13. {
  14. IIC_SendByte(*EEPROM_String++);//将要写入的信息写入
  15. IIC_WaitAck();//等待EEPROM反馈
  16. IIC_Delay(200);
  17. }
  18. IIC_Stop();//停止发送
  19. }
  20. //函数名:读EEPROM函数
  21. //入口参数:读到的数据需要存储的字符串,读取的地址(务必为8的倍数),读取的数量
  22. //返回值:无
  23. //函数功能:读取EERPOM的某个地址中的数据,并存放在字符串数组中。
  24. void EEPROM_Read(unsigned char* EEPROM_String, unsigned char addr, unsigned char num)
  25. {
  26. IIC_Start();//发送开启信号
  27. IIC_SendByte(0xA0);//选择EEPROM芯片,确定写的模式
  28. IIC_WaitAck();//等待EEPROM反馈
  29. IIC_SendByte(addr);//写入要读取的数据地址
  30. IIC_WaitAck();//等待EEPROM反馈
  31. IIC_Start();//发送开启信号
  32. IIC_SendByte(0xA1);//选择EEPROM芯片,确定读的模式
  33. IIC_WaitAck();//等待EEPROM反馈
  34. while(num--)
  35. {
  36. *EEPROM_String++ = IIC_RecByte();//将要写入的信息写入
  37. if(num) IIC_SendAck(0);//发送应答
  38. else IIC_SendAck(1);//不应答
  39. }
  40. IIC_Stop();//停止发送
  41. }

主函数的处理:

  1. #include "iic.h"
  2. ...
  3. ...
  4. ...
  5. unsigned char dat[2] = {30,60};
  6. unsigned char a = 200;
  7. ...
  8. ...
  9. ...
  10. /* 键盘处理函数 */
  11. void Key_Proc()
  12. {
  13. if(Key_Slow_Down) return;
  14. Key_Slow_Down = 1;//键盘减速程序
  15. Key_Val = Key_Read();//实时读取键码值
  16. Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
  17. Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿
  18. Key_Old = Key_Val;//辅助扫描变量
  19. switch(Key_Down)
  20. {
  21. case 19:
  22. a += 10;
  23. break;
  24. case 18:
  25. dat[1] -= 10;
  26. break;
  27. case 17:
  28. EEPROM_Write(dat,0,2);
  29. break;
  30. case 16:
  31. EEPROM_Write(&a,0,1);
  32. break;
  33. }
  34. }
  35. ...
  36. ...
  37. ...
  38. /* 信息处理函数 */
  39. void Seg_Proc()
  40. {
  41. if(Seg_Slow_Down) return;
  42. Seg_Slow_Down = 1;//数码管减速程序
  43. Seg_Buf[0] = a / 100 % 10;
  44. Seg_Buf[1] = a / 10 % 10;
  45. Seg_Buf[2] = a % 10;
  46. Seg_Buf[6] = dat[1] / 10;
  47. Seg_Buf[7] = dat[1] % 10;
  48. }
  49. ...
  50. ...
  51. ...
  52. void main()
  53. {
  54. EEPROM_Read(&a,0,1);
  55. EEPROM_Read(dat,0,2);
  56. System_Init();
  57. Timer0Init();
  58. while (1)
  59. {
  60. Key_Proc();
  61. Seg_Proc();
  62. Led_Proc();
  63. }
  64. }

第一次烧录前将主函数的读出函数删除(注释),烧录后修改变量的值并保存到EEPROM中。断电,将主函数的读出函数加上,上电后显示的变量为修改后的变量。

由于EEPROM读写数据以字节为单位,所以在使用中尽量使用char型。

六、NE555

由原理图可知,NE555的输出引脚OUT连接NET SIG,当使用跳线帽连接P34引脚后,使用定时器1作为定时器(中断号为3)进行1ms定时,使用定时器0作为计数器记录脉冲上升沿。

在TIM0的TMOD中,GATE配置为0,C/T配置为1,M1、M0配置为01模式,即16位不可重装载模式。TMOD0=0101.

 使用STC—ISP对定时器0进行如下配置

不需要进行定时器0中断修改代码为(将TMOD |= 0x01;修改为TMOD |= 0x05;)

  1. void Timer0Init(void) //1毫秒@12.000MHz
  2. {
  3. AUXR &= 0x7F; //定时器时钟12T模式
  4. TMOD &= 0xF0; //设置定时器模式
  5. TMOD |= 0x05; //设置计数模式
  6. TL0 = 0; //设置定时初始值
  7. TH0 = 0; //设置定时初始值
  8. TF0 = 0; //清除TF0标志
  9. TR0 = 1; //定时器0开始计时
  10. }
  11. void Timer1Init(void) //1毫秒@12.000MHz
  12. {
  13. AUXR &= 0xBF; //定时器时钟12T模式
  14. TMOD &= 0x0F; //设置定时器模式
  15. TL1 = 0x18; //设置定时初值
  16. TH1 = 0xFC; //设置定时初值
  17. TF1 = 0; //清除TF1标志
  18. TR1 = 1; //定时器1开始计时
  19. ET1 = 1;
  20. EA = 1;
  21. }

注意:(1)频率值定义为int型(必超255)(2)定时器中断号为3 (3)TH0、TL0的值需要手动归零

  1. unsigned int Timer_1000Ms;//1000毫秒计时变量
  2. unsigned int Freq;//实时频率值
  3. void Timer1Server() interrupt 3
  4. {
  5. if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//键盘减速专用
  6. if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//数码管减速专用
  7. if(++Seg_Pos == 8) Seg_Pos = 0;//数码管显示专用
  8. Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
  9. Led_Disp(Seg_Pos,ucLed[Seg_Pos]);
  10. if(++Timer_1000Ms == 1000) //实时读取频率值
  11. {
  12. Timer_1000Ms = 0;
  13. Freq = TH0 << 8 | TL0;
  14. TH0 = TL0 = 0;
  15. }
  16. }

七、PWM

一般要求设置20%这样的占空比,可考虑在定时器中断中使一个count变量从0到9往复循环,定义一个阈值例如2,可以在一个周期内进行两个变量的比较,例如低于阈值输出1高于阈值输出0,以此在极短的时间内从宏观上控制功率。

下面以P34引脚输出不同占空比、频率为1kHz的PWM为例:

由于频率为1kHz(要实现定时器里的count变量从0到9,定时器的周期要0.1ms)而定时器0的周期为1ms,所以需要重新使能另一个定时器。

  1. void Timer1Init(void) //100微秒@12.000MHz
  2. {
  3. AUXR &= 0xBF; //定时器时钟12T模式
  4. TMOD &= 0x0F; //设置定时器模式
  5. TL1 = 0x9C; //设置定时初值
  6. TH1 = 0xFF; //设置定时初值
  7. TF1 = 0; //清除TF1标志
  8. TR1 = 1; //定时器1开始计时
  9. ET1 = 1;
  10. }
  11. void Timer1Server() interrupt 3
  12. {
  13. if(work_time != 0)
  14. {
  15. if(++pwm_count == 10) pwm_count = 0;
  16. switch(wind_mode)
  17. {
  18. case 0:
  19. pwm_level = 2;
  20. break;
  21. case 1:
  22. pwm_level = 3;
  23. break;
  24. case 2:
  25. pwm_level = 7;
  26. break;
  27. }
  28. if(pwm_count >= pwm_level) P34 = 0;
  29. else P34 = 1;
  30. }
  31. }

使能定时器1 中断,每0.1ms进入一次中断实现pwm_count++,根据pwm_count和pwm_level的大小关系,给P34电平的高和低。

八、超声波

J2跳线帽选1-3 3-5,一般选用定时器1作为信号测量。发送引脚TX,接受引脚RX。

  1. #include <ultrasonic.h>
  2. #include <intrins.h>
  3. sbit Tx = P1^0;
  4. sbit Rx = P1^1;
  5. void Delay12us() //@12.000MHz
  6. {
  7. unsigned char i;
  8. _nop_();
  9. _nop_();
  10. i = 38;
  11. while (--i);
  12. }
  13. void Wave_Init()
  14. {
  15. unsigned char i;
  16. for(i=0;i<8;i++)
  17. {
  18. Tx = 1;
  19. Delay12us();
  20. Tx = 0;
  21. Delay12us();
  22. }
  23. }
  24. unsigned int Ut_Wave_Data() //超声波距离读取函数
  25. {
  26. unsigned int time;//时间储存变量
  27. CMOD = 0x00;//配置PCA工作模式
  28. CH = CL = 0;//复位计数值 等待超声波信号发出
  29. Wave_Init();//发送超声波信号
  30. CR = 1;//开始计时
  31. while((Rx == 1) && (CF == 0));//等待接受返回信号或者定时器溢出
  32. CR = 0;//停止计时
  33. if(CF == 0) //定时器没有溢出
  34. {
  35. time = CH << 8 | CL;//读取当前时间
  36. return (time * 0.017);//返回距离值
  37. }
  38. else
  39. {
  40. CF = 0;//清除溢出标志位
  41. return 0;
  42. }
  43. }

初始化TX产生8个40KHz的方波信号,开启定时器计时,接收到超声波信号返回后关闭定时器。默认声速340m/s,距离=时间*0.017(cm)

main.c的使用

  1. unsigned char Wave_Data;
  2. void Seg_Proc()
  3. {
  4. if(Seg_Slow_Down) return;
  5. Seg_Slow_Down = 1;//数码管减速程序
  6. Wave_Data = Ut_Wave_Data();
  7. Seg_Buf[0] = Wave_Data / 100 % 10;
  8. Seg_Buf[1] = Wave_Data / 10 % 10;
  9. Seg_Buf[2] = Wave_Data % 10;
  10. }

九、串口

配置如下:

添加“ES=1;EA=1”

uart.c如下:

  1. #include <Uart.h>
  2. /* 串口初始化函数 */
  3. void UartInit(void) //9600bps@12.000MHz
  4. {
  5. SCON = 0x50; //8位数据,可变波特率
  6. AUXR |= 0x01; //串口1选择定时器2为波特率发生器
  7. AUXR |= 0x04; //定时器时钟1T模式
  8. T2L = 0xC7; //设置定时初始值
  9. T2H = 0xFE; //设置定时初始值
  10. AUXR |= 0x10; //定时器2开始计时
  11. ES = 1;
  12. EA = 1;
  13. }
  14. /* 字节发送函数 */
  15. void SendByte(unsigned char dat)
  16. {
  17. SBUF=dat;//将dat数据赋给SBUF,将数据发送出去
  18. while(TI == 0);//等待数据发送
  19. TI = 0;//将发送标志位清零
  20. }
  21. /* 字符串发送函数 */
  22. void Uart_Send_String(unsigned char *dat)
  23. {
  24. while(*dat != '\0')//当字符不为空时,继续发送
  25. SendByte(*dat++);//发送后指针dat加1,指向下一个字节
  26. }

由于定时器0负责三大模块的扫描与其他变量的计数,定时器1负责超声波或NE555的计数,故使用定时器2作为波特率发生器。

main.c处理:

  1. #include <Uart.h>//串口底层驱动专用头文件
  2. #include <stdio.h>
  3. unsigned char Uart_Slow_Down;//串口减速专用变量
  4. unsigned char Uart_Recv[10];//串口接收数据储存数组 默认10个字节 若接收数据较长 可更改最大字节数
  5. unsigned char Uart_Recv_Index;//串口接收数组指针
  6. unsigned char Uart_Send[10];//串口接收数据储存数组 默认10个字节 若发送数据较长 可更改最大字节数
  7. unsigned char dat;
  8. void Key_Proc()
  9. {
  10. if(Key_Slow_Down) return;
  11. Key_Slow_Down = 1;//键盘减速程序
  12. Key_Val = Key_Read();//实时读取键码值
  13. Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
  14. Key_Up = ~Key_Val & (Key_Old ^ Key_Val);//捕捉按键上降沿
  15. Key_Old = Key_Val;//辅助扫描变量
  16. switch(Key_Down)
  17. {
  18. case 4:
  19. sprintf(Uart_Send,"T = %.2f\r\n",t);
  20. Uart_Send_String(Uart_Send);
  21. break;
  22. }
  23. }
  24. void Seg_Proc()
  25. {
  26. if(Seg_Slow_Down) return;
  27. Seg_Slow_Down = 1;//数码管减速程序
  28. t = rd_temperature();
  29. Seg_Buf[0] = dat / 10;
  30. Seg_Buf[1] = dat % 10;
  31. }
  32. void Uart_Proc()
  33. {
  34. if(Uart_Slow_Down) return;
  35. Uart_Slow_Down = 1;//串口减速程序
  36. if(Uart_Recv_Index == 6)
  37. {
  38. if(Uart_Recv[0] == 'L' && Uart_Recv[1] == 'e' && Uart_Recv[2] == 'd' && Uart_Recv[4] == '=')
  39. ucLed[Uart_Recv[3] - 48] = Uart_Recv[5] - 48;
  40. Uart_Recv_Index = 0;
  41. }
  42. }
  43. void Uart1Server() interrupt 4
  44. {
  45. if(RI == 1) //串口接收数据
  46. {
  47. Uart_Recv[Uart_Recv_Index] = SBUF;
  48. Uart_Recv_Index++;
  49. RI = 0;
  50. }
  51. }
  52. void main()
  53. {
  54. System_Init();
  55. Timer0Init();
  56. UartInit();
  57. while (1)
  58. {
  59. Key_Proc();
  60. Seg_Proc();
  61. Led_Proc();
  62. Uart_Proc();
  63. }
  64. }

按下S4向串口发送一次温度数据,在接受缓冲区显示。在发送缓冲区输入“Ledi=x”可改变LED状态。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/560695
推荐阅读
相关标签
  

闽ICP备14008679号