当前位置:   article > 正文

蓝桥杯单片机第十届决赛_蓝桥杯单片机第十届省赛难吗

蓝桥杯单片机第十届省赛难吗

感受:难,比起第8 ,第9,第11套决赛来说,出题人无所不用其极。它也得到了我的尊重,我特地写篇文章总结总结。

难点在于:

(1)功能要求较多:

代码比其他的赛题要多,细节也更多,刚开始写怀疑按键和串口会冲突,试着用一个锁去调度公用的P30和P31资源,结果根本不需要,因为按键只用到了13,12,16,17,刚好避开了P30和P31,所以按键的动态扫描只针对这四列就行。

(2)资源占用多

(3)对外设的细节把握较多

解题步骤:

数码管动态刷新

按键动态扫描(只扫描按键所需的4行,就屏蔽了P30和P31)

led动态刷新

按键长按短按判断(状态机实现)

外设定时使用(超声波,温度,epprom)

串口消息收发(状态机加串口中断)

全工程如下

main函数如下

  1. #include "sys.h"
  2. u8 s=0;
  3. u8 keytime=0;
  4. u8 smgtime=0;
  5. u16 temp;//温度
  6. u8 ctemp=30;//温度参数
  7. u8 dis=0;//距离
  8. u8 cdis=35;//距离参数
  9. u16 cge=0;//参数变动次数
  10. u8 cmode=1;//参数编号 1 :温度数据 2:距离数据
  11. // 3:温度参数 4:距离参数 5:变更次数
  12. u16 tempt=0;//读取温度间隔时间
  13. bit uic=0;
  14. u16 dist=0;//读取距离间隔时间
  15. u8 longf=0;
  16. u16 longt=0;
  17. bit cdac=0; // 0:dac输出 1:dac关闭
  18. u8 ledflush=0;
  19. u16 dact=0;//dac输出时间
  20. u8 savef=0;//参数变更保存到epprom标志
  21. u8 bstate=0;//串口指令状态
  22. u8 xdata sendbuf[18]={'\0'};
  23. //dac电压输出
  24. void DacOut(){
  25. if(cdac){
  26. Pcf_W_Dac(20);
  27. }
  28. else{
  29. if(dis<=cdis) Pcf_W_Dac(102);
  30. else
  31. Pcf_W_Dac(204);
  32. }
  33. }
  34. //判断LED的状态
  35. void Judgement(){
  36. if(temp<(ctemp*100))LEDDT[0]=0;
  37. else LEDDT[0]=1;
  38. if(dis<cdis)LEDDT[1]=2;
  39. else LEDDT[1]=0;
  40. if(!cdac)LEDDT[2]=3;
  41. else LEDDT[2]=0;
  42. }
  43. //判断按键长按与短按状态
  44. void los(){
  45. static int keys=0;
  46. if(key_v==12)keys=12;
  47. else if(key_v==13)keys=13;
  48. if(!keys)return;
  49. switch(longf){
  50. case 0:
  51. longt=0;
  52. if(key_v==keys)longf=1;
  53. break;
  54. case 1:
  55. if(key_v==keys){
  56. if(longt++>1000){//达到长按临界值执行长按的瞬间性操作
  57. if(keys==12)cge=0;//长按12清0
  58. else if(keys==13)cdac=~cdac;
  59. longf=2;
  60. }
  61. }
  62. //按键松开
  63. else {
  64. if(longt<=1000){ //执行短按
  65. if(keys==12){
  66. if(!uic){//数据界面显示
  67. if(cmode==1)cmode=2;
  68. else if(cmode==2)cmode=5;
  69. else cmode=1;
  70. }
  71. else {//参数界面切换
  72. if(cmode==3)cmode=4;
  73. else cmode=3;
  74. }
  75. }
  76. else if(keys==13){
  77. if(!uic){
  78. uic=1;
  79. cmode=3;
  80. }
  81. else {
  82. uic=0;
  83. cmode=1;
  84. }
  85. }
  86. keys=0;
  87. longf=0;
  88. }
  89. }
  90. break;
  91. case 2:
  92. if(key_v==keys){//执行长按的连续性操作
  93. //没有操作空
  94. }
  95. else {
  96. longf=0;
  97. keys=0;
  98. }
  99. break;
  100. }
  101. }
  102. void UISetting(){
  103. if(cmode==1){//温度数据显示
  104. DT[0]=21;
  105. DT[1]=10;
  106. DT[2]=10;
  107. DT[3]=10;
  108. DT[4]=temp/1000%10;
  109. DT[5]=temp/100%10+11;
  110. DT[6]=temp/10%10;
  111. DT[7]=temp%10;
  112. }
  113. else if(cmode==2){//距离数据显示
  114. DT[0]=22;
  115. DT[1]=10;
  116. DT[2]=10;
  117. DT[3]=10;
  118. DT[4]=10;
  119. DT[5]=10;
  120. DT[6]=dis/10%10;
  121. DT[7]=dis%10;
  122. }
  123. else if(cmode==3){//温度参数显示
  124. DT[0]=24;
  125. DT[1]=10;
  126. DT[2]=10;
  127. DT[3]=1;
  128. DT[4]=10;
  129. DT[5]=10;
  130. DT[6]=ctemp/10%10;
  131. DT[7]=ctemp%10;
  132. }
  133. else if(cmode==4){//距离参数显示
  134. DT[0]=24;
  135. DT[1]=10;
  136. DT[2]=10;
  137. DT[3]=2;
  138. DT[4]=10;
  139. DT[5]=10;
  140. DT[6]=cdis/10%10;
  141. DT[7]=cdis%10;
  142. }
  143. else {//变更次数显示
  144. DT[0]=23;
  145. DT[1]=10;
  146. DT[2]=10;
  147. DT[3]=cge>=10000?cge/10000:10;
  148. DT[4]=cge>=1000?cge/1000%10:10;
  149. DT[5]=cge>=100?cge/100%10:10;
  150. DT[6]=cge>=10?cge/10%10:10;
  151. DT[7]=cge%10;
  152. }
  153. }
  154. void KeyConsole(){
  155. if(keytime>8){
  156. keytime=0;
  157. key_scan();
  158. if(rkey){
  159. switch(rkey){
  160. case 16:
  161. //参数次数变动
  162. if(cmode==3){
  163. cge++;
  164. if(ctemp==0)return;
  165. ctemp-=2;
  166. }
  167. else if(cmode==4){
  168. cge++;
  169. if(cdis==0)return;
  170. cdis-=5;
  171. }
  172. savef=1;
  173. break;
  174. case 17:
  175. if(cmode==3){
  176. ctemp+=2;
  177. cge++;
  178. }
  179. else if(cmode==4){
  180. cdis+=5;
  181. cge++;
  182. }
  183. savef=1;
  184. break;
  185. default:
  186. break;
  187. }
  188. }
  189. }
  190. }
  191. void main(){
  192. UartInit();//定时器2的串口初始化
  193. Timer1Init();
  194. sys_init();
  195. Timer0Init();
  196. while(1){
  197. //按键扫描
  198. KeyConsole();
  199. //界面显示
  200. if(smgtime>48){
  201. smgtime=0;
  202. UISetting();
  203. Judgement();
  204. if(savef){
  205. if(savef==1){//将改变次数保存到epprom ,分两次保存
  206. EppromW(0,cge/256);
  207. savef=2;
  208. }
  209. else if(savef==2){
  210. EppromW(1,cge%256);
  211. savef=0;
  212. }
  213. }
  214. }
  215. if(dact>479){
  216. dact=0;
  217. DacOut();
  218. }
  219. //测温
  220. if(tempt>540){
  221. //
  222. tempt=0;
  223. temp=ReadTemp()*100;
  224. }
  225. //超声波测距
  226. if(dist>=898){
  227. dist=0;
  228. dis=get_dis();
  229. }
  230. }
  231. }
  232. void TIME1() interrupt 3{//
  233. smg_play(DT[s],s++);
  234. if(s>7)s=0;
  235. smgtime++;
  236. keytime++;
  237. dist++;
  238. tempt++;
  239. dact++;
  240. los();
  241. if(ledflush++==10){
  242. ledflush=0;
  243. LedRunning();
  244. }
  245. }
  246. void UART1() interrupt 4{
  247. ReceiveDate();
  248. }
  249. void ReceiveDate(){
  250. u8 rec;
  251. if(RI==1){ //每次接受完字符后判断
  252. RI=0;
  253. rec=SBUF;
  254. switch(bstate){
  255. case 0:
  256. if(rec=='S')bstate=1;
  257. else if(rec=='P')bstate=5;
  258. else bstate=13;
  259. break;
  260. case 1:
  261. if(rec=='T')bstate=2;
  262. else bstate=13;
  263. break;
  264. case 2:
  265. if(rec=='\r')bstate=3;
  266. else bstate=13;
  267. break;
  268. case 3:
  269. if(rec=='\n')bstate=4;
  270. else bstate=13;
  271. break;
  272. case 5:
  273. if(rec=='A')bstate=6;
  274. else bstate=13;
  275. break;
  276. case 6:
  277. if(rec=='R')bstate=7;
  278. else bstate=13;
  279. break;
  280. case 7:
  281. if(rec=='A')bstate=8;
  282. else bstate=13;
  283. break;
  284. case 8:
  285. if(rec=='\r')bstate=9;
  286. else bstate=13;
  287. break;
  288. case 9:
  289. if(rec=='\n')bstate=10;
  290. else bstate=13;
  291. break;
  292. }
  293. if(bstate==4){//查询数据指令
  294. sendbuf[0]='$';
  295. sendbuf[1]=dis/10%10+48;
  296. sendbuf[2]=dis%10+48;
  297. sendbuf[3]=',';
  298. sendbuf[4]=temp/1000%10+48;
  299. sendbuf[5]=temp/100%10+48;
  300. sendbuf[6]='.';
  301. sendbuf[7]=temp/10%10+48;
  302. sendbuf[8]=temp%10+48;
  303. sendbuf[9]='\r';
  304. sendbuf[10]='\n';
  305. sendbuf[11]='\0';
  306. SendString(sendbuf);
  307. bstate=0;
  308. }
  309. //查询参数指令
  310. //返回当前的距离参数和温度参数
  311. else if(bstate==10){
  312. sendbuf[0]='#';
  313. sendbuf[1]=cdis/10%10+48;
  314. sendbuf[2]=cdis%10+48;
  315. sendbuf[3]=',';
  316. sendbuf[4]=ctemp/10%10+48;
  317. sendbuf[5]=ctemp%10+48;
  318. sendbuf[6]='\r';
  319. sendbuf[7]='\n';
  320. sendbuf[8]='\0';
  321. SendString(sendbuf);
  322. bstate=0;
  323. }
  324. if(bstate==13){
  325. bstate=0;
  326. //发送错误
  327. SendString("ERROR\r\n");
  328. }
  329. }
  330. }

sys.c

  1. #include "sys.h"
  2. u8 key_state=0;
  3. u8 rkey=0;
  4. u8 key_v=0;
  5. u8 DT[]={10,10,10,10,10,10,10,10};
  6. u8 code SMGINDEX[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,
  7. 0xc0&0x7f,0xf9&0x7f,0xa4&0x7f,0xb0&0x7f,0x99&0x7f,0x92&0x7f,
  8. 0x82&0x7f,0xf8&0x7f,0x80&0x7f,0x90&0x7f,
  9. 0xc6,0xc7,0xc8,0x8c //C L n p
  10. };
  11. u8 code LED[]={0xff,0xfe,0xfd,0xfb};
  12. u8 LEDDT[]={0,0,0};
  13. void LedRunning(){
  14. P0=0xff;
  15. P0=LED[LEDDT[0]]&LED[LEDDT[1]]&LED[LEDDT[2]];
  16. lock(4);
  17. }
  18. void sys_init(){
  19. P0=0xff;
  20. lock(4);
  21. P0=0;
  22. lock(5);
  23. P0=0;
  24. lock(6);
  25. P0=0xff;
  26. lock(7);
  27. }
  28. void smg_play(u8 du,u8 we){
  29. P0=0xff;
  30. lock(7); //消影
  31. P0=0;
  32. P0=1<<we;
  33. lock(6);
  34. P0=0xff;
  35. P0=SMGINDEX[du];
  36. lock(7);
  37. }
  38. void key_scan(){
  39. switch(key_state){
  40. case 0:
  41. key_v=0;
  42. P32=1;
  43. P33=1;
  44. P34=0;
  45. P35=0;
  46. if(P33!=1||P32!=1)key_state=1;
  47. break;
  48. case 1:
  49. P32=1;
  50. P33=1;
  51. P34=0;
  52. P35=0;
  53. if(P32!=1||P33!=1){
  54. if(!P32)key_v=5;
  55. else if(!P33)key_v=4;
  56. P34=1;
  57. P35=1;
  58. P32=0;
  59. P33=0;
  60. if(!P34)key_v+=12;
  61. else if(!P35)key_v+=8;
  62. rkey=key_v;
  63. key_state=2;
  64. }
  65. else
  66. key_state=0;
  67. break;
  68. case 2:
  69. P32=1;
  70. P33=1;
  71. P34=0;
  72. P35=0;
  73. rkey=0;
  74. if(P32==1&&P33==1)key_state=0;
  75. break;
  76. default:
  77. break;
  78. }
  79. }
  80. void Timer1Init(void) //1毫秒@12.000MHz
  81. {
  82. AUXR &= 0xBF; //定时器时钟12T模式
  83. TMOD &= 0x0F; //设置定时器模式
  84. TL1 = 0x18; //设置定时初始值
  85. TH1 = 0xFC; //设置定时初始值
  86. TF1 = 0; //清除TF1标志
  87. TR1 = 1; //定时器1开始计时
  88. EA=1;
  89. ET1=1;
  90. }

sys.h

  1. #ifndef __SYS_H_
  2. #define __SYS_H_
  3. #include "mystc.h"
  4. #define lock(x) P2=P2&0x1f|(x<<5); P2=P2&0x1f;
  5. typedef unsigned char u8;
  6. typedef unsigned int u16;
  7. void sys_init();
  8. void smg_play(u8 du,u8 we);
  9. void key_scan();
  10. void Timer1Init(void);
  11. float ReadTemp();
  12. u16 get_dis();
  13. void Timer0Init(void);
  14. void LedRunning();
  15. void Pcf_W_Dac(u8 dat);
  16. void EppromW(u8 word,u8 dat);
  17. void SendString(u8 *str);
  18. void Send_char(u8 q);//发送单个字符
  19. void UartInit(void);
  20. u8 DT[];
  21. void ReceiveDate();
  22. u8 LEDDT[];
  23. extern u8 rkey;
  24. extern u8 key_v;
  25. #endif

其他的是外设驱动文件,需要用的函数都在sys.h里进行声明就行

onewire.c

  1. /*
  2. 程序说明: 单总线驱动程序
  3. 软件环境: Keil uVision 4.10
  4. 硬件环境: CT107单片机综合实训平台(外部晶振12MHz) STC89C52RC单片机
  5. 日 期: 2011-8-9
  6. */
  7. #include "sys.h"
  8. sbit DQ = P1^4; //单总线接口
  9. //单总线延时函数
  10. void Delay_OneWire(unsigned int t) //STC89C52RC
  11. {
  12. u8 i=0;
  13. while(t--){
  14. for(i=0;i<10;i++);
  15. }
  16. }
  17. //通过单总线向DS18B20写一个字节
  18. void Write_DS18B20(unsigned char dat)
  19. {
  20. unsigned char i;
  21. for(i=0;i<8;i++)
  22. {
  23. DQ = 0;
  24. DQ = dat&0x01;
  25. Delay_OneWire(5);
  26. DQ = 1;
  27. dat >>= 1;
  28. }
  29. Delay_OneWire(5);
  30. }
  31. //从DS18B20读取一个字节
  32. unsigned char Read_DS18B20(void)
  33. {
  34. unsigned char i;
  35. unsigned char dat;
  36. for(i=0;i<8;i++)
  37. {
  38. DQ = 0;
  39. dat >>= 1;
  40. DQ = 1;
  41. if(DQ)
  42. {
  43. dat |= 0x80;
  44. }
  45. Delay_OneWire(5);
  46. }
  47. return dat;
  48. }
  49. //DS18B20设备初始化
  50. bit init_ds18b20(void)
  51. {
  52. bit initflag = 0;
  53. DQ = 1;
  54. Delay_OneWire(12);
  55. DQ = 0;
  56. Delay_OneWire(80);
  57. DQ = 1;
  58. Delay_OneWire(10);
  59. initflag = DQ;
  60. Delay_OneWire(5);
  61. return initflag;
  62. }
  63. float ReadTemp(){
  64. u8 low,high;
  65. u16 temp=0;
  66. init_ds18b20();
  67. Write_DS18B20(0xcc);
  68. Write_DS18B20(0x44);
  69. init_ds18b20();
  70. Write_DS18B20(0xcc);
  71. Write_DS18B20(0xbe);
  72. low=Read_DS18B20();
  73. high=Read_DS18B20();
  74. temp=high<<8|low;
  75. return temp*0.0625;
  76. }

sonnic.c(用定时器1进行定时算超声波距离)

  1. #include "sys.h"
  2. #include "intrins.h"
  3. //用定时器0来计算时间
  4. sbit RX=P1^1;
  5. sbit TX=P1^0;
  6. void Delay12us() //@12.000MHz
  7. {
  8. unsigned char i;
  9. _nop_();
  10. _nop_();
  11. i = 33;
  12. while (--i);
  13. }
  14. void Timer0Init(void) //100微秒@12.000HZ 定时器0做超声波计时器
  15. {
  16. AUXR &= 0x7F; //定时器时钟12T模式
  17. TMOD &= 0xF0; //设置定时器模式
  18. TL0 = 0x00; //设置定时初始值
  19. TH0 = 0x00; //设置定时初始值
  20. TF0 = 0; //清除TF0标志
  21. TR0 = 0; //定时器0开始计时
  22. }
  23. void csb_start(){
  24. u8 i=8;
  25. EA=0;
  26. while(i--){
  27. TX=1;
  28. Delay12us();
  29. TX=0;
  30. Delay12us();
  31. }
  32. EA=1;
  33. }
  34. u16 get_dis(){
  35. u16 dis;
  36. RX=1;
  37. csb_start();
  38. TR0=1;
  39. while(RX==1&&TF0==0);
  40. TR0=0;
  41. if(!TF0){
  42. dis=TH0<<8|TL0;
  43. dis=dis*0.017;
  44. if(dis<=2||dis>=400)dis=999;
  45. }
  46. else {
  47. dis=999;
  48. }
  49. TH0=TL0=0;
  50. return dis;
  51. }

iic.c

  1. /*
  2. 程序说明: IIC总线驱动程序
  3. 软件环境: Keil uVision 4.10
  4. 硬件环境: CT107单片机综合实训平台 8051,12MHz
  5. 日 期: 2011-8-9
  6. */
  7. #include "sys.h"
  8. #include "intrins.h"
  9. #define DELAY_TIME 5
  10. #define SlaveAddrW 0xA0
  11. #define SlaveAddrR 0xA1
  12. //总线引脚定义
  13. sbit SDA = P2^1; /* 数据线 */
  14. sbit SCL = P2^0; /* 时钟线 */
  15. void IIC_Delay(unsigned char i)
  16. {
  17. do{_nop_();}
  18. while(i--);
  19. }
  20. //总线启动条件
  21. void IIC_Start(void)
  22. {
  23. SDA = 1;
  24. SCL = 1;
  25. IIC_Delay(DELAY_TIME);
  26. SDA = 0;
  27. IIC_Delay(DELAY_TIME);
  28. SCL = 0;
  29. }
  30. //总线停止条件
  31. void IIC_Stop(void)
  32. {
  33. SDA = 0;
  34. SCL = 1;
  35. IIC_Delay(DELAY_TIME);
  36. SDA = 1;
  37. IIC_Delay(DELAY_TIME);
  38. }
  39. //发送应答
  40. void IIC_SendAck(bit ackbit)
  41. {
  42. SCL = 0;
  43. SDA = ackbit; // 0:应答,1:非应答
  44. IIC_Delay(DELAY_TIME);
  45. SCL = 1;
  46. IIC_Delay(DELAY_TIME);
  47. SCL = 0;
  48. SDA = 1;
  49. IIC_Delay(DELAY_TIME);
  50. }
  51. //等待应答
  52. bit IIC_WaitAck(void)
  53. {
  54. bit ackbit;
  55. SCL = 1;
  56. IIC_Delay(DELAY_TIME);
  57. ackbit = SDA;
  58. SCL = 0;
  59. IIC_Delay(DELAY_TIME);
  60. return ackbit;
  61. }
  62. //通过I2C总线发送数据
  63. void IIC_SendByte(unsigned char byt)
  64. {
  65. unsigned char i;
  66. for(i=0; i<8; i++)
  67. {
  68. SCL = 0;
  69. IIC_Delay(DELAY_TIME);
  70. if(byt & 0x80) SDA = 1;
  71. else SDA = 0;
  72. IIC_Delay(DELAY_TIME);
  73. SCL = 1;
  74. byt <<= 1;
  75. IIC_Delay(DELAY_TIME);
  76. }
  77. SCL = 0;
  78. }
  79. //从I2C总线上接收数据
  80. unsigned char IIC_RecByte(void)
  81. {
  82. unsigned char i, da;
  83. for(i=0; i<8; i++)
  84. {
  85. SCL = 1;
  86. IIC_Delay(DELAY_TIME);
  87. da <<= 1;
  88. if(SDA) da |= 1;
  89. SCL = 0;
  90. IIC_Delay(DELAY_TIME);
  91. }
  92. return da;
  93. }
  94. void EppromW(u8 word,u8 dat){
  95. IIC_Start();
  96. IIC_SendByte(0xa0);
  97. IIC_WaitAck();
  98. IIC_SendByte(word);
  99. IIC_WaitAck();
  100. IIC_SendByte(dat);
  101. IIC_WaitAck();
  102. IIC_SendAck(1);
  103. IIC_Stop();
  104. }
  105. void Pcf_W_Dac(u8 dat){
  106. IIC_Start();
  107. IIC_SendByte(0x90);
  108. IIC_WaitAck();
  109. IIC_SendByte(0x40);
  110. IIC_WaitAck();
  111. IIC_SendByte(dat);
  112. IIC_WaitAck();
  113. IIC_SendAck(1);
  114. IIC_Stop();
  115. }

uart.c

  1. #include "sys.h"
  2. void UartInit(void) //4800bps@12.000MHz
  3. {
  4. SCON = 0x50; //8位数据,可变波特率
  5. AUXR |= 0x01; //串口1选择定时器2为波特率发生器
  6. AUXR |= 0x04; //定时器时钟1T模式
  7. T2L = 0x8F; //设置定时初始值
  8. T2H = 0xFD; //设置定时初始值
  9. AUXR |= 0x10; //定时器2开始计时
  10. ES=1;
  11. }
  12. void Send_char(u8 q){
  13. ES=0;
  14. SBUF=q;
  15. while(!TI);
  16. TI=0;
  17. ES=1;
  18. }
  19. void SendString(u8 *str){
  20. while(*str!='\0'){
  21. Send_char(*str);
  22. str++;
  23. }
  24. }

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

闽ICP备14008679号