当前位置:   article > 正文

蓝桥杯第十三届电子类单片机组决赛程序设计_第十三届蓝桥杯单片机

第十三届蓝桥杯单片机

前言

一、决赛题目

1.比赛题目

2.题目解读

二、功能实现

1.关于定时器资源

1)超声波和NE555需要的定时器资源

2)定时器2

2.单位切换

3.数据长度不足时,高位熄灭

4.AD/DA多通道的处理

5.PWM输出

6.长按功能的实现

三、完整代码演示

main.c

iic.c

iic.h


前言

之前一直吐槽第十四届省赛已经赶上国赛水平了,现在我感觉我错了,还是国赛更难一些。但是也不排除,今年省赛会出前几年国赛考过的知识点,这里还是多给大家分享一些。重复分享某一个知识点的话,确实有些繁琐,所以这里分享十三届决赛代码的同时,更多的还是分享一些前边没有提到的知识点,给出我的处理方法供大家参考,以应对赛场上的各种突发情况。

另外今年的赛点的资料好像已经发了,回头我会在比对一下跟去年的有没有区别,考虑是近两年底层驱动才不给.h文件的,后续我也会分享一下.h文件应该怎么写,底层的.c文件应该怎么修改(前边每一篇文章底层的.c 都是我自己修改过后的,.h文件都是我自己写的)

一、决赛题目

1.比赛题目

2.题目解读

首先,可能会很让人头大的一个点就是,这里不仅仅需要读取超声波,还要读取NE555,这两个就要各占一个定时器资源,以我们之前写的代码来看,定时器资源是不够的。

其次,这里用到的AD和DA,而AD和DA都是通过PCF8591来控制的,之前从来没有遇到过同时控制两路PCF8591的情况。

再次这个题目还要求输出PWM,这个对有单片机基础的人来说不是什么难事,但是要是你根本不知道PWM是什么,那省赛的时候,你绝对难受的要死。

当然,省赛的话绝对不会这么复杂,但也不排除考国赛的某个知识点的可能。下边都会一一介绍

二、功能实现

1.关于定时器资源

1)超声波和NE555需要的定时器资源

之前我们已经介绍过如何读取超声波和NE555了。之前写的代码都是定时器0来完成扫描数码管等其他主要的工作,定时器1用来为超声波计数,或者使用定时器1的外部中断来读取NE555.你如果要问用定时器1同时来为超声波计数和读取NE555可以不可以,那当然不可以,一个需要定时器工作在计时模式,一个需要定时器工作在计数模式,这样使用肯定冲突。

所以他俩每个都需要独占一个定时器资源,我们需要把定时器0分给超声波,用来给超声波计时,定时器1的外部中断模式用于读取NE555.

那数码管还有其他计时工作怎么办呢?其实STC15F2系列单片机还有一个定时器2可以使用

2)定时器2

跟常规的定时器一样,我们可以直接在STC-ISP生成定时器2的初始化函数。

如果你赛点的stc不是新版的,没有使能定时器中断的选项,那你可得提前记一下了,定时器0使能中断使用的是ET0=1;定时器1是ET1 =1;定时器2可不是ET2 =1;而是IE2 |= 0x04;  定时器2的中断号是12。完整的代码如下

void Timer2_Isr(void) interrupt 12
{
}

void Timer2_Init(void)        //1毫秒@12.000MHz
{
    AUXR |= 0x04;            //定时器时钟1T模式
    T2L = 0x20;                //设置定时初始值
    T2H = 0xD1;                //设置定时初始值
    AUXR |= 0x10;            //定时器2开始计时
    IE2 |= 0x04;            //使能定时器2中断
}
 

定时器2的初始化函数跟我们常用的定时器0和1也不太一样,我们也不方便使用定时器2来给超声波计时,所以我一般选择定时器0给超声波计时,定时器2扫描数码管了。

2.单位切换

对于这次的单位切换,我选择采取简单粗暴的方式——把显示不同单位的菜单直接定义成不同的菜单,比如频率界面可以按按键切换单位,我直接定义两个菜单显示不同单位的频率,这样切换单位就变成了切换菜单,简单粗暴。不过这样的话,那就得写7个菜单了,不过还好菜单里面需要显示的东西并不复杂。

3.数据长度不足时,高位熄灭

前几篇文章应该也提到过,这里在简单介绍一下。

定义一个数据为dat,如果它的长度小于3位,也就是dat/1000为0时,那么倒数第三位数码管就熄灭,如果他的长度大于2位,则正常显示dat/100%10;我们可以使用三木运算符来实现,这里以显示菜单1的频率为例:

if(mod==0)//显示频率
{
    Nixie_num[0]=21,//F
    Nixie_num[1]=20,
    Nixie_num[2]=fre/100000>0 ? fre/100000%10 : 20;
    Nixie_num[3]=fre/10000>0 ? fre/10000%10 : 20;
    Nixie_num[4]=fre/1000>0 ? fre/1000%10 : 20;
    Nixie_num[5]=fre/100>0 ? fre/100%10 : 20;
    Nixie_num[6]=fre/10>0 ? fre/10%10 : 20;
    Nixie_num[7]=fre/1>0 ? fre/1%10 : 20;
}
 

当然前提是你得知道什么是三目运算符。

4.AD/DA多通道的处理

首先:为什么要处理呢?

答案:如果你不处理的话,如果要读取两个通道的AD值,那可能读取第二个通道的AD值时读取到的还是第一个通道。

当然上边只是对于多通道处理的一种情况哈,总之就是第二个通道可能出现意想不到的结果。老师咋讲的现在我已经忘完了,都具体读和写的哪些情况组合会出现问题,我也记不清了,但是我咱们可以使用一种一劳永逸的办法,那就是连续读取两次,比如我需要读取通道0和3那我就这样写:


unsigned char AD0;

unsigned char AD1;

AD0=read_pcf(0);AD0=read_pcf(0);

AD1=read_pcf(1);AD1=read_pcf(1);

如果它还读取不到期望的值的话,那我们就在中间加几个Delay。

5.PWM输出

提到PWM,就得顺带着占空比一块提一下,占空比就是上文提到的duty。

PWM是一种常用的信号控制和转换技术。简单来说,PWM通过调节脉冲信号的宽度来模拟一个连续变化的信号,通常用于控制直流(DC)电动机、LED亮度调节、声音的调节以及其他需要精确控制输入信号的应用。

比如高电平电压为5V,低电平电压为0V,我现在定义PWM为1KHz也就是说一个周期(一个高电平+一个低电平)时1ms,如果定义精度为10%,也就是十分之一个周期。如果一个周期的前50%是高电平,后50%是低电平,这样就记为的50%占空比。刚才又说,高电平为5V,低电平为0V,而一个周期有一半时间是高电平,那么此时输出的电压应该是高电平的一半,也就是5V。对于其他占空比同理。

因此,我们可以通过测量电压,来测量占空比是多少。

题目要求输出1KHz的两种占空比的PWM信号,分别为80%占空比和20%占空比的。

我们当然可以使用一个0.1ms的定时器,定时器计数,前八次输出高电平,后两次输出低电平,这样就可以实现80%占空比输出了,20%也同理。

但是,现在我们的定时器资源急缺,唯一一个用来计时的定时器也设置的1ms,而我又不想修改怎么办呢,我们直接延时其实也可以。输出高电平,延时800us,输出低电平,延时200us,同样也输出了80%占空比,1Khz的PWM信号,然后我们再在main的while循环运行这串输出代码即可,当然,main函数里不可以有延时。

至于如何输出高电平,如何输出低电平,其实就跟控制继电器和蜂鸣器一样,毕竟都是ULN芯片控制的,前边也介绍过ULN芯片了,可以看这篇文章中关于继电器开启与关闭的部分

蓝桥杯第十三届电子类单片机组程序设计-CSDN博客

只是开关继电器我们控制的事N RELAY引脚的高低电平,现在我们需要控制N MOTOR引脚的高低电平而已。

输出80%和20%占空比,1KHz信号的代码如下:

#define MOTOR_ON()        ULN|=0x20;    P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
#define MOTOR_OFF()        ULN&=0xDF;    P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;

void Delay800us(void)    //@12.000MHz
{
    unsigned char data i, j;

    i = 10;
    j = 83;
    do
    {
        while (--j);
    } while (--i);
}
void Delay200us(void)    //@12.000MHz
{
    unsigned char data i, j;

    i = 3;
    j = 82;
    do
    {
        while (--j);
    } while (--i);
}


void PWM_out_80(void)//输出duty为80的PWM
{
    MOTOR_ON()
    Delay800us();
    MOTOR_OFF();
    Delay200us();
}
void PWM_out_20(void)//输出duty为20的PWM
{
    MOTOR_ON()
    Delay200us();
    MOTOR_OFF();
    Delay800us();
}

6.长按功能的实现

虽然之前提到过,这里还是介绍一下我个人的思路吧。

题目上要求的是长按1s,那我也以长按1s为例介绍,如何判断长按1s吧。

首先定义一个标志位is_1s,在定时器里检查is_1s,如果is_1s=0,则开始数数(定时器每1s进一次),数够1000了,就让is_1s置为1,如果检测到is_1s已经是1了,则清零数数。注意初始状态下is_1s应为1.

bit is_1s=1;

unsigned int count_1000ms=0;//为长按按键数数

void Timer2_Isr(void) interrupt 12
{
    if(is_1s==0)//1s数数,主要服务于长按S7 1秒
    {
        if(++count_1000ms==1000)
        {
            is_1s=1;
            count_1000ms=0;
        }
    }
    else
    {
        count_1000ms=0;
    }
}

现在就需要我们利用这个is_1s来判断长按是否达到1s了。其实对于长按1s我一直有两种理解,最开始的时候我理解的是长按达到1s后松开,则触发效果,不过现在感觉这样理解的不对,应该是长按1S之后,就算没松开按键,也应该产生长按1s的现象了。我们在按下按键后第一个Delay5ms()消抖之后加上is_1s=0来开始计时,在while(P30==0)里判断is_1s的值,如果is_1s为1了,也就是说从上一次清零(即按下按键之后)到现在,已经过来1s,此时就判定为长按1s了,处理长按1s并跳出while循环。具体代码如下:

P3=0xFF;
if(P30==0)
{
    Delay5ms();
    is_1s=0;//is_1s置为0的1s之后会被定时器置为1,通过检查is_1s就可以判断是否长按了1S
    while(P30==0)
    {

        if(is_1s==1&&mod==2)//如果检测到长按1s了,并且此时处在湿度界面
        {
            write_at(0,0);//则重置计数
            break;
        }
    }
    is_1s=1;
    Delay5ms();
    key_value=7;
}
 

三、完整代码演示

首先,我要说明,因为PWM输出需要持续修改ULN的值,可能会导致数据窜位,进而导致蜂鸣器和继电器条一下。之前在LED处理时就提到过,应该避免重复开关控制某个外设的锁存器,LED也进行了相关处理,但是这个PWM没办法,不得不重复开关它的锁存器。这是代码中没有解决的问题(之一)。

另外,由于LED灯需要100ms闪烁,刚才提到的PWM输出可以放在main的while循环里,但是循环里不能有延时,如果while循环不加延时,全用定时器数数的话,真的太麻烦了。所以我就把while里面的延时留下,不过延时的同时输出PWM了,具体可以看代码。

代码还有很多不足的地方,不过内容基本实现了。

后续还是老老实实做省赛的题目吧......

main.c

  1. #include <stc15.h>
  2. #include <intrins.h>
  3. #include "iic.h"
  4. code unsigned char Seg_Table[] =
  5. {
  6. 0xc0, //0
  7. 0xf9, //1
  8. 0xa4, //2
  9. 0xb0, //3
  10. 0x99, //4
  11. 0x92, //5
  12. 0x82, //6
  13. 0xf8, //7
  14. 0x80, //8
  15. 0x90, //9
  16. 0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10,
  17. 0xFF,//熄灭 20
  18. 0x8E, //F 21
  19. 0x89,//H 22
  20. 0x88, //A 23
  21. 0x8C, //P 24
  22. };
  23. volatile unsigned char Led_Num=0xFF;
  24. volatile unsigned char ULN=0x00;
  25. #define LED_ON(x) Led_Num&=~(0x01<<x);P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
  26. #define LED_OFF(x) Led_Num|=0x01<<x; P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
  27. #define LED_OFF_ALL() Led_Num=0xFF; P0=Led_Num;P2|=0x80;P2&=0x9F;P2&=0x1F;
  28. #define NIXIE_CHECK() P2|=0xC0;P2&=0xDF;P2&=0x1F;
  29. #define NIXIE_ON() P2|=0xE0;P2&=0xFF;P2&=0x1F;
  30. #define MOTOR_ON() ULN|=0x20; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
  31. #define MOTOR_OFF() ULN&=0xDF; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
  32. #define RELAY_ON() ULN|=0x10; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
  33. #define RELAY_OFF() ULN&=0xEF; P0=ULN;P2|=0xA0;P2&=0xBF;P2&=0x1F;
  34. void get_key(void);
  35. void Delay100ms(void); //@12.000MHz
  36. void Delay5ms(void); //@12.000MHz
  37. void Timer0_Init(void);
  38. void Timer1_Init(void); //1毫秒@12.000MHz
  39. void Timer2_Init(void); //1毫秒@12.000MHz
  40. void send_wave(void);
  41. void read_remote(void);
  42. void read_ne555(void);
  43. void show_menu(void);
  44. void run(void);
  45. void PWM_out_80(void);
  46. void PWM_out_20(void);
  47. void Led_run(void);
  48. void relay_run(void);
  49. unsigned char location;
  50. unsigned char key_value=0;
  51. unsigned char Nixie_num[]={20,20,20,20,20,20,20,20};
  52. unsigned char AD=0;
  53. sbit TX=P1^0;
  54. sbit RX=P1^1;
  55. bit is_read_remote=0;
  56. bit is_read_555=0;
  57. unsigned int remote=0;
  58. unsigned int fre=0;
  59. unsigned char mod=0;
  60. unsigned char fre_canshu=90;//频率参数的单位是0.1KHz
  61. unsigned char shidu_canshu=40;
  62. unsigned char remote_canshu=6;
  63. unsigned char shidu=0;
  64. unsigned char count_relay=0;
  65. bit relay_is_on=0;
  66. void main()
  67. {
  68. unsigned char count_100=0;//中间变量,记录100个1ms循环
  69. LED_OFF_ALL();//关闭LED灯
  70. RELAY_OFF();//关闭继电器
  71. count_relay=read_at(0);//读取继电器闭合次数
  72. Delay100ms();
  73. Timer0_Init();
  74. Timer1_Init();
  75. Timer2_Init();
  76. EA=1;
  77. while(1)
  78. {
  79. get_key();//读取按键
  80. run();
  81. while(1)//一个100ms的延时,延时的同时,输出PWM驱动电机
  82. {//迫不得已的做法,主要LED灯闪烁哪里太需要这个100ms的延时了
  83. if(++count_100==100)//100ms后跳出while(1)循环,下边的PWM输出一次刚好1ms
  84. {
  85. count_100=0;
  86. break;
  87. }
  88. //在此延时100ms的循环内,输出PWM,1KHz的PWM周期刚好是1ms
  89. //注意频率参数的单位是0.1KHz
  90. if(fre/100>fre_canshu)//如果频率大于频率参数的话
  91. PWM_out_80();//就输出duty为80的PWM
  92. else if(fre/100<=fre_canshu)//如果频率小于频率参数的话
  93. PWM_out_20();//就输出duty为20的PWM
  94. }
  95. }
  96. }
  97. void run()
  98. {
  99. unsigned char DA=0;//定义一个中间变量,用于记录待输出的DA值
  100. read_remote();//超声波测距
  101. read_ne555();//读取NE555
  102. show_menu();//显示菜单
  103. Led_run();//控制LED灯运行
  104. relay_run();//控制继电器
  105. AD=read_pcf(3);AD=read_pcf(3);//读取电位计,因为下边还要DA输出,这里重复读取两次是为了防止读取不出来数据
  106. shidu=AD*0.3921;//湿度值=AD/255*100;
  107. DA=(shidu-shidu_canshu)*4/(80-shidu_canshu)+1;//根据曲线拟合出的函数
  108. DA=DA>5 ? 5 : DA;//限制输出幅值
  109. DA=DA<1 ? 1 :DA;
  110. write_pcf(DA*51);write_pcf(DA*51);//同上连续输出两次。输出=待输出电压/5*255;
  111. }
  112. bit is_1s=1;
  113. unsigned int count_500ms;
  114. unsigned int count_1s=0;
  115. unsigned int count_1000ms=0;//为长按按键数数
  116. void Timer2_Isr(void) interrupt 12
  117. {
  118. P0=0x01<<location;NIXIE_CHECK();//数码管扫描
  119. P0=Seg_Table[Nixie_num[location]];NIXIE_ON();
  120. if(++location==8)
  121. location=0;
  122. if(is_read_remote==0)//每500ms读取一次超声波
  123. {
  124. if(++count_500ms==500)
  125. {
  126. is_read_remote=1;
  127. count_500ms=0;
  128. }
  129. }
  130. if(is_read_555==0)//每过1s读取一次Ne555
  131. {
  132. if(++count_1s==1000)
  133. {
  134. is_read_555=1;
  135. count_1s=0;
  136. }
  137. }
  138. if(is_1s==0)//1s数数,主要服务于长按S7 1秒
  139. {
  140. if(++count_1000ms==1000)
  141. {
  142. is_1s=1;
  143. count_1000ms=0;
  144. }
  145. }
  146. else
  147. {
  148. count_1000ms=0;
  149. }
  150. }
  151. void Timer0_Init(void)
  152. {
  153. AUXR = 0x80; //定时器0为1T模式
  154. TMOD = 0x04; //设置定时器0为16位自动重装载外部记数模式
  155. TH0 = TL0 = 0x00; //设置定时器0初始值
  156. TR0 = 1; //定时器0开始工作
  157. //ET0 = 1; //开定时器0中断
  158. }
  159. void Timer1_Init(void) //1毫秒@12.000MHz
  160. {
  161. AUXR |= 0x40; //定时器时钟1T模式
  162. TMOD &= 0x0F; //设置定时器模式
  163. TL1 = 0x20; //设置定时初始值
  164. TH1 = 0xD1; //设置定时初始值
  165. TF1 = 0; //清除TF1标志
  166. //TR1 = 1; //定时器1开始计时
  167. }
  168. void Timer2_Init(void) //1毫秒@12.000MHz
  169. {
  170. AUXR |= 0x04; //定时器时钟1T模式
  171. T2L = 0x20; //设置定时初始值
  172. T2H = 0xD1; //设置定时初始值
  173. AUXR |= 0x10; //定时器2开始计时
  174. IE2 |= 0x04; //使能定时器2中断
  175. }
  176. void Delay100ms(void) //@12.000MHz
  177. {
  178. unsigned char data i, j, k;
  179. _nop_();
  180. _nop_();
  181. i = 5;
  182. j = 144;
  183. k = 71;
  184. do
  185. {
  186. do
  187. {
  188. while (--k);
  189. } while (--j);
  190. } while (--i);
  191. }
  192. void Delay5ms(void) //@12.000MHz
  193. {
  194. unsigned char data i, j;
  195. i = 59;
  196. j = 90;
  197. do
  198. {
  199. while (--j);
  200. } while (--i);
  201. }
  202. void get_key(void)
  203. {
  204. unsigned char key_P3=P3;
  205. P3=0xFF;
  206. if(P30==0)
  207. {
  208. Delay5ms();
  209. is_1s=0;//is_1s置为0的1s之后会被定时器置为1,通过检查is_1s就可以判断是否长按了1S
  210. while(P30==0)
  211. {
  212. run();
  213. if(is_1s==1&&mod==2)//如果检测到长按1s了,并且此时处在湿度界面
  214. {
  215. write_at(0,0);//则重置计数
  216. break;
  217. }
  218. }
  219. is_1s=1;
  220. Delay5ms();
  221. key_value=7;
  222. }
  223. else if(P31==0){Delay5ms();while(P31==0){run();}Delay5ms();key_value=6;}
  224. else if(P32==0){Delay5ms();while(P32==0){run();}Delay5ms();key_value=5;}
  225. else if(P33==0){Delay5ms();while(P33==0){run();}Delay5ms();key_value=4;}
  226. //S4菜单切换
  227. if(key_value==4)
  228. {
  229. if(mod==0||mod==1)
  230. mod=2;
  231. else if(mod==2)
  232. mod=3;
  233. else if(mod==3||mod==4)
  234. mod=5;
  235. else if(mod==5||mod==6||mod==7)
  236. mod=0;
  237. }
  238. //S5在三个参数界面之间切换
  239. else if(key_value==5)
  240. {
  241. if(mod==5)
  242. mod=6;
  243. else if(mod==6)
  244. mod=7;
  245. else if(mod==7)
  246. mod=5;
  247. }
  248. //S6 在参数界面:加 在距离界面,切换距离单位
  249. else if(key_value==6)
  250. {
  251. if(mod==5)
  252. fre_canshu=fre_canshu<120 ? fre_canshu+5 : 10;//限幅,下同
  253. else if(mod==6)
  254. shidu_canshu=shidu_canshu<60 ? shidu_canshu+10 : 10;
  255. else if(mod==7)
  256. remote_canshu=remote_canshu<12 ? remote_canshu+1 : 1;
  257. if(mod==3)
  258. mod=4;
  259. else if(mod==4)
  260. mod=3;
  261. }
  262. //S7 在参数界面:减 在频率界面,切换频率单位 长按功能在上边读取按键那里
  263. else if(key_value==7)
  264. {
  265. if(mod==5)
  266. fre_canshu=fre_canshu>10 ? fre_canshu-5 : 120;//限幅
  267. else if(mod==6)
  268. shidu_canshu=shidu_canshu>10 ? shidu_canshu-10 : 60;
  269. else if(mod==7)
  270. remote_canshu=remote_canshu>1 ? remote_canshu-1 : 12;
  271. if(mod==0)
  272. mod=1;
  273. else if(mod==1)
  274. mod=0;
  275. }
  276. key_value=0;
  277. P3=key_P3;
  278. }
  279. void Delay14us(void) //@12.000MHz
  280. {
  281. unsigned char data i;
  282. _nop_();
  283. _nop_();
  284. i = 47;
  285. while (--i);
  286. }
  287. void send_wave(void)
  288. {
  289. unsigned char i=0;
  290. for(;i<8;i++)
  291. {
  292. TX=0;Delay14us();
  293. TX=1;Delay14us();
  294. }
  295. }
  296. void read_remote(void)
  297. {
  298. unsigned int url_t=0;//记录超声波来回的时间,注意没有单位
  299. if(is_read_remote==1)//没过一段时间读取一次超声波,避免连续发送读取时相互干扰
  300. {
  301. is_read_remote=0;
  302. send_wave();//发送超声波
  303. TR1=1;//开始计时
  304. while(RX==1&&TF1==0);//如果检测到返回的超声波或者定时器超时
  305. TR1=0;//停止计时
  306. if(RX==0)//如果检测到了返回的超声波
  307. {//则记录来回的时间
  308. url_t=TH1;
  309. url_t<<=8;
  310. url_t|=TL1;
  311. }
  312. else//如果超声波超时
  313. {
  314. url_t=0;
  315. }
  316. //实际的时间=url_t/12000000秒
  317. //实际的距离=(url_t/12000000)*340*100/2 厘米
  318. remote=(unsigned int)(url_t*0.001417);
  319. url_t=0;//为下次读取超声波,清零所有数据。下同
  320. TL1=0;TH1=0;
  321. TF1=0;
  322. }
  323. }
  324. void read_ne555(void)
  325. {
  326. if(is_read_555==1)//每隔1s读取一次NE555,读出来的数据就刚好是频率
  327. {
  328. is_read_555=0;
  329. TR0=0;
  330. fre=TH0;//读取频率
  331. fre<<=8;
  332. fre|=TL0;
  333. TH0=0;//清零相关数据
  334. TL0=0;
  335. TF0=0;
  336. TR0=1;
  337. }
  338. }
  339. void show_menu(void)
  340. {
  341. if(mod==0)//显示频率
  342. {
  343. Nixie_num[0]=21,//F
  344. Nixie_num[1]=20,
  345. Nixie_num[2]=fre/100000>0 ? fre/100000%10 : 20;
  346. Nixie_num[3]=fre/10000>0 ? fre/10000%10 : 20;
  347. Nixie_num[4]=fre/1000>0 ? fre/1000%10 : 20;
  348. Nixie_num[5]=fre/100>0 ? fre/100%10 : 20;
  349. Nixie_num[6]=fre/10>0 ? fre/10%10 : 20;
  350. Nixie_num[7]=fre/1>0 ? fre/1%10 : 20;
  351. }
  352. else if(mod==1)//显示频率,单位KHz
  353. {
  354. Nixie_num[0]=21,
  355. Nixie_num[1]=20,
  356. Nixie_num[2]=fre/10000000>0 ? fre/10000000%10 : 20;
  357. Nixie_num[3]=fre/1000000>0 ? fre/1000000%10 : 20;
  358. Nixie_num[4]=fre/100000>0 ? fre/100000%10 : 20;
  359. Nixie_num[5]=fre/10000>0 ? fre/10000%10 : 20;
  360. Nixie_num[6]=fre/1000>0 ? fre/1000%10+10 : 10;//显示带小数点的数字
  361. Nixie_num[7]=fre/100>0 ? fre/100%10 : 0;
  362. }
  363. else if(mod==2)//显示湿度
  364. {
  365. Nixie_num[0]=22,
  366. Nixie_num[1]=20,
  367. Nixie_num[2]=20;
  368. Nixie_num[3]=20;
  369. Nixie_num[4]=20;
  370. Nixie_num[5]=20;
  371. Nixie_num[6]=shidu/10>0 ? shidu/10%10 : 20;
  372. Nixie_num[7]=shidu/1>0 ? shidu/1%10 : 0;;
  373. }
  374. else if(mod==3)//显示距离
  375. {
  376. Nixie_num[0]=23,
  377. Nixie_num[1]=20,
  378. Nixie_num[2]=20;
  379. Nixie_num[3]=20;
  380. Nixie_num[4]=20;
  381. Nixie_num[5]=remote/100>0 ? remote/100%10 : 20;
  382. Nixie_num[6]=remote/10>0 ? remote/10%10 : 20;
  383. Nixie_num[7]=remote/1>0 ? remote/1%10 : 0;
  384. }
  385. else if(mod==4)//显示距离,单位m
  386. {
  387. Nixie_num[0]=23,
  388. Nixie_num[1]=20,
  389. Nixie_num[2]=20;
  390. Nixie_num[3]=20;
  391. Nixie_num[4]=20;
  392. Nixie_num[5]=remote/100>0 ? remote/100%10+10 : 10;
  393. Nixie_num[6]=remote/10>0 ? remote/10%10 : 0;
  394. Nixie_num[7]=remote/1>0 ? remote/1%10 : 0;
  395. }
  396. else if(mod==5)//显示频率参数
  397. {
  398. Nixie_num[0]=24,
  399. Nixie_num[1]=1,
  400. Nixie_num[2]=20;
  401. Nixie_num[3]=20;
  402. Nixie_num[4]=20;
  403. Nixie_num[5]=fre_canshu/100>0 ? fre_canshu/100%10 : 20;
  404. Nixie_num[6]=fre_canshu/10>0 ? fre_canshu/10%10+10 : 10;
  405. Nixie_num[7]=fre_canshu/1>0 ? fre_canshu/1%10 : 0;
  406. }
  407. else if(mod==6)//显示湿度参数
  408. {
  409. Nixie_num[0]=24,
  410. Nixie_num[1]=2,
  411. Nixie_num[2]=20;
  412. Nixie_num[3]=20;
  413. Nixie_num[4]=20;
  414. Nixie_num[5]=20;
  415. Nixie_num[6]=shidu_canshu/10%10;
  416. Nixie_num[7]=shidu_canshu/1%10;
  417. }
  418. else if(mod==7)//显示距离参数
  419. {
  420. Nixie_num[0]=24,
  421. Nixie_num[1]=3,
  422. Nixie_num[2]=20;
  423. Nixie_num[3]=20;
  424. Nixie_num[4]=20;
  425. Nixie_num[5]=20;
  426. Nixie_num[6]=remote_canshu/10%10+10;
  427. Nixie_num[7]=remote_canshu/1%10;
  428. }
  429. }
  430. void Delay800us(void) //@12.000MHz
  431. {
  432. unsigned char data i, j;
  433. i = 10;
  434. j = 83;
  435. do
  436. {
  437. while (--j);
  438. } while (--i);
  439. }
  440. void Delay200us(void) //@12.000MHz
  441. {
  442. unsigned char data i, j;
  443. i = 3;
  444. j = 82;
  445. do
  446. {
  447. while (--j);
  448. } while (--i);
  449. }
  450. void PWM_out_80(void)//输出duty为80的PWM
  451. {
  452. MOTOR_ON()
  453. Delay800us();
  454. MOTOR_OFF();
  455. Delay200us();
  456. }
  457. void PWM_out_20(void)//输出duty为20的PWM
  458. {
  459. MOTOR_ON()
  460. Delay200us();
  461. MOTOR_OFF();
  462. Delay800us();
  463. }
  464. void Led_run(void)
  465. {
  466. static bit L1_is_on=0;
  467. static bit L2_is_on=0;
  468. static bit L3_is_on=0;
  469. static bit L4_is_on=0;
  470. static bit L5_is_on=0;
  471. static bit L6_is_on=0;
  472. //配合主函数里的100ms延时,即可达到每次运行Led_run切换一次灯的状态,完成闪烁
  473. if(mod==0||mod==1)//在频率界面,L1闪烁
  474. {
  475. if(L1_is_on==0)//如果L1没有点亮,则点亮
  476. {
  477. LED_ON(0);
  478. L1_is_on=1;
  479. }
  480. else if(L1_is_on==1)//否则熄灭。
  481. {
  482. LED_OFF(0);
  483. L1_is_on=0;
  484. }
  485. }
  486. else if(mod==2)//在湿度界面,L2闪烁
  487. {
  488. if(L2_is_on==0)
  489. {
  490. LED_ON(1);
  491. L2_is_on=1;
  492. }
  493. else if(L2_is_on==1)
  494. {
  495. LED_OFF(1);
  496. L2_is_on=0;
  497. }
  498. }
  499. else if(mod==3||mod==4)//在距离界面,L3闪烁
  500. {
  501. if(L3_is_on==0)
  502. {
  503. LED_ON(2);
  504. L3_is_on=1;
  505. }
  506. else if(L3_is_on==1)
  507. {
  508. LED_OFF(2);
  509. L3_is_on=0;
  510. }
  511. }
  512. //下面为退出某个模式,但是刚好闪烁到LED点亮的状态,则关闭不该点亮的LED
  513. if(!(mod==0||mod==1)&&L1_is_on==1)//如果不在频率界面,并且L1点亮了
  514. {//则熄灭。。下边都一样
  515. LED_OFF(0);
  516. L1_is_on=0;
  517. }
  518. else if(!(mod==2)&&L2_is_on==1)
  519. {
  520. LED_OFF(1);
  521. L2_is_on=0;
  522. }
  523. else if(!(mod==3||mod==4)&&L3_is_on==1)
  524. {
  525. LED_OFF(2);
  526. L3_is_on=0;
  527. }
  528. //如果频率大于频率参数,并且L4没有点亮,
  529. if(fre/100>fre_canshu&&L4_is_on==0)//注意频率参数的单位是0.1KHz
  530. {//则点亮
  531. LED_ON(3);
  532. L4_is_on=1;
  533. }
  534. else if((!(fre/100>fre_canshu))&&L4_is_on==1)//如果频率小于频率参数,并且L4还没西梅
  535. {//则熄灭。。下同
  536. LED_OFF(3);
  537. L4_is_on=0;
  538. }
  539. if(shidu>shidu_canshu&&L5_is_on==0)//
  540. {
  541. LED_ON(4);
  542. L5_is_on=1;
  543. }
  544. else if(!(shidu>shidu_canshu)&&L5_is_on==1)
  545. {
  546. LED_OFF(4);
  547. L5_is_on=0;
  548. }
  549. if(remote/10>remote_canshu&&L6_is_on==0)
  550. {
  551. LED_ON(5);
  552. L6_is_on=1;
  553. }
  554. else if(!(remote/10>remote_canshu)&&L6_is_on==1)
  555. {
  556. LED_OFF(5);
  557. L6_is_on=0;
  558. }
  559. }
  560. void relay_run(void)
  561. {
  562. if((remote/10>=remote_canshu)&&(relay_is_on==0))//如果距离大于距离参数,并且继电器没有打开
  563. {//则打开继电器
  564. relay_is_on=1;
  565. RELAY_ON();
  566. write_at(0,++count_relay);
  567. }
  568. else if((!(remote/10>remote_canshu))&&(relay_is_on==1))//如果距离小于距离参数,并且继电器打开了
  569. {//则熄灭
  570. relay_is_on=0;
  571. RELAY_OFF();
  572. }
  573. }

iic.c

  1. /* # I2C代码片段说明
  2. 1. 本文件夹中提供的驱动代码供参赛选手完成程序设计参考。
  3. 2. 参赛选手可以自行编写相关代码或以该代码为基础,根据所选单片机类型、运行速度和试题
  4. 中对单片机时钟频率的要求,进行代码调试和修改。
  5. */
  6. #include <stc15.h>
  7. #include <intrins.h>
  8. #include "iic.h"
  9. sbit sda=P2^1;
  10. sbit scl=P2^0;
  11. #define DELAY_TIME 5
  12. //
  13. static void I2C_Delay(unsigned char n)
  14. {
  15. do
  16. {
  17. _nop_();_nop_();_nop_();_nop_();_nop_();
  18. _nop_();_nop_();_nop_();_nop_();_nop_();
  19. _nop_();_nop_();_nop_();_nop_();_nop_();
  20. }
  21. while(n--);
  22. }
  23. //
  24. void I2CStart(void)
  25. {
  26. sda = 1;
  27. scl = 1;
  28. I2C_Delay(DELAY_TIME);
  29. sda = 0;
  30. I2C_Delay(DELAY_TIME);
  31. scl = 0;
  32. }
  33. //
  34. void I2CStop(void)
  35. {
  36. sda = 0;
  37. scl = 1;
  38. I2C_Delay(DELAY_TIME);
  39. sda = 1;
  40. I2C_Delay(DELAY_TIME);
  41. }
  42. //
  43. void I2CSendByte(unsigned char byt)
  44. {
  45. unsigned char i;
  46. for(i=0; i<8; i++){
  47. scl = 0;
  48. I2C_Delay(DELAY_TIME);
  49. if(byt & 0x80){
  50. sda = 1;
  51. }
  52. else{
  53. sda = 0;
  54. }
  55. I2C_Delay(DELAY_TIME);
  56. scl = 1;
  57. byt <<= 1;
  58. I2C_Delay(DELAY_TIME);
  59. }
  60. scl = 0;
  61. }
  62. //
  63. unsigned char I2CReceiveByte(void)
  64. {
  65. unsigned char da;
  66. unsigned char i;
  67. for(i=0;i<8;i++){
  68. scl = 1;
  69. I2C_Delay(DELAY_TIME);
  70. da <<= 1;
  71. if(sda)
  72. da |= 0x01;
  73. scl = 0;
  74. I2C_Delay(DELAY_TIME);
  75. }
  76. return da;
  77. }
  78. //
  79. unsigned char I2CWaitAck(void)
  80. {
  81. unsigned char ackbit;
  82. scl = 1;
  83. I2C_Delay(DELAY_TIME);
  84. ackbit = sda;
  85. scl = 0;
  86. I2C_Delay(DELAY_TIME);
  87. return ackbit;
  88. }
  89. //
  90. void I2CSendAck(unsigned char ackbit)
  91. {
  92. scl = 0;
  93. sda = ackbit;
  94. I2C_Delay(DELAY_TIME);
  95. scl = 1;
  96. I2C_Delay(DELAY_TIME);
  97. scl = 0;
  98. sda = 1;
  99. I2C_Delay(DELAY_TIME);
  100. }
  101. void write_pcf(unsigned char add)
  102. {
  103. I2CStart();
  104. I2CSendByte(0x90);
  105. I2CWaitAck();
  106. I2CSendByte(0x40);
  107. I2CWaitAck();
  108. I2CSendByte(add);
  109. I2CWaitAck();
  110. I2CStop();
  111. }
  112. unsigned char read_pcf(unsigned char add)
  113. {
  114. unsigned char ad=0;
  115. I2CStart();
  116. I2CSendByte(0x90);
  117. I2CWaitAck();
  118. I2CSendByte(add);
  119. I2CWaitAck();
  120. I2CStop();
  121. I2CStart();
  122. I2CSendByte(0x91);
  123. I2CWaitAck();
  124. ad=I2CReceiveByte();
  125. I2CSendAck(1);
  126. I2CStop();
  127. return ad;
  128. }
  129. void write_at(unsigned char add,dat)
  130. {
  131. I2CStart();
  132. I2CSendByte(0xA0);
  133. I2CWaitAck();
  134. I2CSendByte(add);
  135. I2CWaitAck();
  136. I2CSendByte(dat);
  137. I2CWaitAck();
  138. I2CStop();
  139. }
  140. unsigned char read_at(unsigned char add)
  141. {
  142. unsigned char at=0;
  143. I2CStart();
  144. I2CSendByte(0xA0);
  145. I2CWaitAck();
  146. I2CSendByte(add);
  147. I2CWaitAck();
  148. I2CStop();
  149. I2CStart();
  150. I2CSendByte(0xA1);
  151. I2CWaitAck();
  152. at=I2CReceiveByte();
  153. I2CSendAck(1);
  154. I2CStop();
  155. return at;
  156. }

iic.h

  1. #ifndef _IIC_H_
  2. #define _IIC_H_
  3. void write_pcf(unsigned char add);
  4. unsigned char read_pcf(unsigned char add);
  5. void write_at(unsigned char add,dat);
  6. unsigned char read_at(unsigned char add);
  7. #endif

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号