2014-03-11 18:42:33 huang20083200056 阅读数 13887
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3997 人正在学习 去看看 朱有鹏

51单片机开发系列一

51单片机开发环境搭建以及入门汇编代码

象棋小子    1048272975

1. 51单片机概述

51单片机是对所有兼容Intel 8031指令系统的单片机的统称。目前教科书基本都是以早期的MCS-51为原型,讲解微机的原理及其接口技术的。早期的51单片机功能都较弱,需扩展rom,ram等才能组成一个较复杂的系统。而现今流行的8位51单片机都比早期芯片作了较多的功能扩展,性能强劲。生产51单片机的厂家有很多,像atmel的at80c51系列,华邦w78c051系列,宏晶stc80c51系列等,其中台湾宏晶stc系列51单片机在国内尤为流行。因此,笔者采用宏晶性价比较高的一款51单片机stc12c5a60s2来作开发讲解。该款51单片机已经是宏昌第N代产品了,其主要特征如下:

1.1. 增强型8051 CPU,1T,单时钟/机器周期,指令代码完全兼容传统8051;

1.2. 工作频率范围:0 - 35MHz,相当于普通8051的 0~420MHz;

1.3. 用户应用程序空间60K字节,片上集成1280字节RAM;

1.4. 通用I/O口36个(以封装PDIP40为例),可设置成准双向口/弱上拉,推挽/强上拉,仅为输入/高阻,开漏,每个I/O口驱动能力均可达到20mA,但整个芯片最大不要超过55Ma;

1.5. ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器,无需专用仿真器 可通过串口(P3.0/P3.1)直接下载用户程序。

1.6. 内部集成MAX810专用复位电路,2路PWM,8路高速10位A/D转换(250K/S),EEPROM,看门狗,内部RC振荡器,4个定时器,7路外部I/O口中断等。

尤其需注意的是stc新一代的单片机都是1T单时钟/机器周期,与课本介绍的早期51单片机是12T单周期是不一样的,软件实现延时时需作注意,其余扩充的特殊功能寄存器等请参考stc12c5a60s2的数据手册。

2. 51单片机编程环境概述

2.1. 代码编译工具

51单片机开发软件基本无疑选用Keil C51集成开发环境。Keil C51是德国Keil Software公司(ARM公司收购了)出品的51系列兼容单片机C语言软件开发系统,提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等,目前最新版本已经到了uVision5。这里需要说明的是,Keil C51并不完全与ANSI C一样,Keil C51由于面向的是51单片机,为了更好地详述51的架构,Keil C51扩展了一些独特的关键字,语法描述等。如用code说明为常量放在代码区,data说明直接寻址区的变化,sfr声明特殊功能寄存器等。


图2.1-1

2.2. 代码调试工具

2.2.1. Keil自带仿真调试器

Keil集成开发环境除了编译链接工具外,还自带了一个功能强大的仿真调试器。当然软仿真是能够看到Keil编译后的汇编代码,单步调试可以跟踪各个寄存器的状态变化,但是软仿真是无法得到真实的外部输入状态的,如仿真真实开发板的按键输入等。代码调试时往往需要知道编译器是否按照要求进行代码的编译处理,因此,可以让编译器输出它是如何编译,链接文件的,我们可以查看这些了解编译器编译c生成的汇编代码,链接的符号,内存分配之类的信息。Keil在Target属性中Listing列可进行设置,如C Compiler Listing选项中是c编译器输出选项,点上Assembly Code即输出c编译对应的汇编代码,在文件.lst中。C Preprocessor Listing为c编译器预处理输出的信息,AssemblerListing为汇编器输出的处理信息,LinkerListing为链接器输出的处理信息(在.m51后缀文件中),这包括编译器对内存的分配,各个函数符号等。通常编译后的汇编代码以及代码的链接信息是可以跟踪查看,以判断代码的问题所在。


图2.2.1-1


图2.2.1-2

2.2.2. Proteus仿真软件

Proteus软件是英国Lab Center Electronics公司出版的EDA工具软件,它不仅具有其它EDA工具软件的仿真功能,还能仿真单片机及外围器件。Proteus具有电路仿真功能,能仿真一些最基本电子元器件,如led,数码管,键盘等,并且是能仿真51单片机代码运行的。在这里需要说明的是,请务必不能以仿真电路的效果图与真实的硬件开发混搅,电路仿真软件往往都是逻辑上的电路连接,完全不能够用来说明真实硬件开发过程。此外,Proteus仿真51单片机也会有一些逻辑的问题,例如,在硬件上真实可运行的代码在Proteus上无法仿真达到效果,修改一下代码顺序即可。总之,对于小代码,Proteus都是可以胜任的,如果代码太复杂,仿真任务会达到100%,无法再进行仿真了。因此,笔者推荐对于初学者,可先用Proteus进行仿真,进行一些最基本的练习,熟悉汇编语言,51单片机状态变化,c语言等,验证自己在51单片机架构学习中的一些理解,代码实现的理解。学习到一定的程度还是需要一块51开发板进行开发练习的,毕竟仿真软件只能仿真很少部分的外围,也不能仿真复杂的硬件及代码。


图2.2.2-1

2.3. 代码烧录工具

Keil C51编译生成的hex文件通过STC_ISP工具,连接串口线(一般采用usb转串口线)进行下载。代码烧写传输是通过51单片机的uart串口信号线Tx,Rx完成的,stc单片机实现isp下载是因为芯片内部有厂商的固化代码,上电复位后是先执行固化代码,检测串口有无接收到特定的命令,如果有则进入下载模式,与上位机的isp下载软件进行通信,从而把代码下载进单片机rom区。如果没有有效的串口下载命令,则跳转执行真正的用户代码,即从0000H处开始执行代码。


图2.3-1

3. 汇编流水灯入门代码

笔者认为学习51单片机并不是能通过别人的例子用c语言模仿写出类似的功能即可,必须要对自己的编码意图比较清晰,这样脱离任何例程都是可以自己掌控编写代码。因此学习51单片机其实更准确来说是学习微机的原理以及接口技术。而微机的原理以及接口技术对于51,arm或其它架构的mcu都是通用的,通过51来学习微机原理会涉及到汇编语言,因为只有汇编语言才能直接描述51内部的工作状态。笔者以过来人的身份推荐初学者从51微机原理,汇编学起。C语言只是简化封装了汇编语言的一些处理过程,学完汇编,c语言也自然会达到相应的水平。此外,对于软件出错调试,只能跟踪汇编代码,查看寄存器的状态判断,而想学习arm,从事更深入的嵌入式开发,汇编是必不可少的。

3.1. 硬件原理图

8个LED连接到P0口,当短接CON2后,只要P0口对应位为0(低电平),相应的LED则被点亮。此外说明一下为什么不用P0对应位为1时点亮而用0,因为传统51单片机I/O口是弱上拉的,高电平是输不出大电流的(相对低电平),高电平拉电流估计是ua级,但低电平灌电流几个ma是不成问题的。对于stc系列51单片机,I/O口是可以配置成推挽输出的,这样高低电平都是可以达到20ma(手册数据)的输出/吸收电流。


图3.1-1

3.2. 工程搭建

打开Keil C51,Project->NewuVersion Project,保存项目后,选择cpu为Atmel的AT89C52的51单片机,这里需要说明的是,Keil没有stc系列的51单片机选择,只要是51内核,在Keil下可选择任一厂家,任一款51单片机进行代码编写,因为代码都是兼容的。而不同厂商芯片之间的差异只是rom大小,ram大小,片内外设以及一些厂家特有的特殊功能寄存器的定义。这些都可以在工程中,代码中重新定义,编译器会老老实实按照要求编译代码。选择了cpu后,会提示是否加入51的启动代码到工程中,由于我们编写的是汇编语言,此处不需要,加入后启动代码会与我们自己的汇编代码定义冲突。这里需要说明的是,启动代码是初始化c环境需要的文件,启动代码会设置c代码运行时的堆栈,清零全局变量,静态变量区等。这就是为什么我们在c文件中定义一个全局变量,默认这个变量的初始值为0(C标准)。

3.3. 代码编写

创建一个新文件,命名为LEDs.ASM,ASM为51汇编文件后缀,保存并加入工程。汇编的一些基本用法在代码注释中有说明,更多的汇编用法请google,百度。这里需要说明的是,51单片机第一条指令位置是在0H,后面相邻的地址是分配给相应的中断进入的,因此第一条指令往往会跳转避开中断向量地址区。以下代码实现8个LED灯轮流点亮,点亮延时1s,这个汇编代码是模仿c语言函数结构化编程的,里面可以类似认识到c编译器大概是如何处理c函数并生成汇编的,当然编译器汇编质量基本是无法达到人工汇编质量的。

 

ORG 0H        ; 表示后面紧跟的那条指令的地址是 0000H

JMP     Begin ; 无条件跳转到Begin处,以避免中断向量地址

ORG 0BH      ;000BH处为定时器T0的中断处理入口

JMP T0_INT ; 未使用T0定时器中断,只供代码说明

 

T0_INT:

; 中断发生时会自动把当前程序运行地址PC压入栈sp

; 中断处理完后用RETI中断返回,从栈sp中出栈到PC返回打断程序处

RETI

 

LED1 EQUP0.0  ; LED1由P0口第0位控制,以下类似

LED2 EQU P0.1

LED3 EQU P0.2

LED4 EQU P0.3

LED5 EQU P0.4

LED6 EQU P0.5

LED7 EQU P0.6

LED8 EQU P0.7

 

ORG 100H

Begin:

MOV P0, #0xff ;P0口输出全1,所有LED灭

 

LOOP:

; R6,R7为调用函数的参数传入,参数为16位,需2字节

; _Delay_ms对应c函数原型为void Delay_ms(intnCount)

; 共延时nCount * 1ms(12M普通8051),对于stc指令周期1T的

; 延时nCount * (1/6)ms (12M)

CLR LED1 ;直接位清0指令,清除P0口第0位,LED1亮

MOV R7, #(1000& 0xff) ; 参数为1000,普通8051延时1s

MOV    R6, #((1000>>8) & 0xff)   ; 16位变量需用2字节

CALL _Delay_ms ;延时n个1ms(普通51),延时n个1/6ms(stc 51)

SETB LED1 ; 直接位位置位指令,置位P0口第0位,LED1灭

 

CLR     LED2

MOV R7, #(1000& 0xff) ; 普通8051延时1s,stc应改1000为1000*6

MOV    R6, #((1000>>8) & 0xff) ; 能让编译器运算的不要自己手动计算

CALL _Delay_ms

SETB LED2

 

CLR     LED3

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

SETB LED3

 

CLR     LED4

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

SETB LED4

 

CLR     LED5

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

SETB LED5

 

CLR     LED6

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

SETB LED6

 

CLR     LED7

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

SETB LED7

 

CLR     LED8

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

SETB LED8

 

MOV P0, #0    ; 常数存入直接地址,清零P0口,LED全亮

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

MOV P0, #0xff

MOV R7, #(1000& 0xff)

MOV    R6, #((1000>>8) & 0xff)

CALL _Delay_ms

 

JMP     LOOP ; LOOP循环

 

; 按照keil c与汇编调用规则命令函数及传参,可先不管

; 用CALL调用函数会硬件把调用处PC地址压栈

; 处理完后用RET函数返回,从栈sp中出栈到PC返回调用程序处

_Delay_ms:

PUSH ACC ; 子函数需用到累加器,需压栈保存以免覆盖调用前值

PUSH PSW ; 用到程序状态寄存器,需压栈

MOV    A, R0 ; 用到R0寄存器,没有直接寄存器名压栈指令

PUSH ACC  ; 通过累加器完成压栈

MOV A, R1 ; 用到R1寄存器,同理压栈

PUSH ACC

 

; 以下是16位的递减1减法运算,高8位在R6中,低8位在R7中

; 数据运算涉及到进位/借位,只能通过累加器ACC来完成

Delay:

CLR C ; 清除借位标志

MOV A, R7 ; 低8位值给到累加器,只有针对累加器运算的指令

SUBB A, #1 ; 自减1,会改变程序状态标志(进位/借位)

MOV R7, A ; 运算结果返回到原变量中

JNC DelayOnce ; 没有借位说明延时次数未到,跳转延时一次

CLR C ; 产生了借位,需向高8位R6减1

MOV A, R6

SUBB A, #1

MOV R6, A

JNC DelayOnce ; 高8位未减至0,说明延时次数未到

POP ACC   ; 高8位也为0,不能再给低8位借位了,延时到返回

MOV R1, A ; 返回时先出栈,出栈顺序与入栈顺序相反

POP ACC     ; 并且PUSH与POP指令必须一一对应,不然只有让程序飞

MOV R0, A

POP PSW

POP ACC

RET ; 子函数返回,与c函数是一至的

 

; DelayOnce执行一次机器周期总数为 1+R0*(1+R1*2+2)+2=997个

; 若普通51晶振12M,每个机器周期1us,则DelayOnce一次延时1ms

; 对于stc51,同等晶振下,指令速度快了6倍,DelayOnce延时1/6ms

DelayOnce:

MOV R0, #2 ;普通51机器周期数1(stc这条指令比普通的快6倍)

Delay1:

MOV R1, #247 ;普通51机器周期数1(stc快6倍)

DJNZ R1, $ ; R7减1不为0则跳转到当前地址循环,机器周期数2

DJNZ R0, Delay1; 机器周期数2(stc快6倍)

JMP Delay ; 已延时一次,机器周期数2(stc快6倍)

 

END            ; 汇编代码结束

3.4. 代码运行

在Keil上选中Create HEX File复选框,编译生成hex文件,可以直接在Keil进行debug,通过查看P0口数据的变化以跟踪代码等,注意设置仿真的时钟为12M。更直观的是用Proteus搭建一个51单片机仿真电路,在P0口连接8个LED,即可看到效果,注意设置仿真的时钟为12M。如果有51开发板,把代码下载进单片机中即可(但对于stc 1T 51单片机需修改一下代码中延时的参数)。


图3.4-1 Proteus仿真代码效果图

4. 小结

笔者概述性介绍了51单片机(以stc12c5a60s2为例),讲解了其基本的编译,调试工具、环境的搭建。简单给出了采用c函数结构编程的流水灯汇编代码,让读者对汇编,c编译汇编过程有一个初步的认识,由于笔者的认识有限,文章中个人观点有些可能非常片面,以及文章中可能存在不少的错误,恳请大家指正。

 

由于一门技术是不可能用只言片语就能说清的,笔者也只能在文章中概述性讲述,可能会有初学者觉得例程汇编过难,笔者想说明的是,学习是一个渐近的过程,只要学习了,那么就会有潜移默化的进步。以下资料笔者认为跟本文学习是相关的,推荐大家学习或参考。

a. stc12c5a60s2数据手册,非常有用,里面有很多编程示例代码以及详尽的stc51系列单片机寄存器编程描述。

b. 单片机初学者实验指导书.doc,只对入门者,讲述怎样连接usb转串口线下载代码,keil安装以及工程搭建。

c. Keil软件使用手册.ppt,简单讲解了Keil软件工程的搭建,调试介绍

d. Proteus中文入门教程.doc,讲述了Proteus如何搭建电路以及进行51单片机的仿真

e. 51汇编指令.ppt,较好地介绍了51汇编指令,伪指令等的使用,但细节不够,如指令执行后栈变化没有说明,以51微机原理教科书汇编指令资料为最佳。

f. LEDs.ASM,汇编工程代码,加入到keil工程即可编译。

http://pan.baidu.com/s/1nbXwU

 

2019-11-27 19:50:32 Binair2019 阅读数 142
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3997 人正在学习 去看看 朱有鹏

单片机与嵌入式的区别

#1:单片机是一种芯片,组成部分有:中央处理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器。

#2:大学时学习的是51单片机,出来工作后接触的是stm32单片机(如STM32F207ZC),嵌入式是一个系统,类比如Linux系统那样,有文件目录结构,app可以在上面跑,可以后台调试查看进程等等。

#3:
一个完整的单片机程序往往需要移植系统进行开发,例如ucos(ucos-II)操作系统,rtos系统等等配合开发。
一个完整的嵌入式系统往往需要重烧系统:uboot,内核,文件系统;然后要考虑接口,接线,装驱动,装软件(app)等等问题。

#4:
一个单片机程序一般都是用keil uvision4,keil uvision5,keil MDK等等软件进行开发,调试。
一个嵌入式程序一般是用Xilinx SDK进行开发。

#5:
调试单片机和嵌入式程序一般都可以通过串口(如rs232,usb转ttl串口)进行调试,一般都可以通过j-link进行软件烧录。

#6:
k4,k5,sdk可以生成的目标文件可以有:Hex、bin、axf、elf等文件。

keil从0开始新建工程

这篇链接写得很详细,特地引转发一下:“STM32F4教程从零开始1——建工程”
https://blog.csdn.net/linxiaobo110/article/details/40349183

stm32标准外设库

这篇链接写得很详细,特地引转发一下:“STM32F20x_StdPeriph_Lib_V3.5.0库全面解析”
https://blog.csdn.net/u011511430/article/details/17322643

移植ucos-ii

本文例子对应stm32f207zc移植的ucos-ii。

一个简单的单片机代码框架

#include "stdio.h"
#include <includes.h>
#include "os_cpu.h"
#include "ucos_ii.h"

static  OS_STK      TaskStk[1024];
static  OS_STK      AppTask1Stk[1024];

//任务1线程
void  AppTask1(void *p_arg)
{	
	while(1)
	{
		//USER_OSTimeDlyHMSM(0, 0, 1, 0);
		//to do
	}
}

//在这里创建不同的线程任务
void Task(void *pdata)
{
	  //创建线程任务1
      OSTaskCreate(AppTask1,(void *)0,
                   &AppTask1Stk[1024-1], 
                   30);
    //在这里创建不同的线程任务,to add
	  //创建任务n           
}

static void init()
{
	
		//初始化LED

		//蜂鸣器初始化

		//系统时钟配置

		//SRAM初始化

		//串口1,2,3等初始化

		//初始化网络

		//uc/os初始化
		OSInit();	
	
}

int main(void)
{
	int ret = 0;
	init();
	
    ret = OSTaskCreateExt(Task,
					     (void *) 0,
					     &TaskStk[1024-1],
					     30,
					     30,
					     (OS_STK  *)&TaskStk[0],
					     1024,
					     (void *) 0,
					     OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR);
	OSStart();   
}

自己写了一个简单demo链接:
https://download.csdn.net/download/Binair2019/12003498

2019-09-26 16:35:34 peakcoo 阅读数 18
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3997 人正在学习 去看看 朱有鹏

单片机是一个小型的计算机,但与计算机不同的是计算机的输入输出操作比较简单便利,但单片机的输入输出则需要依靠它的引脚,通过编程控制实现。在性能上面单片机与计算机的相同点都是有CPU、内存、时钟系统、中断系统、定时器等。

单片机开发中,单片机能运行起来的必要条件就需要包括电源、晶振及复位电路这个几个功能。没有电源就没有能源,那就无法工作;没有晶振,就没有时钟电路,就没有节拍,指令就不能按一定的步调运行;没有复位电路,单片机就很不可靠,会出现“死机”、“程序走飞(PC)”等现象。所以这三个就单片机运行的基本因素。

以口袋秤单片机为例,我们来聊下关于单片机开发。

口袋秤单片机CSU18MB86芯片是一个8位RISC架构的高性能单片机,集成了24Bit高精度ADC和LCD显示模块。内部集成8k*16Bits的MTP程序存储器。

高性能的RISC CPU:

8位单片机MCU

内置8K×16位MTP可编程存储器(烧录次数不低于1000次)

488字节数据存储器(SRAM)

只有43条单字指令

8级存储堆栈


模拟特性:

24位分辨率

内部集成的可编程增益放大器

ADC的输出速率可选:30Hz/60Hz/120Hz/240Hz/480Hz/960Hz/1920Hz/3480Hz

内带电荷泵

内带稳压器供传感器和调制器

专用微控制器的特性:

上电复位(POR)

上电复位延迟定时器(39ms)

内带低电压复位(LVR)

定时器0

-可编程预分频的8位的定时器

定时/计数器1

-可编程预分频的8位的分频器

看门狗定时器(3K WDT)
 

设置单片机内部的寄存器和端口引脚以便输出高低电平控制其他(连接在单片机上)器件,这是我们做单片机开发主要的目的,所以在单片机开发中首要的是配置时钟、配置IO口、配置复位方式、看门狗设置等等,接下来才是:中断(外部,定时器,串口),串口,ADC,XRAM读写,内部FLASH等。
 

2019-12-06 23:15:35 zhongvv 阅读数 416
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3997 人正在学习 去看看 朱有鹏

        应广单片机价格美丽,性价比高,mini-c好用,适用于消费电子开发。但是案例demo太少了,为了方便大家能够快速入门。这里贴出了一份经典PMS150C的点灯程序代码。不是Hello world!那种,是偏向于实际产品的那种。

涉及到的内容有不少,包括应广单片机PMS150C:

1.端口输入输出配置

2.Tmr16中断配置(systick),倒计时

3.按键检测,包括防抖,可以用于长按键检测

4.低功耗,睡眠配置

5.唤醒

6.系统时钟配置,系统时钟切换

7.看门狗应用

8.状态机应用

其他比如PMS152,PMS132设置也类似,希望大家喜欢!

 

#include    "extern.h"

#define     HIGH    1
#define  LOW    0

#define DISABLE 0
#define ENABLE 1

#define EMPTY 0
#define FULL 1

#define ON 1
#define OFF 0

#define LED_OPEN 0

#define LED_FLASH 1

#define LED_BREATH 2

#define LED_CLOSE  3

//#define RUN_TMR  60000
/*单位 ms*/
#define RUN_TMR     15000

#define LOW_POW_TIMING_TMR  3000

BIT     LED_G           :        PA.3;
BIT     KEY_HIT            :        PA.4;

#define KEY_DEBOUNCE_CNT  20
#define KEY_LONG_PRESS_TIMER 1200

#define GRE_LED_FLASH  {if(LED_G){LED_G=0;}else{ LED_G=1;}}

/*计数值Cinit*/
word ucFlashTmrCnt;
/*cinit*/
BIT ubMsFlag; 

/*cinit*/
BIT  ubEnLedFlash; 

/*cinit*/
bit ubMod;
byte ucSysSt;
//byte ucLedPwmDt;

/*定时时间是否到了cinit*/
bit     FLAG_NMS;
/*计数值cinit*/
byte    count;
/*定时器初始化cinit*/
word    T16COUNTER;

Eword ueLowPowAltTm;

byte ucKeyHitHigtCnt;
byte ucKeyHitLowCnt;
byte ucKeyHitSt;
byte ucKeyHitBak;
word usKeyHitLoPreCnt;
word usKeyHitLoPreCntSet;
bit ucKeyHitPreSt;

byte ucLedSt;


void UpDateLedSt(void);
/***************************************/
void    TIME16_Init(void)
{
    /*计数值清零*/
    T16COUNTER    =488;
    /*ms标记reset*/
    FLAG_NMS    =0;
    
    /*使能定时器*/
    $    INTEN    T16;
    /*关中断*/
    INTRQ        =    0;
    
    /*停止定时器*/
       T16M.5        =0;               

    STT16 T16COUNTER;
    
    /*计算方法16M/*/
    $    T16M    IHRC,/1,BIT11;     
    

}

void StartLowPowTmr(void)
{
    ueLowPowAltTm=RUN_TMR;
}

/*低功耗*/
void LowPow(void)
{
    /*退出功能则进入低功耗*/
   if((!ueLowPowAltTm) )
   {
           ubEnLedFlash=0;
           GRE_LED_OFF;

        wdreset;
        /*IHRC ->ILRC,关看门狗*/
        CLKMD=0xf4;
        /*禁用IHRC*/
        CLKMD.4=0;
        
        while(1)
        {
             /*低功耗*/
             STOPSYS;
         

             /*有按键按下,充电和按键唤醒*/
              if(KEY_HIT==0)
              { 
                    /*退出低功耗*/
                    break; 
              }
           
        }
        /*ILRC->IHRC ,
             b7:5@001=IHRC/8,
             b4@1=IHRC
             
             b3@1=模式1
             b2@ 1=ILRC启动
             b1@ 1=看门狗开启
             b0@ 0=Pa5;
             
             模式口1;开看门狗*/
        CLKMD=0b001_1_1110;

        ucLedSt=1;
        ucSysSt=LED_CLOSE;
        StartLowPowTmr();
    }
    
}

void UpdateLedTmr(void)
{
   /*使能,闪灯*/    
   if(ubEnLedFlash)
   {
         /*1ms进行一次计数*/
        ucFlashTmrCnt++;
        if(ucFlashTmrCnt>250)
        {
            ucFlashTmrCnt=0;
            /*标记*/
            ubMsFlag=1; 
        }
   }

}

/*闪灯*/
void FlashLed(void)
{
    /*定时时间到*/
    if(ubMsFlag)
    {
        /*闪灯*/
        GRE_LED_FLASH;
        ubMsFlag=0;
    }
}


/*端口检测*/
void ResetKeyTmr(void)
{

    if(KEY_HIT)
    {
        ucKeyHitHigtCnt=KEY_DEBOUNCE_CNT;
    }
    /*当前状态为0*/
    else
    {
        ucKeyHitLowCnt=KEY_DEBOUNCE_CNT;
    }

    
}


/*1ms任务函数*/
void UpDateKeyTmr(void)
{
      /*重新设置计数值*/ 
     ResetKeyTmr();
      /*KeyHit高电平计数值*/
     if(ucKeyHitHigtCnt)
     {
         ucKeyHitHigtCnt--;
        if(!ucKeyHitHigtCnt)
        {
            /*高电平倒计时完成说明当前为低*/
            ucKeyHitSt=LOW;
        }
     }
     /*高电平计数值为0*/
     else
     {
         usKeyHitLoPreCnt++;
        /*做饱和加*/
        if(usKeyHitLoPreCnt>20000)
        {
            usKeyHitLoPreCnt=20000;
        }
        
        if(usKeyHitLoPreCnt==usKeyHitLoPreCntSet)
        {
            /*10s 钟定信号*/
            ucKeyHitPreSt=1;
        }
     }

      /*KeyHit低电平计数值*/
     if(ucKeyHitLowCnt)
     {
         ucKeyHitLowCnt--;
        if(!ucKeyHitLowCnt)
        {
            /*低电平倒计时完成,说明当前为高*/
            ucKeyHitSt=HIGH;

            usKeyHitLoPreCnt=0;
            ucKeyHitPreSt=0;
        }
     }


}

void MsTask(void)
{
    
    /*按键*/
    UpDateKeyTmr();

    UpdateLedTmr();
    
    FlashLed();

    
    /*低功耗定时器*/
    if(ueLowPowAltTm)
    {
        ueLowPowAltTm--;
    }
    
    LowPow();
    
}


void UpDateLedSt(void)
{
    
    ucSysSt++;
    if(ucSysSt>1)
    {
        ucSysSt=0;
    }

    switch(ucSysSt)
    {
        /*开闪烁*/
        case 0:
            ubEnLedFlash=1;
            break;
            
        
        /*关灯*/
        case 1:
            ueLowPowAltTm=LOW_POW_TIMING_TMR;
            ubEnLedFlash=0;
            /*关灯,关闪烁*/
            GRE_LED_OFF;
            /*关灯*/
            break;
        default:
        break;
    }
    
}


/*状态切换*/
void GetKeySt(void)
{
   /*状态有变化*/
   if(ucKeyHitSt!=ucKeyHitBak)
   {
          /*保存当前状态*/
          ucKeyHitBak=ucKeyHitSt;
       
       if(!ucKeyHitSt)
       {    
             /*更新倒计时*/
                StartLowPowTmr();
             /*状态切换*/
             UpDateLedSt(); 
       }
      
   }
}

/*端口状态初始化*/
void KeyAppInit(void)
{
    
    if(KEY_HIT)
    {
        ucKeyHitSt=HIGH;
        ucKeyHitBak=HIGH;
    }
    else
    {
        ucKeyHitSt=LOW;
        ucKeyHitBak=LOW;
    }

   ucKeyHitHigtCnt=KEY_DEBOUNCE_CNT;
   ucKeyHitLowCnt=KEY_DEBOUNCE_CNT;
   usKeyHitLoPreCnt=0;
   ucKeyHitPreSt=0;
   
}


void    FPPA0 (void)
{
    .ADJUST_IC    SYSCLK=IHRC/8, IHRC=16MHz, VDD=3.0V;
    $ CLKMD IHRC/8,En_IHRC,En_ILRC,En_WatchDog;
    /**/
    .delay 40000;
    
    $  LED_G    OUT,HIGH;          
    $  KEY_HIT    IN,PULL;   
    
    TIME16_Init();
    
    KeyAppInit();

   
    ucLedSt=1;

    ubMsFlag=0;

    ubEnLedFlash=0;
    
    /*长按键时间*/
    usKeyHitLoPreCntSet=3000;
    ucSysSt=LED_CLOSE;
    
    engint;
    while (1)
    {

            wdreset;

         /*1ms定时时间到*/
         if ( FLAG_NMS )
         {
          
            MsTask();

            /*清除标记*/
            FLAG_NMS=0;
        }
        
        /*取得按键状态*/
        GetKeySt();

        
    }
}

void    Interrupt ( void )
{
    pushaf;

    if ( Intrq.T16 )  
    {

        Intrq.T16    =    0;
        
        STT16 T16COUNTER;

        
        
        if ( count>0 )
        {
            count--;
        }
        else
        {
            count   =   9;
            /*1ms*/
            FLAG_NMS=   1;      
        }

    }

    popaf;
}

/*end create by zhongvv QQ:85547259*/

 

2019-11-19 15:13:04 z896435317 阅读数 22
  • 单片机控制第一个外设-LED灯-第1季第6部分

    本课程是《朱有鹏老师单片机完全学习系列课程》第1季第6个课程,主要讲解LED的工作原理和开发板原理图、实践编程等,通过学习目的是让大家学会给单片机编程控制LED灯,并且为进一步学习其他外设打好基础。

    3997 人正在学习 去看看 朱有鹏

单片机开发 — 准备

一、处理芯片 - STC89C52

二、开发环境 - win7+Keil4

三、新建单片机项目

打开Keil—>Project—>New μVision Project,在弹出框中添加项目文件(.uvpro的格式,一般新建一个独立的项目目录,在目录中添加)

四、选择处理芯片 - AT89S52

新建单片机项目后,会弹出窗口选择CPU,用Atmel中的AT89S52

五、设置输出HEX文件

打开Project—>Opeions for Target “你的项目名” ,在弹出框中选择Output,勾选Create HEX File

没有更多推荐了,返回首页