21 分钟
avr单片机
代码基于ATmega16单片机
〇、编程模板
1、main.c模板
#include <iom16v.h>
#include <macros.h>
#include "util.h"
#include "debug.h"
int main(){
//定义变量
//初始化寄存器
//业务
return 0;
}
2、util.h工具库
#ifndef _UTIL_H_
#define _UTIL_H_
//定义数据类型byte
#define byte unsigned char
/**
* 延时函数
* @param ms 延时毫秒数
*/
void delay(unsigned int ms){
byte i;
while(ms--){
for(i=1; i<187; i++){
asm("nop");
}
}
}
/**
* 将寄存器reg第pos[0,8)位置为0
* @param reg 寄存器名
* @param pos 要操作的寄存器位数
* @return 修改后寄存器的值
*/
#define bitSet0(reg,pos) (reg &= ~(1<<(pos)))
/**
* 将寄存器reg第pos[0,8)位置为1
* @param reg 寄存器名
* @param pos 要操作的寄存器位数
* @return 修改后寄存器的值
*/
#define bitSet1(reg,pos) (reg |= (1<<(pos)))
/**
* 将寄存器reg第pos[0,8)位置为val
* @param val 设置的值
* @param reg 寄存器名
* @param pos 要操作的寄存器位数
* @return 修改后寄存器的值
*/
#define bitSet(val,reg,pos) if(val) bitSet1(reg,pos); else bitSet0(reg,pos)
/**
* 获取寄存器reg第pos[0,8)位的值
* @param reg 寄存器名
* @param pos 要操作的寄存器位数
* @return 寄存器reg第pos位的值
*/
#define bitGet(reg,pos) (((reg)>>(pos)) & 1)
#endif
3、debug.h debug库
#ifndef _DEBUG_H_
#define _DEBUG_H_
/**
* 数码管显示,变量值
*/
//设置推出信号前执行的命令
#define OUTBefore() PORTD |= 0xf0
//设置输出禁止
#define Conversion 10
//设置硬件连线情况
#define SERReg PORTA
#define SERPos 0
#define SCLKReg PORTB
#define SCLKPos 1
#define PCLKReg PORTB
#define PCLKPos 0
byte __led_7[16]={0xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
void __HC595Driver(byte sendData) {
byte i;
for(i=0;i<8;i++){
bitSet(bitGet(sendData,7-i),SERReg,SERPos);
bitSet0(SCLKReg,SCLKPos);
bitSet1(SCLKReg,SCLKPos);
}
bitSet0(PCLKReg,PCLKPos);
OUTBefore();
bitSet1(PCLKReg,PCLKPos);
}
byte __getNum(int a, byte pos){
while(pos--){
a/=Conversion;
}
return a%Conversion;
}
void debugVar(int var){
byte i;
DDRD |= (1<<4)|(1<<5)|(1<<6)|(1<<7);
DDRA |= (1<<0);
DDRB |= (1<<0)|(1<<1);
while(1){
for(i=0; i<4; i++){
__HC595Driver(__led_7[__getNum(var,3-i)]);
bitSet0(PORTD,7-i);
}
}
}
void debugReg(byte reg){
byte i;
DDRC = 0xff; //c口数据方向寄存器(0表示输入,1表示输出)
PORTC = 0xff; //c口数据输出寄存器,当输出时表示输出数据,
//当输入时表示是否接入上拉电阻(1:表示上拉,0表示不上拉)
DDRD &= ~(1<<3); //配置d口第3位为输出信号
PORTD|=(1<<3); //配置d口第3位上拉
while(1){
PORTC = ~reg;
}
}
#endif
4、config.h 跨平台配置
//暂未实现
一、通用IO
1、功能表
DDRXn | PORTXn | I/O方式 | 引脚(悬空)状态 | 引脚接法 | PINXn |
---|---|---|---|---|---|
1 | 1 | 输出 | 1 | 被控设备 | x |
1 | 0 | 输出 | 0 | 被控设备 | x |
0 | 0 | 输入 | 0 | 信号源(GCC) | 1 |
0 | 0 | 输入 | 0 | 信号源(悬空、GND) | 0 |
0 | 1 | 输入 | 1 | 信号源(GCC、悬空) | 1 |
0 | 1 | 输入 | 1 | 信号源(GND) | 0 |
2、电路接法与寄存器
(1) 被控设备
LED灯、数码管等用电设备
设备接法 | DDRXn | 设备工作 PORTXn 的值 |
---|---|---|
设备+ 接GCC ,另一端接引脚 | 1 | 0 |
设备- 接GND ,另一端接引脚 | 1 | 1 |
(2)信号源
各种开关
设备接法 | DDRXn | PORTXn | 开关闭合 PINXn 的值 |
---|---|---|---|
一端接GCC ,另一端接引脚 | 0 | 0 | 1 |
一端接GND ,另一端接引脚 | 0 | 1 | 0 |
二、常用模块
1、74HC595
(1) 作用
用于拓展IO接口,将并行数据转换为串行数据,常用与推动数码管
(2) 驱动程序算法描述
开始
第一步:接收输入byte类型的输入到sendData
第二步:
byte i;
for(i=0; i<8; i++){
设置SER的值为 sendData第7-i位的值;
使SCLK产生 从0到1的脉冲(上升沿)//实现方式,先置为0,再置为1
}
第三步:使PCLK 产生从0到1的脉冲(上升沿)
结束
(3)驱动程序算法模板
#ifndef _HC595DRIVER_H_
#define _HC595DRIVER_H_
byte led_7[16]={0xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
//设置进制
#define Conversion 10
//设置推出信号前执行的命令
#define OUTBefore() PORTD |= 0xf0
//设置硬件连线情况
#define SERReg PORTA
#define SERPos 0
#define SCLKReg PORTB
#define SCLKPos 1
#define PCLKReg PORTB
#define PCLKPos 0
byte getNum(int a, byte pos){
while(pos--){
a/=Conversion;
}
return a%Conversion;
}
void HC595Driver(byte sendData) {
byte i;
for(i=0;i<8;i++){
bitSet(bitGet(sendData,7-i),SERReg,SERPos);
bitSet0(SCLKReg,SCLKPos);
bitSet1(SCLKReg,SCLKPos);
}
bitSet0(PCLKReg,PCLKPos);
OUTBefore();
bitSet1(PCLKReg,PCLKPos);
}
byte LEDNumberPos = 0;
void displayLEDNumber(int cnt){
if(LEDNumberPos>=4){
LEDNumberPos = 0;
}
HC595Driver(led_7[getNum(cnt,3-LEDNumberPos)]);
bitSet0(PORTD,7-LEDNumberPos);
LEDNumberPos++;
}
#endif
2、八段数码管
(1)字库
共阳极
byte led_7[16]={0xc0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,
0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
共阴极
byte led_7[16] ={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
三、中断系统
1、外部终端功能寄存器
(1)MCUCR和MCUCSR(配置型) 中断触发方式
MCUCR
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | SM2 | SE | SM1 | SM0 | ISC11 | ISC10 | ISC01 | ISC00 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
说明
ISCn1(MCUCR[3,1]) | ISCn0(MCUCR[2,0]) | 触发方式 |
---|---|---|
0 | 0 | 0触发(低电平) |
0 | 1 | 0->1 或 1->0 触发(下降上升沿) |
1 | 0 | 1->0 触发(下降沿) |
1 | 1 | 0->1 触发(上升沿) |
其中n为INTn号中断
MCUCSR
ISC2 (MCUCSR[6]) | 触发方式 |
---|---|
0 | 1->0 触发(下降沿) |
1 | 0->1 触发(上升沿) |
(2)GICR(配置型) 中断开关
1 表示开
INT1 :GICR[7] INT0 :GICR[6] INT2 :GICR[5]
(3)CIFR(系统型)中断标记
1 表示发生中断
INTF1:CIFR[7] INTF0:CIFR[6] INTF2:CIFR[5]
(4)中断总开关
SEI();
2、外部中断初始化样例
以INT1连接按钮为例,按钮按下触发终端
//中断配置初始化管脚
void init(){
//按钮中断
bitSet0(DDRD,3);
bitSet1(PORTD,3);
MCUCR |= (1<<3)|(1<<1); //中断触发方式,INT0、INT1下降沿触发(根据外设选择)
GICR |= (1<<INT1)|(1<<INT0); //中断开关,INT0、INT1开中断
GIFR=0xC0; //中断标志位清零??
//配置全局中断开
SEI();
}
//中断函数
#pragma interrupt_handler int1_isr:iv_INT1
void int1_isr(void) {
}
四、定时计时器
0、功能介绍
(1)计数功能
寄存器:TCNT0
输入引脚Tn
(2)定时中断
- 溢出中断
- 比较中断
(3)数字信号调制
输出引脚OCn
(PB3
I/O口)
1、功能寄存器
(1)TCNT0 (系统型)计数器
可读写,计数器
(2)OCR0(配置型)比较值
当配置发生比较匹配中断时配置
(3)TIMSK(配置型)中断开关
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | OCIE2 | TOIE2 | TICIE1 | OCIE1A | OCIE1A | TOIE1 | OCIE0 | TOIE0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
OCIEn:TIMSK[7,1]:比较中断开关 TOIEn:TIMSK[6,0]:溢出中断开关
(4)TIFR(系统型)中断标记
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | OCF2 | TOV2 | ICF1 | OCF1A | OCF1B | TOV1 | OCF0 | TOV0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
OCFn:FIFR[7,1]:比较中断标志 TOVn:FIFR[6,0]:溢出中断标志
(5)TCCR0(配置型)功能选择
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | FOC0 | WGM00 | COM01 | COM00 | WGM01 | CS02 | CS01 | CS00 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
波形选择模式
模式 | WGM01(TCCR0[6]) | WGM00(TCCR0[3]) | 工作模式 | 计数器上限 | OCR0(比较的值)更新 | TOV0(溢出中断标志)值 |
---|---|---|---|---|---|---|
0 | 0 | 0 | 普通模式 | 0xFF | 立即 | 1 |
1 | 0 | 1 | PWM,相位可调 | 0xFF | 立即 | 0 |
2 | 1 | 0 | CTC模式 | 0xOCR0 | 立即 | 1 |
3 | 1 | 1 | 快速PWM | 0xFF | 立即 | 1 |
说明
- 普通模式(用于计数):计数器++到0xFF,溢出后TOV0溢出中断标志位设为1,若触发中断TOV0将归零
- CTC模式(用于中断、信号调制):计数器++到OCR0的值,当计数器(TCNT0)==OCR0时,OCF0比较中断标志位设为1。
- 快速PWM模式(用于信号调制):计数器++到0xFF。
- 当计数器(TCNT0)==OCR0时, OC0(输出引脚)为(0|1),
- 当计数器值==0时为(1|0)。
- ——OC0变化取决于COM0n配置(2|3)
- 相位可调模PWM模式(用于信号调制):计数器++到0xFF,再–到0x00。
- 当计数器++(TCNT0)==OCR0时,OC0(输出引脚)为(0|1)
- 当计数器–(TCNT0)==OCR0时,OC0(输出引脚)为(1|0)
- ——OC0变化取决于COM0n配置(2|3)
计时/计数器用作调制信号时,信号如何变化的配置
序号 | COM01(TCCR0[5]) | COM00(TCCR0[4]) | 普通模式与非PWM模式(模式0、2) | 快速PWM模式(模式3) | 相位可调PWM模式(模式1) |
---|---|---|---|---|---|
0 | 0 | 0 | PB3位普通I/O,OC0不与引脚相连(不输出信号) | PB3位普通I/O,OC0不与引脚相连(不输出信号) | PB3位普通I/O,OC0不与引脚相连(不输出信号) |
1 | 0 | 1 | 比较匹配时,触发OC0取反 | 保留 | 保留 |
2 | 1 | 0 | 比较匹配时,OC0设为0 | 比较匹配时,OC0位0;计数值为0xFF,OC0为1 | 计数++比较匹配时,OC0为0;计数–比较匹配时,OC0为1 |
3 | 1 | 1 | 比较匹配时,OC0设为1 | 比较匹配时,OC0位1;计数值为0xFF,OC0为0 | 计数++比较匹配时,OC0为1;计数–比较匹配时,OC0为0 |
时钟源选择
序号 | CS02(TCCR0[2]) | CS01(TCCR0[1]) | CS00(TCCR0[0]) | 说明 |
---|---|---|---|---|
0 | 0 | 0 | 0 | 无时钟源,禁用定时器 |
1 | 0 | 0 | 1 | 内部时钟不分频 |
2 | 0 | 1 | 0 | 内部时钟/8 |
3 | 0 | 1 | 1 | 内部时钟/64 |
4 | 1 | 0 | 0 | 内部时钟/256 |
5 | 1 | 0 | 1 | 内部时钟/1024 |
6 | 1 | 1 | 0 | 外部引脚T0,下降沿驱动 |
7 | 1 | 1 | 1 | 外部引脚T0,上升沿驱动 |
2、定时器功能样例
(1)定时中断
每一毫秒执行一次中断函数
//初始化管脚
void init(){
//配置定时器
OCR0 = 125; //配置比较中断的值
bitSet1(TIMSK,OCIE0); //开比较中断
bitSet1(TCCR0,WGM01); //选择ctc模式
//选择时钟
bitSet0(TCCR0,CS00);
bitSet1(TCCR0,CS01);
bitSet0(TCCR0,CS02);
//配置全局中断开
SEI();
}
//中断函数
#pragma interrupt_handler timerHandler:20
void timerHandler(void) {
}
五、模拟比较器
1、模拟比较器相关定义概念与术语
(1)外接引脚
- 正极(AIN0)对应PB2引脚,正向端
- 负极(AIN1)对应PB3引脚,反向端
- 输出(ACO)可用于中断等
(2)功能
- AIN0电压 > AIN1电压,ACO置为1
2、模拟比较器功能寄存器
(1)SFIOR(配置型)特殊功能I/O寄存器
ACME位 SFIOR[3]
- 置1时,且ADC被关闭时,使用ADC的多路复用器选择ADC的模拟输入端作为模拟比较器反向端的输入信号源
- 清0时,将AIN1的引脚信号接入反向端
(2)ACSR(配置型、系统型)控制寄存器和状态寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | ACD | ACBG | ACO | ACI | ACIE | ACIC | ACIS1 | ACS0 |
读写 | R/w | R/w | R | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | N/A | 0 | 0 | 0 | 0 | 0 |
说明
- ACD(ACSR[7]):模拟比较器禁止(置1),即电源关闭。不使用模拟比较器应该关闭它(省电)。若要在运行时改变ACD的值,要先禁止模拟比较器中断(ACBG置为0)
- ACBG(ACSR[6]):模拟比较器参考电源选择
- 1:内部电源1.22V将代替AIN0作为正极输入
- 0:AIN0任然作为正极输入
- ACO(ACSR[5]):模拟比较器输出(存在1到2个系统时钟的时延)
- ACI(ACSR[4]):中断标记位,1代表开中断
- ACIE(ACSR[3]):中断开关
- ACIC(ACSR[2]):模拟比较器输入捕获允许
- 1:输出直接连到定时计数器1的输入捕获前端电路
- 0:不使用
- ACIS1、ACIS0(ACSR[1,0]):触发方式
ACIS1(ACSR[1]) | ACIS0(ACSR[0]) | 中断模式 |
---|---|---|
0 | 0 | ACO上升沿下降沿都触发 |
0 | 1 | 保留 |
1 | 0 | 下降沿触发 |
1 | 1 | 上升沿触发 |
模拟比较器的多路输入
ACME | ADEN | MUX[2:0] | 模拟比较器反向输入端 |
---|---|---|---|
0 | x | xxx | AIN1 |
1 | 1 | xxx | AIN1 |
1 | 0 | 000 | ADC0 |
1 | 0 | 001 | ADC1 |
1 | 0 | 010 | ADC2 |
1 | 0 | 011 | ADC3 |
1 | 0 | 100 | ADC4 |
1 | 0 | 101 | ADC5 |
1 | 0 | 110 | ADC6 |
1 | 0 | 111 | ADC7 |
六、数模转换ADC
1、数模转换相关介绍
(1)数模转换的个人理解
将模拟信号进行采样,转换为一个整数。
具体而言就是在给定一个采样时间t,对输入电压在这个采样时间内进行积分,求出的值除以最大电压下的积分,再乘以精度下的最大值
简而言之就是计算单位时间下模拟信号电压关于时间的函数的图形所围成的面积就是转换后的值。
原始信号和输出值之间的直接关联就是在采样时间下功率相等。
(2)AVR数模转化的特点
- 10位精度(输出值为:0~2^10-1)
- 0.5LSB积分非线性误差
- 正负2LBS绝对误差
- 13~260微秒转换时间
- 最高精度下可达15kSPS/s的采样率
- 8路可选单端输入通道
- 7路差分输入通道
- 2路差分输入通道带有可选的10和20倍增益
- ADC转换可设置为左端对齐
- ADC电压输入范围0~Vcc
- 内部可选2.56V参考电压
- 自由连续转换和单次转换模式
- ADC自动转换触发模式选择
- ADC转换完成触发中断
- 休眠模式下噪声抑制
(3)相关定义
- AVcc ADC的参考电源(防止数模干扰),与Vcc误差不超过正负0.3V
2、ADC功能寄存器
(1)ADMUX(配置型)多路复用选择
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | REFS1 | REFS0 | ADLAR | MUX4 | MUX3 | MUX2 | MUX1 | MUX0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
说明
ADMUX[7,6]参考电源选择
REFS1 | REFS0 | ADC参考电源 |
---|---|---|
0 | 0 | 外部引脚AREF,断开外部参考电源 |
0 | 1 | AVcc,AREF外部并接电源 |
1 | 0 | 保留 |
1 | 1 | 内部2.56,AREF外部并接电源 |
说明:若ADC转换中,电源被改变,将会在下次转换时改变
ADMUX[5]对齐方式
- ADLAR(ADMUX[5]):ADC结果左右对齐选择位
- 1:左对齐
- 0:右对齐
ADMUX[4:0]模拟通道和增益选择位
MUX[4:0] | 单端收入 | 差分正极输入 | 差分父级输入 | 增益 |
---|---|---|---|---|
00000 | ADC0 | |||
00001 | ADC1 | |||
00010 | ADC2 | |||
00011 | ADC3 | |||
00100 | ADC4 | |||
00101 | ADC5 | |||
00110 | ADC6 | |||
00111 | ADC7 | |||
01000 | ADC0 | ADC0 | 10 | |
01001 | ADC1 | ADC0 | 10 | |
01010 | ADC0 | ADC0 | 200 | |
01011 | ADC1 | ADC0 | 200 | |
01100 | ADC2 | ADC2 | 10 | |
01101 | ADC3 | ADC2 | 10 | |
01110 | ADC2 | ADC2 | 200 | |
01111 | ADC3 | ADC2 | 200 | |
10000 | ADC0 | ADC1 | 1 | |
10001 | ADC1 | ADC1 | 1 | |
10010 | ADC2 | ADC1 | 1 | |
10011 | ADC3 | ADC1 | 1 | |
10100 | ADC4 | ADC1 | 1 | |
10101 | ADC5 | ADC1 | 1 | |
10110 | ADC6 | ADC1 | 1 | |
10111 | ADC7 | ADC1 | 1 | |
11000 | ADC0 | ADC2 | 1 | |
11001 | ADC1 | ADC2 | 1 | |
11010 | ADC2 | ADC2 | 1 | |
11011 | ADC3 | ADC2 | 1 | |
11100 | ADC4 | ADC2 | 1 | |
11101 | ADC5 | ADC2 | 1 | |
11110 | 1.22V(VBG) | |||
11111 | 0V(GND) | N/A |
(2)ADCSRA(配置型)ADC控制和状态寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | ADEN | ADSC | ADATE | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- ADEN(ADCSRA[7]):ADC开关(正在转换中也会中止)
- 1:开启
- 0:关闭
- ADSC(ADCSRA[6]):ADC开始转换,在自由模式下,第一次转化需要25个ADC时钟周期,在转换过程中ADSC将始终为1,强制置0无效(不会中止)
- 1:启动一次转换
- 0:转换结束
- ADATE(ADCSRA[5]):自动转换允许位,
- 1:ADC工作在自动转换下,在触发信号上升沿,ADC自动进行一次ADC转换。信号源由SFIOR[ADTS]选择确定
- 0:禁用自动转换
- ADIF(ADCSRA[4]):ADC中断标志位,完成一次ADC转换将触发(可以置1清零)
- ADIE(ADCSRA[3]):中断开关
- 1:开
- 0:关
- ADPSx(ADCSRA[2:0]):ADC内部时钟分频选择
分频选择
ADPS[2:0] | 分频系数 |
---|---|
000 | 2 |
001 | 2 |
010 | 4 |
011 | 8 |
100 | 16 |
101 | 32 |
110 | 64 |
111 | 128 |
(3)ADCL、ADCH(系统型)ADC结果数据寄存器
读写权限为只读 默认值为0 ADLAR=0 右对齐 ADCH[1,0] 和 ADCL[7:0]
ADLAR=1 左对齐 ADCH[7,0] 和 ADCL[1:0]
(4)SFIOR(配置型)特殊功能I/O寄存器
ADTS[2:0](SFIOR[7:5])ADC自动转换触发源选择位 读写权限:R/w 默认值:0
ADTS[2:0] | 触发源 |
---|---|
000 | 连续自动转换 |
001 | 模拟比较器 |
010 | 外部中断0 |
011 | T/C0比较匹配 |
100 | T/C0溢出 |
101 | T/C1比较比配B |
110 | T/C1溢出 |
111 | T/C1输入捕获 |
七、数据通讯之串行TWI(I2C)
1、I2C总线简介
- 一种总线通讯标准协议
- 共有两根数据线(SDA:串行数据线,SCL:串行时钟线)
- 两根线必须通过上拉电阻接到正电源
- 接入总线的设备的引脚必须是开漏或者集电集开路输出(具有线与功能、也就是说只要有一个拉低,整个线路为低电平)
- 线路必须是双向通讯的
- 一次通讯只能一方是接收方,另一方是发送方(一次通讯:起始信号 -> 数据传输 -> 结束信号)
- 一次通讯只能由一方控制时钟(叫做主机)
2、I2C通讯流程
(1)总体流程
起始信号 -> 数据传输(寻址->发送数据) -> 结束信号
(2)起始信号
SCL = 1 SDA = 1->0
(3)结束信号
SCL = 1 SDA = 0->1
(4)数据传输
SCL = 1 SDA = 0或者1 不能改变
- 以字节为单位,每字节8位,高位在前,低位在后,字节数不受限制
- 发送方发送完每个字节后,要等待接收方的应答信号ACK(接收方发送SCL=1, SDA=0)
- 发送一个字节需要9个SCL时钟
- 发送方控制SCL(时钟)
- 前8个时钟发送方控制SDA,发送数据
- 第9个时钟发送方释放SDA、交由接收方控制
- 接收方发送ACK信号
- 或接收方发送nACK(SCL=1, SDA=1)信号(无响应)
- 发送方检测到nACK,等于通信结束等待下一次通讯
(5)寻址
主机发送地址数据后,从机必须要在第9个时钟发送ACK信号,通知主机寻址成功,寻址步骤结束。开始传输数据
地址的最低位有特殊含义:
- 0 -> 从机是接收方
- 1 -> 从机是发送方
(6)通讯全过程
- 主机控制时钟SCL
- 主机控制SDA发送起始信号
- 主机开始寻址
- 主机发送8位地址数据后,释放SDA
- 对应地址的从机控制SDA发送ACK应答
- 从机根据地址信号最高位的值做好发送或接收数据的准备
- 主从机开始传输业务数据(根据地址码的最高位切换发送接收方)
3、AVR的TWI(I2C)接口使用与功能寄存器
(1)TWBR(配置型)波特率寄存器
主控模式下配置
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | TWBR7 | TWBR6 | TWBR5 | TWBR4 | TWBR3 | TWBR2 | TWBR1 | TWBR0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
波特率公式
SCL频率= cpu频率 / (16 + 2(TWBR) * 4^TWPS )
主控模式下TWBR
必须大于10
(2)TWCR(配置型)控制寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | TWINT | TWEA | TWSTA | TWSTO | TWWC | TWEN | - | TWIE |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- TWINT(TWCR[7]):中断标志位,当设备完成当前工作,并期待下步操作,(前提条件:中断打开)置1:
- 一旦置1,SCL将拉低(此时应准备下步操作)
- 该位不会自动清零,必须手动写1清零
- 一旦清零,所有的配置讲反映到总线上(执行操作)
- TWEA(TWCR[6]):ACK应答允许位,手动清零表示脱离总线。此位置1时:
- 本机作为被控时,接收到呼叫自己的地址
- TWAR[TWGCE]为1,接受到一个通用呼叫地址
- 接受到一个字节的数据
- TWSTA(TWCR[5]):置1:本机作为主控方发送起始信号(start信号)
- 检测总线是否被占用
- 没被占用发送起始信号
- 被占用等待空闲发送起始信号
- 控制SCL时钟从而获得主线控制权
- 发送启动信号后,自动清零
- 检测总线是否被占用
- TWSTO(TWCR[4]):置1:
- 本机作为主控机:发送停止信号
- 本机作为被控机:从错误状态恢复,本机恢复到未被寻址的状态
- TWWC(TWCR[3]):写冲突标志位。
- 当TWINT(TWCR[7])为0时:试图向TWDR写数据,该位将会被置位
- 当TWINT(TWCR[7])为1时:向TWDR写数据,该位将会被清零
- TWEN(TWCR[2]):TWI(I2C)允许位(开关)。PC0作为SCL、PC1作为SDA
- TWCR[1]:保留
- TWIE(TWCR[0]):TWI中断开关
(3)TWSR(系统型)控制寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | TWS7 | TWS6 | TWS5 | TWS4 | TWS3 | - | TWPS1 | TWPS0 |
读写 | R | R | R | R | R | R | R | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- TWSn{7:3}(TWSR[7:3]):TWI状态位。反应了TWI的逻辑状态和总线状态,具体见课本P459
- TWSR[2]:保留
- TWPSn{1:0}(TWSR[1:0]):分频位
TWPS1 | TWPS0 | 预分频值 |
---|---|---|
0 | 0 | 1 |
0 | 1 | 4 |
1 | 0 | 16 |
1 | 1 | 64 |
(3)TWDR(系统型)数据寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | TWD7 | TWD6 | TWD5 | TWD4 | TWD3 | TWD2 | TWD1 | TWD0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- TWDn{7:0}(TWDR[7:0]):
- 发送模式下:设置为下一个要发送的字节
- 接收模式下:当前时间下最后接收的字节
- TWI发生中断后才可以被写入(TWCR[TWINT]=1)
(4)TWAR(配置型)被控端地址寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | TWA6 | TWA5 | TWA4 | TWA3 | TWA2 | TWA1 | TWA0 | TWGCE |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- TWAn{6:0}(TWAR[7:1]):本机作为被控端时,本机的地址
- TWGCE(TWAR[0]):是否接收主控方的广播
4、I2C设备——24C256
(1)功能特点
- 容量为32k的储存器
- 采用EEPROM,数据断电不丢失
- 地址长度为15位,地址空间为0000H~7FFFH,要用两个字节表示
- 把内存分为512页,每页64字节
- 地址高9位(A14~A6)表示页(0~511),第六位表示页内偏移(0~63)
- 内部有15位的地址寄存器
- 对当前地址操作后,地址指针会自动+1
- 读操作,一直++,直到最后一个地址(7FFFH),跳转到首地址(0000H)
- 写操作,在当前页内循环
(2)引脚定义
名称 | 功能 |
---|---|
A0、A1、A2 | 地址配置 |
SDA | I2C数据线 |
SCL | I2C时钟线 |
WP | 写保护,高电平有效 |
Vdd | 电源 |
Vss | 地 |
(3)操作流程
写操作
主机发出start -> 主机下发写操作 -> 设备ACK -> 主机发送要写的地址的高位(最高位无效,随意) -> 设备ACK -> 主机发送要写的地址的低位 ->设备ACK -> 数据传输。。。。。。
读操作 主机发出start -> 主机下发写操作 -> 设备ACK -> 主机发送要写的地址的高位(最高位无效,随意) -> 设备ACK -> 主机发送要写的地址的低位 ->设备ACK -> 设备发送start -> 主机下发读操作 -> 设备ACK -> 设备发1个字节的数据 -> 主机ACK 设备发1个字节的数据 -> 主机ACK 设备发1个字节的数据 ->主机nACK,结束读数据
八、数据通讯之串口USART
1、串口传输特点
(1)数据帧特点
- IDLE 状态:线路空闲,线路为1
- St 位:起始位,线路为0
- 数据位: 最少5位,最多9位
- 校验位:用于校验数据(奇偶校验)
- Spn 停止位:线路为1
在一般应用中采用1起始位8数据位1停止位
(2)异步通讯
特点
- 收发方根据自己的时钟发送接收数据,不需要传输时钟数据
- 收发方在进行通讯时必须严格按照约定好的波特率进行通讯,否侧会发生错误
波特率
又称比特率单位b/s
异步通讯的标准一般采用1200的倍数作为协议
2、avr的USART的特点
- 全双工(相互独立的收发寄存器)
- 支持同步或异步传输
- 同步操作可采取主机时钟同步,也可以采用从机时钟同步
- 独立波特率发生器,不占用定时器
- ……
3、USART寄存器
(1)UDR数据寄存器
占用2个物理寄存器,用于接收(RXB)或发送(TXB)数据
(2)UCSRA(配置型)控制状态寄存器A
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | RXC | TXC | UDRE | FE | DOR | PE | U2X | MPCM |
读写 | R | R/w | R | R | R | R | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- RXC(UCSRA[7]):数据接收完成,且未读取,该位为1,同时触发中断,用户读取UDR时自动清0
- TXC(UCSRA[6]):数据发送完成,且UDR(TXB)没有待发送数据,值1,同时触发中断,执行中断程序时,自动清0,可以置1清0。
- UDRE(UCSRA[5]):TXB寄存器为空时(或者系统复位),置1(表示可以发送数据了)。
- FE(UCSRA[4]):接收帧出错,接收器缓冲器数据出错,置1;清0的情况:
- FE接收到数据帧的停止位
- 读取UDR
- 重写UCSRA寄存器
- DOR(UCSRA[3]):接收数据溢出错误,当UDR(RXB)和接收缓冲器慢,又来了一个起始位,置1;清0:
- 当UDR(RXB)被读取
- 重写UCSRA寄存器
- PE(UCSRA[2]):校验错误,置1;清0:
- 当UDR(RXB)被读取
- 重写UCSRA寄存器
- U2X(UCSRA[1]):配置传输倍率,异步有效,同步模式应清0
- 1:波特率分频器分频比为8,传输速率加倍
- 0:波特率分频器分频比为16
- MPCM(UCSRA[0]):多级通讯模块允许,发送不受影响
- 1:若接收数据不包含地址,将忽略
(3)UCSRB(配置型)控制状态寄存器B
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | RXCIE | TXCIE | UDRIE | RXEN | TXEN | UCSZ2 | RXB8 | TXB8 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- RXCIE(UCSRB[7]):接收完成中断允许
- TXCIE(UCSRB[6]):发送完成中断允许
- UDRIE(UCSRB[5]):数据寄存器空中断允许
- RXEN(UCSRB[4]):数据接收允许
- TXEN(UCSRB[3]):数据发送允许
- UCSZ2(UCSRB[2]):设置数据帧中的数据字的长度(配合UCSZ[1:0])
- RXB8(UCSRB[1]):接收数据第8位
- TXB8(UCSRB[0]):发送数据第8位
(4)UCSRC(配置型)控制状态寄存器C
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | URSEL | UMSEL | UPM1 | UPM0 | USBS | UCSZ1 | UCSZ0 | UCPOL |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- URSEL(UCSRC[7]):寄存器选择
- 1:UCSRC可读写
- TXCIE(UCSRC[6]):工作模式
- 0:异步
- 1:同步
- UPM[1:0](UCSRC[5:4]):校验方式
- 0 0 :无校验
- 0 1 :保留
- 1 0 :偶校验
- 1 1 :奇校验
- USBS(UCSRC[3]):停止位个数
- 0:1个停止位
- 1:2个停止位
- UCSZ[1:0](UCSRC[2:1]):设置数据帧中的数据字的长度(UCSRB[2])
UCSZ2(UCSRB[2]) | UCSZ1 | UCSZ0 | 数据字长度 |
---|---|---|---|
0 | 0 | 0 | 5 |
0 | 0 | 1 | 6 |
0 | 1 | 0 | 7 |
0 | 1 | 1 | 8 |
1 | 0 | 0 | 9 |
1 | 0 | 1 | 保留 |
1 | 1 | 0 | 保留 |
1 | 1 | 1 | 9 |
- UCPOL(UCSRC[0]):时钟极性选择(同步模式配置)
- 0:TXD输入:XCK上升沿,RXD输入:XCK下升沿
- 1:TXD输入:XCK下升沿,RXD输入:XCK上升沿
(5)UBRRL和UBRRH(配置型)模特率寄存器
UBRRH(和UCSRC共用地址)
位 | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | URSEL | - | - | - | UBRR[11] | UBRR[10] | UBRR[9] | UBRR[8] |
读写 | R/w | R | R | R | R/w | R/w | R | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
UBRRL
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | UBRR[7] | UBRR[6] | UBRR[5] | UBRR[4] | UBRR[3] | UBRR[2] | UBRR[1] | UBRR[0] |
读写 | R/w | R | R | R | R/w | R/w | R | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- URSEL(UBRR[15]):寄存器选择
- 0:UBRRH可读写
- UBRR[14:12]:保留
- UBRR[11:0]:模特率设置
(6)关于UBRRH和UCSRC寄存器
UBRRH和UCSRC寄存器共用一份地址,对URSEL位设置来设置读写哪个寄存器
- 写操作
- 0:写入UBRRH寄存器
- 1:写入UCSRC寄存器
- 读操作
- 读UBRRH:第一次读取
- 读UCSRC:在连续的两个系统时钟内读取两次,第二次为UCSRC的值
九、数据通讯之串行SPI
1、SPI简介
(1)SPI总线构成
- 有3~4根线
- 主机输出/从机输入线(MOSI)
- 主机输入/从机输出线(MISO)
- SCLK时钟
- SS从机选择片选信号(可在MUC控制)
(2)SPI原理
- 完全由主机控制
- 主机和从机构成一个移位寄存器环
- 当主机将1位数据移位到从机中,从机的寄存器也会将1位数据移位到主机中
(3)AVR SPI接口特点
- 全双工同步传输
- 可选主从模式
- 可选低位在前(LSB)和高位在前(MSB)
- 7种传输速率
- 数据传输中断标志
- 写冲突标志位保护
- 从闲置被唤醒(从机模式)
- 倍速SPI传送(主机模式)
(4)引脚定义
- MOSI(PB5)
- MISO(PB6)
- SCK(PB7)
- SS(PB4)
2、SPI功能寄存器
(1)SPCR(配置型)控制寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | SPIE | SPE | DORD | MSTR | CPOL | CPHA | SPR1 | SPR0 |
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- SPIE(SPCR[7]):SPI中断允许
- SPE(SPCR[6]):SPI允许
- DORD(SPCR[5]):数据移除顺序
- 1:LSB
- 0:MSB
- MSTR(SPCR[4]):主从机选择
- 1:本机主机
- 0:本机从机
- CPOL(SPCR[3]):时钟极性
- 1:闲置高电平
- 0:闲置低电平
- CPHA(SPCR[2]):时钟相位选择
- SPR[1:0](SPCR[1:0]):时钟频率选择(速率选择)、配合SPSR[SPI2x]使用
|SPSR[SPI2x]|SPR1|SPR0|SCK频率| |:–:|:–:|:–:|:–:|:–:| |0|0|0|f/4| |0|0|1|f/16| |0|1|0|f/64| |0|1|1|f/128| |1|0|0|f/2| |1|0|1|f/8| |1|1|0|f/32| |1|1|1|f/64|
(2)SPSR状态寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | SPIF | WCOL | - | - | - | - | - | SPI2X |
读写 | R | R | R | R | R | R | R | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
- SPIF(SPSR[7]):中断标志,串行数据传输完成置1
- WCOL(SPSR[6]):写冲突标志,向SPDR写数据,该位清0
- SPSR[5:1]:保留
- SPI2X(SPSR[6]):倍率选择配合SPCR[1:0]使用
(3)SPDR数据寄存器
位 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|
位置宏命名 | MSB | LSB | ||||||
读写 | R/w | R/w | R/w | R/w | R/w | R/w | R/w | R/w |
初始化 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
数据读写寄存器,写入此寄存器数据后,SPI将准备启动传输数据。 读该寄存器后将读取到接收到的数据
3、驱动诺基亚3310LCD
略