完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
`````` 本帖最后由 friend0720 于 2016-9-23 10:18 编辑 前言 单片机是一门综合技术,它要求学习者具有一定的电子电路基础,和一定的 C 语言或汇编语言基础。当然如果你学习过《微机原理》那就更好了。C 语言是目前单片机开发最常用到的开发语言,因此请务必学好 C 语言。 同时单片机又是一门实践性很强的技术,如果不亲自动手搭建电路编写程序,是很难真正学会单片机的。下面说说单片机学习的主要过程: 1. 确定一款单片机作为自己的学习目标。目前主流8位单片机有stm8、AVR、PIC等。 2. 搜集该型单片机的各种学习资料。比如书籍、论坛帖子、视频教程等。 3. 下载并搭建软件开发环境 4. 购买硬件开发工具、烧录器、调试器、各种元器件。搭建单片机最小系统,编写程序驱动各种片内资源。 5. 扩展外围电路,并为之编写驱动程序。 后续本人将以ATmega16单片机为例,介绍具体的学习过程。下面是未来我们开发板的大致模样。 送给初学者的一句话:“勿在浮沙筑高台” 遥远的海 (待续) 附件加密!请勿下载! ``````
评分
|
||
相关推荐
715 个讨论
|
||
|
|
|
|
|
|
本帖最后由 friend0720 于 2016-3-16 07:35 编辑
第六节 I/O口应用之按键扫描 上一节介绍了使用I/O口控制LED及蜂鸣器的方法,本节介绍使用I/O口驱动3*3矩阵键盘的方法。 6.1 3*3矩阵键盘原理图 我们使用PB口的PB[0-5]来驱动3*3矩阵键盘,其中PB[0-2]为列扫描线,PB[3-5]为行扫描线。原理图如下: 6.2 矩阵键盘在开发板上的布局 3*3矩阵键盘位于开发板右下角,需要注意的是Atmega16是倒置在开发板上的。图上画红圈的地方是PB口。 6.3 程序及说明 /* ----------------------------------------------------------------------------------------- 工程: Test系列工程 环境: AVR Studio4.17 + WinAVR2010 设备: Atmega16 作者: 遥远的海 日期: 2015-06-13 说明: 3*3矩阵键盘扫描程序 ----------------------------------------------------------------------------------------- */ #include #include //--------------------------- //数据类型 //--------------------------- typedef unsigned char U8; //定义8位无符号数 typedef signed char S8; //定义8位有符号数 typedef unsigned int U16; //定义16位无符号数 typedef signed int S16; //定义16位有符号数 typedef unsigned long U32; //定义32位无符号数 typedef signed long S32; //定义32位有符号数 typedef unsigned char * PU8; //定义8位无符号指针 typedef signed char * PS8; //定义8位有符号指针 //--------------------------- //空指令 //--------------------------- #define NOP() __asm("nop") //C 语言中内嵌汇编空指令,起到延时一个指令周期的作用 //--------------------------- //调试用LED 占用PD7(21)脚 //--------------------------- #define LED_BIT 0x80 #define LED_Init() DDRD |= LED_BIT //配置 PD7 口为输出 #define LED_OFF() PORTD&=~LED_BIT //PD7 低电平 LED 灭 #define LED_ON() PORTD|= LED_BIT //PD7 高电平 LED 亮 //--------------------------- //蜂鸣器 占用 PD6 (20) 脚 //--------------------------- #define BEEP_BIT 0x40 #define BEEP_Init() DDRD |= BEEP_BIT //配置 PD6 口为输出 #define BEEP_OFF() PORTD|= BEEP_BIT //PD6 高电平 蜂鸣器不响 #define BEEP_ON() PORTD&=~BEEP_BIT //PD6 低电平 蜂鸣器响 //--------------------------- //键码,共9个按键 //--------------------------- #define KEY_CODE0 0 #define KEY_CODE1 1 #define KEY_CODE2 2 #define KEY_CODE3 3 #define KEY_CODE4 4 #define KEY_CODE5 5 #define KEY_CODE6 6 #define KEY_CODE7 7 #define KEY_CODE8 8 #define KEY_CODE_NONE 0xFF //无按键被下时的键码 #define KEY_CNT 9 //按键数量 U8 g_ucKeyCode; //当前键码 //--------------------------- //3*3矩阵键值表 //--------------------------- const U8 m_CodeTable[KEY_CNT]={27,29,30,43,45,46,51,53,54}; /* ----------------------------------------------------------------------------------------- 函数 DelayUS 功能 16MHz系统时钟下实现延时 n (n<256)微秒 入口 U8 n 出口 void 说明 while(n--) 占用3条汇编指令,加上13条空指令共16条汇编指令,16MHz系统时钟下整好1微妙。 ----------------------------------------------------------------------------------------- */ void DelayUS(U8 n) { while(n--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } /* ----------------------------------------------------------------------------------------- 函数 DelayMS 功能 16MHz系统时钟下实现延时 n 毫秒 入口 U8 n 出口 void 说明 16MHz系统时钟下实现延时 n 毫秒。这个毫秒级的延时函数不太精准。 实际工程中毫秒级的延时应该使用定时器来完成。 ----------------------------------------------------------------------------------------- */ void DelayMS(U16 n) { U16 i; while(n--) { i=1000; while(i--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } } /* ----------------------------------------------------------------------------------------- 键盘初始化函数 ----------------------------------------------------------------------------------------- */ void KEY_Init(void) { g_ucKeyCode=KEY_CODE_NONE; } /* ----------------------------------------------------------------------------------------- 键盘扫描函数,使用状态机写法,目前使用软件短延时。可满足实时性不高的一般性应用 ----------------------------------------------------------------------------------------- */ void KEY_Scan(void) { U8 i; U8 ucCode=0; //静态变量,不在堆栈内,和全局变量消耗同样多的内存。 static U8 ucTemp0=0; static U8 ucTemp1=0; static U8 ucStep=0; static U8 ucDelay=0; static U16 usDlyCnt=0; static U16 usDlyCnt1=0; //判断扫描程序是否处于延时状态 if(usDlyCnt1>0) { //扫描函数处于延时中,避免按键快速重复 DelayUS(20); usDlyCnt1--; return; } switch(ucStep) { case 0: DDRB &= 0b11000000; //B口方向寄存器低六位清零 DDRB |= 0b00000111; //PB[3-5]行输入,PB[0-2]列输出 PORTB&= 0b11000000; //PORTB 低六位清零 PORTB|= 0b00111000; //PB[3-5]行上拉电阻,PB[0-2]列输出低电平 DelayUS(10); //短延时,等待电平转换结束 ucStep++; break; case 1: ucTemp0=PINB & 0b00111111; //读取PB[0-5]电平状态 if(0b00111000 == ucTemp0) //无键按下 { ucStep=0; } else { ucStep++; ucDelay=100; //设置延时周期 } break; case 2: if(ucDelay>0) //判断是否处于延时中。 { ucDelay--; DelayUS(50); //延时50us //延时周期内端口电平变化说明按键抖动或受到干扰。 if(ucTemp0 != (PINB & 0b00111111)) ucStep=0; } else { ucStep++; } break; case 3: DDRB &= 0b11000000; //B口方向寄存器低六位清零 DDRB |= 0b00111000; //PB[3-5]行输出,PB[0-2]列输入 PORTB&= 0b11000000; //PORTB 低六位清零 PORTB|= 0b00000111; //PB[3-5]行输出低电平,PB[0-2]列上拉电阻 DelayUS(10); //短延时,等待电平转换结束 ucStep++; break; case 4: ucTemp1=PINB & 0b00111111; //读取PB[0-5]电平状态 if(0b00000111 == ucTemp1) //无键按下 { ucStep=0; } else { ucStep++; ucDelay=100; //设置延时周期 } break; case 5: if(ucDelay>0) //判断是否处于延时中。 { ucDelay--; DelayUS(50); //延时50us //延时周期内端口电平变化说明按键抖动或受到干扰。 if(ucTemp1 != (PINB & 0b00111111)) ucStep=0; } else { ucStep++; } break; case 6: ucCode = (ucTemp0 & 0b00111000) + (ucTemp1 & 0b00000111); for(i=0;i { if(ucCode == m_CodeTable) { //这里获取建值 g_ucKeyCode=i; usDlyCnt1=5000; //按键扫描延时周期 5000*DelayUS(20)=100毫秒。 //调整这个参数可以控制每秒钟按键重复次数。 } } ucStep++; default: ucStep=0; break; } } /* ----------------------------------------------------------------------------------------- 按键服务程序 ----------------------------------------------------------------------------------------- */ void KEY_Service(void) { switch(g_ucKeyCode) { case KEY_CODE0: LED_ON(); break; case KEY_CODE1: LED_OFF(); break; case KEY_CODE2: BEEP_ON(); break; case KEY_CODE3: BEEP_OFF(); break; case KEY_CODE4: break; case KEY_CODE5: break; case KEY_CODE6: break; case KEY_CODE7: break; case KEY_CODE8: break; default: break; } g_ucKeyCode=KEY_CODE_NONE; } /* ----------------------------------------------------------------------------------------- 函数: main 功能: C 语言主函数 入口: void 出口: int 备注: 无 ----------------------------------------------------------------------------------------- */ int main(void) { //调试用LED LED_Init(); //蜂鸣器 BEEP_Init(); BEEP_OFF(); //键盘初始化 KEY_Init(); while(1) { KEY_Scan(); //按键扫描 KEY_Service(); //按键服务 } return 0; } /* ----------------------------------------------------------------------------------------- end of file ----------------------------------------------------------------------------------------- */ WinAVR 2010的过度优化使程序仿真调试变得困难。廉价仿真器的效果也很一般,但总比没有强。希望大家尽快适应。比较起来还是STM32的开发环境好用。 代码本人亲自编写,调试通过,如有错漏或不足敬请指正。代码缩进量请自行调整。 “8位机编程中请使用微妙级时间观念思考问题” “勿在浮沙筑高台” 遥远的海 (待续) |
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 19:02 编辑
第七节IO口应用之74HC595 在单片机的实际应用过程中,经常会遇到 IO口不足的情况,这时我们就需要对IO口进行扩展。扩展IO口有很多方法,使用74HC595就是其中常用方法之一。 7.1 74HC595简介: 74HC595芯片是一种串入并出的芯片,在电子显示屏制作当中有广泛的应用。74HC595具有8位移位寄存器和一个存储器,三态输出功能。 移位寄存器和存储器有相互独立的时钟。数据在SH_cp(移位寄存器时钟输入)的上升沿输入到移位寄存器中,在ST_cp(存储器时钟输入)的上升沿输入到存储寄存器中去。如果两个时钟连在一起,则移位寄存器总是比存储寄存器早一个脉冲。当OE为低电平时,存储器内容输出到Q[7:0]。当MR为低电平时,所有寄存器清零。 7.2引脚说明: Q0…Q7 8位并行数据输出,其中Q0为第15脚 GND 第8脚 地 Q7' 第9脚 串行数据输出 MR 第10脚 主复位(低电平),低电平时清除寄存器内容。 SHCP 第11脚 移位寄存器时钟输入 STCP 第12脚 存储寄存器时钟输入 OE 第13脚 输出有效(低电平),低电平时8位存储器内容输出到 Q[7:0]。 DS 第14脚 串行数据输入 VCC 第16脚 电源 7.3 74HC595在开发板上的位置: 下图中红圈圈中的地方就是74HC595芯片。它的第一脚朝向单片机。 7.4 电路图 我们使用PA口的PA[7:4]控制74HC595,实际应用中可将74HC595的OE脚直接接地,这样就可以用三根信号线控制74HC595,这里使用四根信号线控制74HC595,电路原理图如下: 7.5 程序 /* ----------------------------------------------------------------------------------------- 工程: Test系列工程 环境: AVR Studio4.17 + WinAVR2010 设备: Atmega16 作者: 遥远的海 日期: 2015-06-16 说明: 74HC595 驱动 ----------------------------------------------------------------------------------------- */ #include #include //--------------------------- //数据类型 //--------------------------- typedef unsigned char U8; //定义8位无符号数 typedef signed char S8; //定义8位有符号数 typedef unsigned int U16; //定义16位无符号数 typedef signed int S16; //定义16位有符号数 typedef unsigned long U32; //定义32位无符号数 typedef signed long S32; //定义32位有符号数 typedef unsigned char * PU8; //定义8位无符号指针 typedef signed char * PS8; //定义8位有符号指针 //--------------------------- //空指令 //--------------------------- #define NOP() __asm("nop") //C 语言中内嵌汇编空指令,起到延时一个指令周期的作用 //--------------------------- //调试用LED 占用PD7(21)脚 //--------------------------- #define LED_BIT 0x80 #define LED_Init() DDRD |= LED_BIT //配置 PD7 口为输出 #define LED_OFF() PORTD&=~LED_BIT //PD7 低电平 LED 灭 #define LED_ON() PORTD|= LED_BIT //PD7 高电平 LED 亮 //--------------------------- //蜂鸣器 占用 PD6 (20) 脚 //--------------------------- #define BEEP_BIT 0x40 #define BEEP_Init() DDRD |= BEEP_BIT //配置 PD6 口为输出 #define BEEP_OFF() PORTD|= BEEP_BIT //PD6 高电平 蜂鸣器不响 #define BEEP_ON() PORTD&=~BEEP_BIT //PD6 低电平 蜂鸣器响 /* ----------------------------------------------------------------------------------------- 函数 DelayUS 功能 16MHz系统时钟下实现延时 n (0 入口 U8 n 出口 void 说明 while(n--) 占用3条汇编指令,加上13条空指令共16条汇编指令,16MHz系统时钟下整好1微妙。 ----------------------------------------------------------------------------------------- */ void DelayUS(U8 n) { while(n--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } /* ----------------------------------------------------------------------------------------- 函数 DelayMS 功能 16MHz系统时钟下实现延时 n 毫秒 入口 U8 n 出口 void 说明 16MHz系统时钟下实现延时 n 毫秒。这个毫秒级的延时函数不太精准。 实际工程中毫秒级的延时应该使用定时器来完成。 ----------------------------------------------------------------------------------------- */ void DelayMS(U16 n) { U16 i; while(n--) { i=1000; while(i--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } } /* ----------------------------------------------------------------------------------------- 位操作宏定义 ----------------------------------------------------------------------------------------- */ #define HC595_DS_L() PORTA &=~0x80 //HC595_DS 输出低电平 #define HC595_DS_H() PORTA |= 0x80 //HC595_DS 输出高电平 #define HC595_OE_L() PORTA &=~0x40 //HC595_OE 输出低电平 #define HC595_OE_H() PORTA |= 0x40 //HC595_OE 输出高电平 #define HC595_ST_L() PORTA &=~0x20 //HC595_ST 输出低电平 #define HC595_ST_H() PORTA |= 0x20 //HC595_ST 输出高电平 #define HC595_SH_L() PORTA &=~0x10 //HC595_SH 输出低电平 #define HC595_SH_H() PORTA |= 0x10 //HC595_SH 输出高电平 /* ----------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- */ void HC595_Init(void) { DDRA |= 0xF0; //PA[7:4]配置为输出 HC595_DS_L(); //数据线输出低电平 HC595_ST_L(); //存储器时钟低电平 HC595_SH_L(); //移位寄存器时钟低电平 HC595_OE_H(); //OE高电平,禁止存储器内容输出到Q[7:0] } /* ----------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- */ void HC595_Write(U8 ucData) { U8 n=8; HC595_OE_H(); //OE高电平,禁止存储器内容输出到Q[7:0] while(n--) { //按位发送数据,先发送高位。 if(ucData&0x80) { HC595_DS_H(); } else { HC595_DS_L(); } //移位时钟产生一个上升沿,完成一次移位操作。 HC595_SH_L(); //移位时钟低电平 DelayUS(1); //短延时 HC595_SH_H(); //移位时钟高电平 DelayUS(1); //短延时 ucData<<=1; } //存储器时钟产生一个上升沿,完成一次存储操作 HC595_ST_L(); DelayUS(1); //短延时 HC595_ST_H(); DelayUS(1); //短延时 HC595_DS_L(); //数据线输出低电平 HC595_ST_L(); //存储器时钟低电平 HC595_SH_L(); //移位寄存器时钟低电平 HC595_OE_L(); //OE低电平,允许存储器内容输出到Q[7:0] } /* ----------------------------------------------------------------------------------------- 函数: main 功能: C 语言主函数 入口: void 出口: int 备注: 无 ----------------------------------------------------------------------------------------- */ int main(void) { U8 c=0; //调试用LED LED_Init(); //初始化 74HC595 HC595_Init(); while(1) { HC595_Write(c++); DelayMS(1000); } return 0; } /* ----------------------------------------------------------------------------------------- end of file ----------------------------------------------------------------------------------------- */ 74HC595的驱动程序很简单,也很好理解,后面我们将用它来驱动板子上的数码管。 “勿在浮沙筑高台” 遥远的海 (待续) |
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 19:05 编辑
第八节 IO口应用之数码管 上一节讲了74HC595,这一节讲讲数码管。数码管在单片机领域应用广泛,而且比较简单,上一节讲的74HC595就是为驱动数码管做准备的。 8.1 数码管显示原理 (引自 http://blog.csdn.net/yafeilinux/article/details/4751631) 我们最常用的是七段式和八段式LED数码管,八段比七段多了一个小数点,其他的基本相同。所谓的八段就是指数码管里有八个小LED发光二极管,通过控制不同的LED的亮灭来显示出不同的字形。数码管又分为共阴极和共阳极两种类型,其实共阴极就是将八个LED的阴极连在一起,让其接地,这样给任何一个LED的另一端高电平,它便能点亮。而共阳极就是将八个LED的阳极连在一起。其原理图如下。 其中引脚图的两个COM端连在一起,是公共端,共阴数码管要将其接地,共阳数码管将其接正5伏电源。一个八段数码管称为一位,多个数码管并列在一起可构成多位数码管,它们的段选线(即a,b,c,d,e,f,g,dp)连在一起,而各自的公共端称为位选线。显示时,都从段选线送入字符编码,而选中哪个位选线,那个数码管便会被点亮。 数码管的8段,对应一个字节的8位,a对应最低位,dp对应最高位。所以如果想让数码管显示数字0,那么共阴数码管的字符编码为00111111,即0x3f;共阳数码管的字符编码为11000000,即0xc0。可以看出两个编码的各位正好相反。如下图。 8.2 数码管在开发板上的位置及显示效果 4位数码管的显示十分稳定,没有任何闪烁感。 8.3 原理图 数码管驱动电路一般分为静态显示方式和动态显示方式两种,我们使用动态显示方式驱动4位共阳极数码管。74HC595驱动数码管的8个段,PA[3:0] 控制 4个s8550三极管做位选,低电平有效。原理图如下: 8.4 程序及说明 /* ----------------------------------------------------------------------------------------- 工程: Test系列工程 环境: AVR Studio4.17 + WinAVR2010 设备: Atmega16 作者: 遥远的海 日期: 2015-06-16 说明: 数码管 驱动 ----------------------------------------------------------------------------------------- */ #include #include //--------------------------- //数据类型 //--------------------------- typedef unsigned char U8; //定义8位无符号数 typedef signed char S8; //定义8位有符号数 typedef unsigned int U16; //定义16位无符号数 typedef signed int S16; //定义16位有符号数 typedef unsigned long U32; //定义32位无符号数 typedef signed long S32; //定义32位有符号数 typedef unsigned char * PU8; //定义8位无符号指针 typedef signed char * PS8; //定义8位有符号指针 //--------------------------- //空指令 //--------------------------- #define NOP() __asm("nop") //C 语言中内嵌汇编空指令,起到延时一个指令周期的作用 /* ----------------------------------------------------------------------------------------- 函数 DelayMS 功能 16MHz系统时钟下实现延时 n 毫秒 入口 U8 n 出口 void 说明 16MHz系统时钟下实现延时 n 毫秒。这个毫秒级的延时函数不太精准。 实际工程中毫秒级的延时应该使用定时器来完成。 ----------------------------------------------------------------------------------------- */ void DelayMS(U16 n) { U16 i; while(n--) { i=1000; while(i--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } } /* ----------------------------------------------------------------------------------------- 74HC595 位操作宏定义 ----------------------------------------------------------------------------------------- */ #define HC595_DS_L() PORTA &=~0x80 //HC595_DS 输出低电平 #define HC595_DS_H() PORTA |= 0x80 //HC595_DS 输出高电平 #define HC595_OE_L() PORTA &=~0x40 //HC595_OE 输出低电平 #define HC595_OE_H() PORTA |= 0x40 //HC595_OE 输出高电平 #define HC595_ST_L() PORTA &=~0x20 //HC595_ST 输出低电平 #define HC595_ST_H() PORTA |= 0x20 //HC595_ST 输出高电平 #define HC595_SH_L() PORTA &=~0x10 //HC595_SH 输出低电平 #define HC595_SH_H() PORTA |= 0x10 //HC595_SH 输出高电平 #define HC595_ALL_L() PORTA &= 0x0F //HC595所有控制信号低电平 /* ----------------------------------------------------------------------------------------- 初始化74HC595 ----------------------------------------------------------------------------------------- */ void HC595_Init(void) { DDRA |= 0xF0; //PA[7:4]配置为输出 HC595_DS_L(); //数据线输出低电平 HC595_ST_L(); //存储器时钟低电平 HC595_SH_L(); //移位寄存器时钟低电平 HC595_OE_H(); //OE高电平,禁止存储器内容输出到Q[7:0] } /* ----------------------------------------------------------------------------------------- 写8位数据到74HC595 ----------------------------------------------------------------------------------------- */ void HC595_Write(U8 ucData) { U8 n=8; HC595_OE_H(); //OE高电平,禁止存储器内容输出到Q[7:0] while(n--) { //按位发送数据,先发送高位。 if(ucData&0x80) { HC595_DS_H(); } else { HC595_DS_L(); } //移位时钟产生一个上升沿,完成一次移位操作。 HC595_SH_L(); //移位时钟低电平 NOP(); //短延时 HC595_SH_H(); //移位时钟高电平 NOP(); //短延时 ucData<<=1; } //存储器时钟产生一个上升沿,完成一次存储操作 HC595_ST_L(); NOP(); //短延时 HC595_ST_H(); NOP(); //短延时 HC595_ALL_L(); } /* ----------------------------------------------------------------------------------------- 数码管 宏定义 ----------------------------------------------------------------------------------------- */ #define NLED_OFF() PORTA |= 0x0F //PA[0:3]输出高电平,数码管4位全不选 #define NLED_ON() PORTA &= 0xF0 //PA[0:3]输出低电平,数码管4位全选 #define NLED_0L() PORTA &= 0b11111110 //PA[0]低电平,对应数码管亮 #define NLED_0H() PORTA |= 0b00000001 //PA[0]高电平,对应数码管灭 #define NLED_1L() PORTA &= 0b11111101 //PA[1]低电平,对应数码管亮 #define NLED_1H() PORTA |= 0b00000010 //PA[1]高电平,对应数码管灭 #define NLED_2L() PORTA &= 0b11111011 //PA[2]低电平,对应数码管亮 #define NLED_2H() PORTA |= 0b00000100 //PA[2]高电平,对应数码管灭 #define NLED_3L() PORTA &= 0b11110111 //PA[3]低电平,对应数码管亮 #define NLED_3H() PORTA |= 0b00001000 //PA[3]高电平,对应数码管灭 #define NLED_DELAY_CNT 3 //每位数码管动态显示时间,单位毫秒 #define NLED_CODE_CNT 10 //共阳极数码管码表长度 //共阳极7段数码管码表,数字 0-9 static U8 m_ucCodeTable[NLED_CODE_CNT]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90}; /* ----------------------------------------------------------------------------------------- 数码管初始化,使用PA[3:0]控制数码管位选 ----------------------------------------------------------------------------------------- */ void NLED_Init(void) { //初始化 74HC595 HC595_Init(); DDRA |= 0x0F; //PA[0:3]配置为输出 //让数码管全闪几下,看看有没有不亮的。 HC595_Write(0x00); //数码管段全选 NLED_ON(); DelayMS(500); NLED_OFF(); DelayMS(500); NLED_ON(); DelayMS(500); NLED_OFF(); //数码管全灭 } /* ----------------------------------------------------------------------------------------- 数码管显示服务函数 ----------------------------------------------------------------------------------------- */ void NLED_DisplayService(void) { static U8 ucStep; NLED_OFF(); //数码管全灭 switch(ucStep) { case 0: HC595_Write(m_ucCodeTable[8]); NLED_0L(); ucStep++; break; case 1: HC595_Write(m_ucCodeTable[4]); NLED_1L(); ucStep++; break; case 2: HC595_Write(m_ucCodeTable[3]); NLED_2L(); ucStep++; break; case 3: HC595_Write(m_ucCodeTable[9]); NLED_3L(); ucStep++; break; default: ucStep=0; return; break; } DelayMS(NLED_DELAY_CNT); } /* ----------------------------------------------------------------------------------------- 函数: main 功能: C 语言主函数 入口: void 出口: int 备注: 无 ----------------------------------------------------------------------------------------- */ int main(void) { //初始化数码管 NLED_Init(); while(1) { NLED_DisplayService(); } return 0; } /* ----------------------------------------------------------------------------------------- end of file ----------------------------------------------------------------------------------------- */ 送给初学者一句话:“代码是写给人读的”。 “勿在浮沙筑高台” 遥远的海 (待续) |
|
|
|
|
|
好帖。。。顶。。。
|
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 19:08 编辑
第九节 IO口应用之DS18B20 DS18B20是常用的温度传感器,本节介绍DS18B20的驱动方法。 9.1DS18B20的特点: DS18B20单线数字温度传感器,即“一线器件”,其具有独特的优点: ( 1)采用单总线的接口方式 与微处理器连接时仅需要一条口线即可实现微处理器与 DS18B20 的双向通讯。单总线具有经济性好,抗干扰能力强,适合于恶劣环境的现场温度测量,使用方便等优点,使用户可轻松地组建传感器网络,为测量系统的构建引入全新概念。 ( 2)测量温度范围宽,测量精度高 DS18B20 的测量范围为 -55 ℃~+ 125 ℃; 在 -10~+ 85°C范围内,精度为 ± 0.5°C 。 ( 3)在使用中不需要任何外围元件。 ( 4)持多点组网功能 多个 DS18B20 可以并联在惟一的单线上,实现多点测温。 ( 5)供电方式灵活DS18B20 可以通过内部寄生电路从数据线上获取电源。因此,当数据线上的时序满足一定的要求时,可以不接外部电源,从而使系统结构更趋简单,可靠性更高。 ( 6)测量参数可配置DS18B20 的测量分辨率可通过程序设定 9~12 位。 ( 7) 负压特性电源极性接反时,温度计不会因发热而烧毁,但不能正常工作。 ( 8)掉电保护功能DS18B20 内部含有 EEPROM ,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。 DS18B20具有体积更小、适用电压更宽、更经济、可选更小的封装方式,更宽的电压适用范围,适合于构建自己的经济的测温系统,因此也就被设计者们所青睐。 9.2 原理图 9.3 DS18B20在开发板上的位置 下图画红圈的地方就是DS18B20,4.7K上拉电阻在板子后面,我们使用PC6来驱动它。 9.4程序及说明 /* ----------------------------------------------------------------------------------------- 工程: Test系列工程 环境: AVR Studio4.17 + WinAVR2010 设备: Atmega16 作者: 遥远的海 日期: 2015-06-18 说明: DS18B20驱动程序 ----------------------------------------------------------------------------------------- */ #include #include //--------------------------- //数据类型 //--------------------------- typedef unsigned char U8; //定义8位无符号数 typedef signed char S8; //定义8位有符号数 typedef unsigned int U16; //定义16位无符号数 typedef signed int S16; //定义16位有符号数 typedef unsigned long U32; //定义32位无符号数 typedef signed long S32; //定义32位有符号数 typedef unsigned char * PU8; //定义8位无符号指针 typedef signed char * PS8; //定义8位有符号指针 #define TRUE (U8)1 #define FALSE (U8)0 //--------------------------- //空指令 //--------------------------- #define NOP() __asm("nop") //C 语言中内嵌汇编空指令,起到延时一个指令周期的作用 /* ----------------------------------------------------------------------------------------- 函数 DelayUS 功能 16MHz系统时钟下实现延时 n (n<256)微秒 入口 U8 n 出口 void 说明 while(n--) 占用3条汇编指令,加上13条空指令共16条汇编指令,16MHz系统时钟下整好1微妙。 ----------------------------------------------------------------------------------------- */ void DelayUS(U8 n) { while(n--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } /* ----------------------------------------------------------------------------------------- 函数 DelayMS 功能 16MHz系统时钟下实现延时 n 毫秒 入口 U8 n 出口 void 说明 16MHz系统时钟下实现延时 n 毫秒。这个毫秒级的延时函数不太精准。 实际工程中毫秒级的延时应该使用定时器来完成。 ----------------------------------------------------------------------------------------- */ void DelayMS(U16 n) { U16 i; while(n--) { i=1000; while(i--) { NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); NOP(); } } } /* ----------------------------------------------------------------------------------------- 使用 PC6脚读写DS18B20 ----------------------------------------------------------------------------------------- */ #define DQ_BIT 0x40 //1< #define DQ_IN() DDRC &=~DQ_BIT //PC6输入 #define DQ_OUT() DDRC |= DQ_BIT //PC6输出 #define DQ_H() PORTC|= DQ_BIT //PC6输出高电平 #define DQ_L() PORTC&=~DQ_BIT //PC6输出低电平 #define DQ_R() (PINC & DQ_BIT) //读取PC6电平状态 /* ----------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------- */ void DS18B20_Init(void) { DQ_OUT(); DQ_H(); } /* ----------------------------------------------------------------------------------------- 单总线起始信号 主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平, 并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。 若无低电平出现一直都是高电平说明总线上无器件应答。 做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有, 在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机 本器件已做好准备。若没有检测到就一直在检测等待。 ----------------------------------------------------------------------------------------- */ void DS18B20_Start(void) { U8 n; DQ_OUT(); //控制脚配置为输出 //起始总线,单总线产生一个下降沿,低电平保持480-960微秒。 DQ_H(); //DQ 高电平 NOP(); //短延时 NOP(); //短延时 DQ_L(); //DQ 低电平 //延时500微妙 n=5; while(n--) { DelayUS(100); } //等待器件应答,器件应该在总线低电平变高后15-60微秒内将总线拉低60-240微秒,表示应答 //我们这里没有检查应答信号。 DQ_H(); //DQ 高电平 //延时500微妙 n=5; while(n--) { DelayUS(100); } } /* ----------------------------------------------------------------------------------------- DS18B20写操作 写周期最少为60微秒,最长不超过120微秒。写周期一开始主机先把总线拉低1微秒表示写周期开始。 随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1, 在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20 则在检测到总线被拉低后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1, 若采样期内总线为低电平则为0。 ----------------------------------------------------------------------------------------- */ void DS18B20_Write(U8 ucCmd) { U8 n; //发送8位数据 n=8; while(n--) { DQ_L(); //DQ 低电平,产生一个下降沿 DelayUS(1); if(ucCmd & 0x01) //先发送低位 { DQ_H(); } ucCmd>>=1; DelayUS(60); //器件在总线拉低后15-60us间对总线采样,此时总线高电平表示1 ,低电平表示 0 DQ_H(); //DQ 高电平 } } /* ----------------------------------------------------------------------------------------- DS18B20读操作 对于读数据操作时序也分为读0时序和读1时序两个过程。读时隙是主机把单总线拉低之后, 在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线 被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则 释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒 在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为 高电平则确认为1。完成一个读时序过程,至少需要60us才能完成 ----------------------------------------------------------------------------------------- */ U8 DS18B20_Read(void) { U8 n,temp=0; //接收8位数据 for(n=0;n<8;n++) { DQ_L(); //拉低电平启动数据传送 DelayUS(1); DQ_H(); //拉高总线电平,等待器件将数据写到总线上 DelayUS(1); if(DQ_R()==DQ_BIT) { temp |= 1< } DelayUS(60); DQ_H(); } return temp; } /* ----------------------------------------------------------------------------------------- 读取 DS18B20温度值 每次读写操作前必须启动一次总线 ----------------------------------------------------------------------------------------- */ U8 g_ucTmpFlag=FALSE; //标记温度是否为负数 U8 DS18B20_GetTemp(void) { U8 temh,teml,data,tmp; DS18B20_Start(); //启动单总线 DS18B20_Write(0xCC); //发送忽略ROM命令 DS18B20_Write(0x44); //发送温度转换命令 DelayMS(750); //延时等待温度转换结束 DS18B20_Start(); //重启单总线 DS18B20_Write(0xCC); //发送忽略ROM命令 DS18B20_Write(0xBE); //发送读暂存命令 teml=DS18B20_Read(); //读数据 temh=DS18B20_Read(); if(255 == teml && 255 == temh) return 255; tmp=(teml>>4)+(temh<<4); if(temh>16) //温度为负值 { data=~tmp+1; // g_ucTmpFlag=TRUE; //负号标志 } else //温度为正值 { data=tmp; // g_ucTmpFlag=FALSE; //正号标志 } if(data>125) return 255; //数据错误 return data; } /* ----------------------------------------------------------------------------------------- 函数: main 功能: C 语言主函数 入口: void 出口: int 备注: 无 ----------------------------------------------------------------------------------------- */ int main(void) { U8 t; DS18B20_Init(); while(1) { t=DS18B20_GetTemp(); DelayMS(1000); } return 0; } /* ----------------------------------------------------------------------------------------- end of file ----------------------------------------------------------------------------------------- */ 到此,我们对IO口的应用介绍告一段落,后续将讲解中断相关的内容。 “勿在浮沙筑高台” 遥远的海 (待续) |
|
|
|
|
|
楼主太强大了,学习了,谢谢楼主分享
|
|
|
|
|
|
本帖最后由 friend0720 于 2016-3-4 00:25 编辑
变量不自动初始化 在C语言中,定义的全局变量如果没有被赋初值,则通常自动设置成0。但有些时候我们希望变量的值在单片机复位后保持不变(类似计算机的热启动),仍然保持复位之前的值。 在 AVRGCC 中,可以将不希望清零的变量设置一个 noinit 属性,这样编译器就不会自动将它清零。例如: char cnt1 __attribute__((section(".noinit"))); |
|
|
|
|
|
学习的好帖。。。顶。。
|
|
|
|
|
|
本帖最后由 friend0720 于 2016-2-25 19:11 编辑
第十节 中断 中断是MPU在执行一个程序时,对系统发生的某个事件做出的一种反应。当事件发生时,MPU会暂停正在执行的程序,保留现场后自动转去处理相应的事件,处理完后再返回断点,继续完成被打断的程序。 10.1 Atmega16中断源: ATMEGA16具有20个中断源和一个复位中断。所有中断源都有独立的中断使能位,当相应的使能位和全局中断使能位都置“1”的情况下,中断才可以发生,相应的中断服务程序才会执行。 10.2 对中断进行操作 GCC的中断操作,是通过关键字 “SIGNAL” 实现的形式如下: SIGNAL(SIG_NAME) { 中断服务程序; } 在使用SIGNAL这个关键字之前,一定要记得包含 10.3 外部中断 ATMEGA16共有3个外部中断(INT0:PD2,INT1:PD3, INT2: PB2),在MCUCR寄存器可以控制外部中断的触发条件(INT2只能沿触发,在MCUCSR中设置) MCU 控制寄存器- MCUCR中的ISC11和ISC10用来控制INT1的中断触发条件: MCUCR中的ISC01和ISC00用来控制INT0的中断触发条件: 通用中断控制寄存器- GICR • Bit 7 – INT1: 使能外部中断请求1 当INT1 为'1’,而且状态寄存器SREG的I 标志置位,相应的外部引脚中断就使能了。 MCU通用控制寄存器– MCUCR的中断敏感电平控制1位 1/0 (ISC11与ISC10)决定中断是 由上升沿、下降沿,还是INT1 电平触发的。只要使能,即使INT1 引脚被配置为输出, 只要引脚电平发生了相应的变化,中断可将产生。 • Bit 6 – INT0: 使能外部中断请求 0 当INT0 为'1’,而且状态寄存器SREG的I 标志置位,相应的外部引脚中断就使能了。 MCU通用控制寄存器– MCUCR的中断敏感电平控制0位 1/0 (ISC01与ISC00)决定中断是 由上升沿、下降沿,还是INT0 电平触发的。只要使能,即使INT0 引脚被配置为输出, 只要引脚电平发生了相应的变化,中断可将产生。 • Bit 5 – INT2: 使能外部中断请求2 当INT2 为'1’,而且状态寄存器SREG的I 标志置位,相应的外部引脚中断就使能了。 MCU通用控制寄存器– MCUCR的中断敏感电平控制2位 1/0 (ISC2与ISC2)决定中断是由 上升沿、下降沿,还是INT2 电平触发的。只要使能,即使INT2 引脚被配置为输出,只 要引脚电平发生了相应的变化,中断可将产生。 通用中断标志寄存器- GIFR • Bit 7 – INTF1: 外部中断标志1 INT1引脚电平发生跳变时触发中断请求,并置位相应的中断标志INTF1。如果SREG的位 I以及GICR寄存器相应的中断使能位INT1为”1”,MCU即跳转到相应的中断向量。进入中 断服务程序之后该标志自动清零。此外,标志位也可以通过写入”1” 来清零。 • Bit 6 – INTF0: 外部中断标志 0 INT0引脚电平发生跳变时触发中断请求,并置位相应的中断标志INTF0。如果SREG的位 I以及GICR寄存器相应的中断使能位INT0为”1”,MCU即跳转到相应的中断向量。进入中 断服务程序之后该标志自动清零。此外,标志位也可以通过写入”1” 来清零。 • Bit 5 – INTF2: 外部中断标志2 INT2引脚电平发生跳变时触发中断请求,并置位相应的中断标志INTF2。如果SREG的位 I以及GICR寄存器相应的中断使能位INT2为”1”,MCU即跳转到相应的中断向量。进入中 断服务程序之后该标志自动清零。此外,标志位也可以通过写入”1” 来清零。注意,当 INT2中断禁用进入某些休眠模式时,该引脚的输入缓冲将禁用。这会导致INTF2标志设置 信号的逻辑变化, 10.4 外部中断的简单应用 原理图: 我们将红外接收管 HS0038B的输出端连接到Atmega16的PD3(INT1)管脚上。当红外遥控器有键按下时,HS0038B的OUT脚将会输出一系列的高低电平,从而引发单片机中断。这只是一个简单的外部中断响应实验,你完全可以使用一个按键来代替HS0038B. 程序及说明: //--------------------------- //总中断 //--------------------------- #define EN_INT() sei() //开中断 #define DS_INT() cli() //关中断 //------------------------------------------ //外部中断1 //------------------------------------------ #define EN_INT1() GICR |= (1< #define DS_INT1() GICR &=~(1< /* --------------------------------------------------------------------------------------- 使用外部中断1 (PD3 第17脚) ISC11 ISC10 说明 0 0 INT1 为低电平时产生中断请求 0 1 INT1 引脚上任意的逻辑电平变化都 1 0 INT1 的下降沿产生异步中断请求 1 1 INT1 的上升沿产生异步中断请求 --------------------------------------------------------------------------------------- */ void INT1_Init(void) { //外部中断通过引脚INT0、INT1 与INT2 触发。只要使能了中断,即使引脚INT0..2 配置 //为输出,只要电平发生了合适的变化,中断也会触发。这个特点可以用来产生软件中断。 //IO口方向 1 输出 0 输入 DDRD &= ~(1< //MCU 控制寄存器包含中断触发控制位与通用MCU 功能 //持续时间大于一个时钟周期的脉冲将触发中断,过短的脉冲则不能保证触发中断。 MCUCR|= (1< MCUCR&=~(1< GIFR |= 1< EN_INT1(); // 外部中断1使能 } /* --------------------------------------------------------------------------------------- 外部中断1响应函数 --------------------------------------------------------------------------------------- */ SIGNAL(SIG_INTERRUPT1) { LED_ON(); //点亮发光二极管 } /* ----------------------------------------------------------------------------------------- 函数: main 功能: C 语言主函数 入口: void 出口: int 备注: 无 ----------------------------------------------------------------------------------------- */ int main(void) { LED_Init(); INT1_Init(); EN_INT(); //开全局中断 while(1) { DelayMS(1000); LED_OFF(); //熄灭LED } return 0; } “勿在浮沙筑高台” 遥远的海 (待续) |
|
|
|
|
|
|
|
|
|
|
|
大家发现什么错误、不足或有什么建议就出来冒个泡。
|
|
|
|
|
|
你正在撰写讨论
如果你是对讨论或其他讨论精选点评或询问,请使用“评论”功能。
690 浏览 0 评论
735 浏览 1 评论
基于瑞萨FPB-RA4E2智能床头灯项目——1编译环境搭建与点亮驱动ws2812全彩LED
624 浏览 0 评论
嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-LCD显示图片编程示例之介绍mmap
1099 浏览 0 评论
《DNESP32S3使用指南-IDF版_V1.6》第二章 常用的C语言知识点
1092 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
11794 浏览 31 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-26 12:16 , Processed in 1.179577 second(s), Total 76, Slave 69 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号