单片机最小系统及跑马灯

2015-11-05 20:17:36 rl529014 阅读数 11026

功能:让8个LED灯顺序点亮,然后在逆向点亮;

代码如下:

#include<reg52.h>

#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
void delay(uint z);

main()
{  
uchar aa;   
uint count=0;
while(1)
{
aa=0xfe;
P0=aa;
delay(100);
while(1)
{
aa=_crol_(aa,1);//aa左移一位,
P0=aa;
delay(1000);

        count++;

if(count>=7)        //判断8个LED灯是否已被全部点亮一次
{
for(;count>0;)//逆向点亮8个LED灯
{
aa=_cror_(aa,1);//aa右移一位
P0=aa;
delay(1000);//调用延时程序,延时1S;
count--;
}
break;
}
 
}
}
}
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);

}

贴出一张图片:


2018-07-24 20:01:42 Mao1253 阅读数 1183

      学习单片机需要手动自己多写代码,一般刚学51单片机代码量也不会太多,需要有一定的C语言,数字电路和模拟电路的基础知识,比起看理论知识,不如多写多在板子上调试,这样学得快多了,也非常有效,但其基础知识也非常重要,这样有利于深入学习,学习用的开发板可以在网上淘一个,很多,学习软件有:开发软件我用的是(Keil  uVsion5.LNK)和单片机下载编程烧录软件(STC—ISP),网上可以下载,具体下载调试若有不懂可以百度或者留言哟!

      刚开始学应先学51pcb板原理图,画一下51最小系统原理图,今天就先讲一个入门小程序 :跑马灯的实现!话不多说,直接上代码:

 

 

 

代码不多,很好理解,18行先给变量led1赋值0x01;进入for循环后P0口得到led1取反的值,即为:P0 = 0xfe,第一个灯(板子上LED1)亮,延时后,led1左位移一位,即led1=0x0000 0010,即P0又为led1取反的值,即P0=0x1111 1101,这时第二个灯亮,以此类推,经过while死循环就实现了八个LED无限流水灯现象,即为跑马灯。

2017-10-16 10:46:06 lxq_xsyu 阅读数 2466

在上一篇中《单片机实现跑马灯》中我们完成了一个漂亮的流水灯,里面有一段代码我们来分析一下吧,看看单片机是怎么理解这段代码的。

                                        

《单片机实现跑马灯》(点击图片跳转)



#include <reg51.h>    //此文件中定义了51的一些特殊功能寄存器


void Delay10ms(unsigned int);   //误差 0us


void main()     //主函数

while(1)

{

P2  = 0x00;     //置P0口为低电平

Delay10ms(50);  //调用延时程序

P2  = 0xff;     //置P0口为高电平

Delay10ms(50);  // 调用延时程序

}

}


void Delay10ms(unsigned int c)   //误差 0us

{

    unsigned char a,b;

    for(;c>0;c--)

        for(b=38;b>0;b--)

            for(a=130;a>0;a--);

}



为什么我们可以直接给P2赋值呢?哪里定义了这个变量,打开Keil的安装目录(例如我的在C:\Keil\C51\INC\REG51.H)我们可以看到一个头文件,内容里面定义了P2.粘贴一部分如下。



#ifndef __REG51_H__

#define __REG51_H__


/*  BYTE Register  */

sfr P0   = 0x80;

sfr P1   = 0x90;

sfr P2   = 0xA0;

sfr P3   = 0xB0;

sfr PSW  = 0xD0;

sfr ACC  = 0xE0;

sfr B    = 0xF0;

sfr SP   = 0x81;

sfr DPL  = 0x82;

sfr DPH  = 0x83;


此处省略........


#endif



sfr P2   = 0xA0这句话的意思是把单片机内部地址0xA0处的这个寄存器重新起名叫P2,所以我们在程序中可以直接操作P2来操作地址0xA0处的寄存器。如下图这是STC89C52RC单片机的管脚分布,P0, P1, P2分别对应8个管脚。


对应实物图如下  ↓↓


进入main()函数(程序的入口)后我们让P2寄存器赋值为0意思就是让P2对应的8个IO口电压为低电平,此时发光二极管不亮。然后一段延时后再给P2寄存器赋值为FF(二进制为1111 1111)意思让P2对应的8个IO口电压为高电平。while语句就是让无限循环这一过程。



Delay10ms()函数是一个延时程序,感觉好像在做无用功。单片机工作和人一样有心跳的,这个心跳我们称为时钟周期,如果外接晶振(上图右下角所示)那么这个时钟周期就是这个晶振频率的倒数(如12MHZ的晶振,它的时钟周期就是1/12us),它是单片机中的最小时间单位。我们让单片机没执行一个程序指令就需要消耗一个机器周期(对应多个时钟周期)这样就会延时。


噢耶!程序有了,是怎么将程序烧写到单片机的呢?



就是这个小家伙将程序烧写到单片机的,但是它不是什么神兵利器,它只是做了一件很简单的事情。它里面最核心的就是USB转UART模块,功能是把RS-232电平(PC机输出一般12v)和TTL电平(单片机输出一般5v)相互转换。



然后我们可以看到连接图中TXD和RXD两个管脚,TXD 发送数据Transmit(tx) Data的缩写,RXD接收数据Receive(rx) Data的简写。所以注意这两个接口连接需要互换。将USB一头插到PC机上安装驱动后使用STC单片机烧写工具擦除之前程序写入新的程序。



将发光二极管一个管脚接地另一个串联一个100欧电阻接到P2相应8个管脚就可以跑我们刚刚写入的程序了。



提示:Keil、STC-ISP工具下载和其他疑问请关注公众号后留言询问,吉格斯看到会回复你的。


2018-01-10 13:53:30 fengshuiyue 阅读数 8794

STM32学习了有一阵子了,现做一个简单的总结。
先上一个跑马灯的小程序,本人学习过程中先学习了一些STM32F103的一些基本知识,但是直到接触到真实的程序后开始思索看代码后一些模糊的概念才发现原先学习的基础感念都提到过,不过当时在学习的时候仅仅过了遍脑子没有理解了,故在此我先贴出跑马灯的程序,然后通过程序拓展出STM32编程必知的基础。

1、跑马灯

什么是跑马灯,本篇的程序实现的功能是板子上的两个小灯来回的闪烁。
下面将贴出寄存器版和库函数版两版源码。

1)寄存器版

main.c

#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
 {  
    delay_init();   //延时函数初始化     
    LED_Init();     //初始化与LED连接的硬件接口
    while(1)
    {
        LED0=0;
        LED1=1;
        delay_ms(300);  //延时300ms
        LED0=1;
        LED1=0;
        delay_ms(300);  //延时300ms
    }
 }
 /**下面注释掉的代码是通过 直接操作寄存器 方式实现IO口控制**
int main(void)
{ 
    delay_init();   //初始化延时函数
    LED_Init();     //初始化LED端口
    while(1)
    {
     GPIOB->BRR=GPIO_Pin_5;//LED0亮
     GPIOE->BSRR=GPIO_Pin_5;//LED1灭
     delay_ms(300);
     GPIOB->BSRR=GPIO_Pin_5;//LED0灭
     GPIOE->BRR=GPIO_Pin_5;//LED1亮
     delay_ms(300);

     }
 }
***************************************************/

sys.h

#ifndef __SYS_H
#define __SYS_H 
#include "stm32f10x.h"

//0,不支持ucos
//1,支持ucos
#define SYSTEM_SUPPORT_OS       0       //定义系统文件夹是否支持UCOS

//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入

#endif

led.h

#ifndef __LED_H
#define __LED_H  
#include "sys.h"

#define LED0 PBout(5)// PB5
#define LED1 PEout(5)// PE5 

void LED_Init(void);//初始化                           
#endif

led.c

#include "led.h"

//初始化PB5和PE5为输出口.并使能这两个口的时钟         
//LED IO初始化
void LED_Init(void)
{
    RCC->APB2ENR|=1<<3;    //使能PORTB时钟       
    RCC->APB2ENR|=1<<6;    //使能PORTE时钟  

    GPIOB->CRL&=0XFF0FFFFF; 
    GPIOB->CRL|=0X00300000;//PB.5 推挽输出       
    GPIOB->ODR|=1<<5;      //PB.5 输出高

    GPIOE->CRL&=0XFF0FFFFF;
    GPIOE->CRL|=0X00300000;//PE.5推挽输出
    GPIOE->ODR|=1<<5;      //PE.5输出高 
}

delay.h

#ifndef __DELAY_H
#define __DELAY_H              
#include "sys.h"  
void delay_init(void);
void delay_ms(u16 nms);
void delay_us(u32 nus);
#endif

delay.c

#include "delay.h"
static u8  fac_us=0;//us延时倍乘数              
static u16 fac_ms=0;//ms延时倍乘数,在ucos下,代表每个节拍的ms数

//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);   //选择外部时钟  HCLK/8
    fac_us=SystemCoreClock/8000000;             //为系统时钟的1/8  
    fac_ms=(u16)fac_us*1000;                    
}                                   
//延时nus
//nus为要延时的us数.                                             
void delay_us(u32 nus)
{       
    u32 temp;            
    SysTick->LOAD=nus*fac_us;//时间加载          
    SysTick->VAL=0x00;//清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;//开始倒数    
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
    SysTick->VAL =0X00;//清空计数器   
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864 
void delay_ms(u16 nms)
{                 
    u32 temp;          
    SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
    SysTick->VAL =0x00;//清空计数器
    SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;//开始倒数  
    do
    {
        temp=SysTick->CTRL;
    }while((temp&0x01)&&!(temp&(1<<16)));//等待时间到达   
    SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;//关闭计数器
    SysTick->VAL =0X00;//清空计数器          
} 
#endif 

2、库函数版

main.c

#include "stm32f10x.h"
#include "delay.h"
#include "led.h"

int main(void)
{ 

    delay_init();   //初始化延时函数
    LED_Init();     //初始化LED端口
    while(1)
    {
            GPIO_ResetBits(GPIOB,GPIO_Pin_5);  //LED0对应引脚GPIOB.5拉低,亮  等同LED0=0;
            GPIO_SetBits(GPIOE,GPIO_Pin_5);   //LED1对应引脚GPIOE.5拉高,灭 等同LED1=1;
            delay_ms(300);  //延时300ms
            GPIO_SetBits(GPIOB,GPIO_Pin_5);    //LED0对应引脚GPIOB.5拉高,灭  等同LED0=1;
            GPIO_ResetBits(GPIOE,GPIO_Pin_5); //LED1对应引脚GPIOE.5拉低,亮 等同LED1=0;
            delay_ms(300);                     //延时300ms
    }
} 

led.h

#ifndef __LED_H
#define __LED_H  
void LED_Init(void);//初始化                           
#endif

led.c

#include "led.h"
//初始化PB5和PE5为输出口.并使能这两个口的时钟         
//LED IO初始化
void LED_Init(void)
{
 GPIO_InitTypeDef  GPIO_InitStructure;

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);//使能PB,PE端口时钟

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//LED0-->PB.5 端口配置
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//IO口速度为50MHz
 GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数初始化GPIOB.5
 GPIO_SetBits(GPIOB,GPIO_Pin_5);//PB.5 输出高

 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//LED1-->PE.5 端口配置, 推挽输出
 GPIO_Init(GPIOE, &GPIO_InitStructure);//推挽输出 ,IO口速度为50MHz
 GPIO_SetBits(GPIOE,GPIO_Pin_5);//PE.5 输出高 
}

上面的代码即是一个跑马灯程序的源码了,其中sys.h为正点原子为我们可以实现51类似的GPIO控制功能,定义的一些宏定义。

2、罗列问题

看了上述的单片机入门学习代码(类似语言学习入门代码helloword),想必会有很多疑问提出来,下面会罗列一些学习方面的问题,然后在一个一个的给予解释。

1)开发工具使用MDK5,工程项目如何建立

2)GPIO是什么,程序中用到的GPIOB、GPIOE这些宏定义代表什么

3)stm32f103是什么

4)为何要使能PB、PE端口时钟

5)库函数编程函数都表示的是什么

3、解释问题

1)开发工具使用MDK5,工程项目如何建立

1-1)使用MDK5新建工程,工程建好后,通过右键工程项目菜单“Manage project items…”将相应的基本开发库函数源码填入进来,下图是本程序用到的基础库的介绍,请看图
工程目录说明
要使程序编译成功,还需在 “Options for target ‘marquee’”弹出框中设置 “C/C++”相应配置项
①设置“Preprocessor Symbols”,如下图
Preprocessor Symbols
②设置 “Include Paths”,即设置相应库函数的目录,如下图
Include Paths

USE_STDPERIPH_DRIVER :表示是否在应用中启用外设驱动。我们使用标准外设库是为了方便控制外设,添加这个定义,以启用外设驱动。( to use or not the peripheral’s drivers in application code (i.e. code will be based on direct access to peripheral’s registers rather than drivers API) , this option is controlled by the #define USE_STDPERIPH_DRIVER

STM32F10X_HD:STM32F10X系列每种系列都有所区别,例如sram或者flash或者外设数量不一样,所以stm32标准外设库必须根据你使用的处理器来做相应的预处理。
在stm32f10x.h可以看到相应的宏定义,如下:

#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL) 
  /* #define STM32F10X_LD */     /*!< STM32F10X_LD: STM32 Low density devices */
  /* #define STM32F10X_LD_VL */  /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */  
  /* #define STM32F10X_MD */     /*!< STM32F10X_MD: STM32 Medium density devices */
  /* #define STM32F10X_MD_VL */  /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */  
  /* #define STM32F10X_HD */     /*!< STM32F10X_HD: STM32 High density devices */
  /* #define STM32F10X_HD_VL */  /*!< STM32F10X_HD_VL: STM32 High density value line devices */  
  /* #define STM32F10X_XL */     /*!< STM32F10X_XL: STM32 XL-density devices */
  /* #define STM32F10X_CL */     /*!< STM32F10X_CL: STM32 Connectivity line devices */
#endif
#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL)
 #error "Please select first the target STM32F10x device used in your application (in stm32f10x.h file)"
#endif

1-2)采用ST-Link下载程序
使用ST-Link方式下载程序我们可以使用debug功能在线调试代码,建议开发阶段使用这种方式。
ST-Link方式下载程序设置方式如下图:
st-link
在debug页签中选中“ST-Link debugger”,点击“Setting”修改 Debug Adpater为“ST-Link/V2”,
安照图设置”CPU DLL”为SARMCM3.DLL、“Parameter”为 -REMAP;设置“Debug DLL”为DARMSTM.DLL、“Parameter”为-pSTM32F103ZE;设置“Driver DLL”为SARMCM3.DLL;设置“Dialog DLL”为TARMSTM.DLL、Parameter”为-pSTM32F103ZE;
除了上图设置还需要设置“Cortex-M Target Driver Setup”中的“Flash Download”选中 Reset and Run,如下图
run
ST-Link实物连接如下图:
实物连接
1-3)修改程序编译结果为hex文件
mdk5默认输出是不选中Create Hex File选项的,我们需要选中,如下图:
outfile

按照上面步骤设置好环境后,编译下载到板子上,跑马灯程序即可运行,运行效果如下图:
实物效果

碍于篇幅长度,问题中的2~5后续文章会一一解释记录。

2015-11-04 09:39:13 rl529014 阅读数 15911

(1)首先当然是要购买“51单片机最小系统的散件”,淘宝上面卖的比较多,散件的话一般二、三十元左右,其次还需要购买一个电烙铁,一般30瓦的就足够了,这里我就讲一讲51单片机散件的焊接方法和注意事项。

(2)51单片机散件如下:

注意:购买到散件后第一步不是马上焊接,而是仔细看看卖家有没有少件,由于散件很多,少发元器件的情况也是存在的。

(3)如果元器件和电烙铁都备好了,下面就可以开始焊接了。

焊接的时候注意一下两点:

A.一定要注意正负极性,像LED灯,蜂鸣器等元器件是分正负极的,不注意的话就会烧毁元器件。

B.焊接时的顺序是从小到大,按元器件的大小顺序焊接。本人在焊接的时候就没有注意到这点,所以在焊接的时候很受伤。

C.关于电烙铁使用的方法我就不说了,用起来很简单的,特别注意提锡的时候垂直往上提,尽量别让飞溅的锡污染板子。

(4)焊接完成后如下图;

http://my.csdn.net/my/album/show/319161   ,这是我的相册链接,相册是高清图。

(实在抱歉,CSDN上传图片的大小是2M,图片太大了,显示不了)

(5)焊接完成后,接下来就是通电了,看看焊接是否成功。

如下图,如果灯亮了,说明焊接阶段没有出现问题,也就是焊接步骤OK啦。


(6)下面是向单片机烧录程序,需要以下工具:串口通讯线,电源线,电脑需要安装Keil编译器,烧录软件,

          没有串口驱动的还需要下载安装一下串口驱动。

A.首先在Keil编译器里面输入流水灯的代码:

#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
void delay(uint z);
main()
{  
uchar aa;   
while(1)
{
aa=0xfe;
P1=aa;
delay(100);
while(1)
{
aa=_crol_(aa,1);
P0=aa;
P1=aa;
P2=aa;
delay(100);
}
}
}
void delay(uint z)
{
uint x,y;
for(x=z;x>0;x--)
for(y=110;y>0;y--);
}

然后连接、编译源程序,主要目的是生成流水灯的HEX文件。如何生成Hex文件可以百度,一搜一大把。

B.连接单片机最小系统的电源线和串口下载线。

如下图:




C.接下来是打开烧录程序,我使用的是STC-ISP4.80版本,界面如下:



单击“打开程序按钮”,找到刚刚生成的HEX文件,然后点击“DownLOad/下载”按钮,将程序烧录到单片机里面去,

接下来再断电,重启,就可以看到单片机流水灯效果了。


D:流水灯效果图:




到此51单片机最小系统的焊接,程序烧录就讲解完成了。


注:烧录程序在CSDN里面就能下载到,输入STC-ISP就可以搜索到,我也就不上传了,

我上传一个51单片机串口驱动程序:http://download.csdn.net/detail/rl529014/9239627

stm32f405最小系统

阅读数 216