keil c51默认并没有携带STC芯片的固件库,添加固件库的步骤如下:
1、在下载软件右侧tab界面选择“keil仿真设置”标签,并点击下方红框框出的按钮(“添加型号和头文件…到Keil中”按钮)进行添加。如图1所示。
2、选择添加固件库的路径,如图2所示。务必注意要选择Keil软件的安装根目录,选择好之后点击“确定”。
当我们再打开keil软件新建工程时,就可以看到我们所需要的芯片型号了。如图3所示。
#include “reg52.h”
sfr P4 = 0xc0; // reg52.h 中没有定义 P4 寄存器故自己定义
sbit P3_6 = P4^2; // 位定义用 P3_6 在程序中替换 P4^2的功能
sbit P3_7 = P4^4; // 同上
#include “stc15f2k60s2.h” // 该文件已定义 P4 寄存器故无需重复定义
sbit P3_6 = P4^2; // 位定义用 P3_6 在程序中替换 P4^2 的功能
sbit P3_7 = P4^4; // 同上
// 通过译码器选择对应的锁存器
void select(uchar num)
{
switch (num)
{
case 4: P2 = (P2 & 0x1f) | 0x80; break;
case 5: P2 = (P2 & 0x1f) | 0xa0; break;
case 6: P2 = (P2 & 0x1f) | 0xc0; break;
case 7: P2 = (P2 & 0x1f) | 0xe0; break;
default: P2 &= 0x1f; break;
}
}
void init()
{
// 关闭蜂鸣器、LED灯、数码管
select(5); // 关闭蜂鸣器
P0 = 0x00;
select(4); // 关闭LED灯
P0 = 0xff;
select(6); // 选中全部数码管
P0 = 0xff;
select(7); // 关闭数码管
P0 = 0xff;
select(0); // 锁存
}
// Delayms
void delayms(uint time)
{
uint i, j;
for (i = time; i > 0; i--)
for (j = 845; j > 0; j--);
}
uchar numChar[] = {0XC0, 0XF9, 0XA4, 0XB0, 0X99, 0X92, 0X82, 0XF8, 0X80, 0X90};
// 数码管
void shumaguan(uchar wei, uchar num)
{
select(7); // 关闭数码管
P0 = 0xff;
select(0);
P0 = 0x00;
select(6); // 位选
P0 = 0x80 >> wei; // [0x80]从左至右 [0x01] 从右至左
select(0);
P0 = 0xff;
select(7);
P0 = numChar[num];
select(0);
Delay3ms(); // 延时 3ms
}
void Timer0Init(void) // 10 毫秒 @11.0592MHz
{
AUXR &= 0x7F; // 定时器时钟 12T 模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0x00; // 设置定时初值
TH0 = 0xDC; // 设置定时初值
TF0 = 0; // 清除 TF0 标志
TR0 = 1; // 定时器 0 开始计时
EA = 1;
ET0 = 1;
}
void Timer1Init(void) // 10 毫秒 @11.0592MHz
{
AUXR &= 0xBF; // 定时器时钟 12T 模式
TMOD &= 0x0F; // 设置定时器模式
TL1 = 0x00; // 设置定时初值
TH1 = 0xDC; // 设置定时初值
TF1 = 0; // 清除 TF1 标志
TR1 = 1; // 定时器 1 开始计时
EA = 1;
ET1 = 1;
}
void Timer2Init(void) // 10 毫秒 @11.0592MHz
{
AUXR &= 0xFB; // 定时器时钟 12T 模式
T2L = 0x00; // 设置定时初值
T2H = 0xDC; // 设置定时初值
AUXR |= 0x10; // 定时器 2 开始计时
EA = 1;
IE2 |= 0x04;
}
void initInter0()
{
IT0 = 1; // 中断触发方式:[0]低电平触发 [1]下沿触发
EX0 = 1; // 打开外部中断
EA = 1; // 开启总中断
}
void initInter1()
{
IT1 = 1; // 中断触发方式:[0]低电平触发 [1]下沿触发
EX1 = 1; // 打开外部中断
EA = 1; // 开启总中断
}
注:
1 | ~ | 4 |
~ | … | ~ |
11 | ~ | 16 |
// 矩阵键盘
// IAP15 芯片的 WR/RD 功能不是 P36/P37 引脚功能,故用 P42/P44 引脚代替
// 即:P3^6 => P42 P3^7 => P44
uchar keyScan()
{
uchar keyValue = 0;
// 键盘初始化
P3 = 0x0f; P42 = 0; P44 = 0;
// 键盘扫描
if (P3 != 0x0f)
{
delayms(10); // 消抖
if (P3 != 0x0f)
{
switch (P3)
{
case 0x07: keyValue = 13;break;
case 0x0b: keyValue = 9; break;
case 0x0d: keyValue = 5; break;
case 0x0e: keyValue = 1; break;
default: return 0;
}
// 键盘翻转
P3 = 0xf0; P42 = 1; P44 = 1;
// 扫描
if (!P34) keyValue += 3;
else if (!P35) keyValue += 2;
else if (!P42) keyValue += 1;
else if (!P44) keyValue += 0;
else return 0;
// 等待按键抬起
while (P3 != 0xf0);
while (!P42);
while (!P44);
}
}
return keyValue; // 返回按键值,如无按键按下则返回 0
}
注:
// EEPROM 设备地址 0xA0
void writeByte(uchar addr, uchar byt)
{
IIC_Start();
IIC_SendByte(devAddr);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_SendByte(byt);
IIC_WaitAck();
IIC_Stop();
}
uchar readByte(uchar addr)
{
uchar dat;
IIC_Start();
IIC_SendByte(devAddr);
IIC_WaitAck();
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(devAddr | 0x01);
IIC_WaitAck();
dat = IIC_RecByte();
IIC_Ack(0);
IIC_Stop();
return dat;
}
// AT24C02 共 256 个字节 分为 8 页 共 32 页
// 页写入
void write(uchar addr, uchar len, uchar *arr)
{
while (len > 0)
{
while (1)
{
IIC_Start();
IIC_SendByte(devAddr);
if (IIC_WaitAck()) break;
}
IIC_SendByte(addr);
IIC_WaitAck();
while (len > 0)
{
IIC_SendByte(*arr++);
IIC_WaitAck();
len--;
addr++;
// 判断是否达到页边界
if ((addr & 0x07) == 0) break;
}
IIC_Stop();
}
}
void read(uchar addr, uchar len, uchar *arr)
{
while (1)
{
IIC_Start();
IIC_SendByte(devAddr);
if (IIC_WaitAck()) break;
}
IIC_SendByte(addr);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(devAddr | 0x01);
IIC_WaitAck();
while (--len)
{
*arr++ = IIC_RecByte();
IIC_Ack(1);
}
*arr = IIC_RecByte();
IIC_Ack(0);
IIC_Stop();
}
注:
// [chl]选择通道
uchar ADC(uchar chl)
{
uchar value;
while (1) // 等待设备应答
{
IIC_Start();
IIC_SendByte(0x90);
if (IIC_WaitAck()) break;
}
IIC_SendByte(chl);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
value = IIC_RecByte();
IIC_Ack(0);
IIC_Stop();
return value; // 需转换为电压值
}
void DAC(uchar value) // 输入值需要转换
{
while (1)
{
IIC_Start();
IIC_SendByte(devAddr);
if (IIC_WaitAck()) break;
}
IIC_SendByte(0x40);
IIC_WaitAck();
IIC_SendByte(value);
IIC_WaitAck();
IIC_Stop();
}
注:
// DS18B20
// XXXX YYYY YYYY ZZZZ
// [X]正负(0-正 1-负) [Y]温度整数值 [Z]温度小数值(Z * 0.0625)
uchar Temper_Read()
{
uchar temp, Tl, Th;
Init_DS18B20(); // DS18B20 初始化
Write_DS18B20(0xcc); // 跳过 ROM 的字节命令
Write_DS18B20(0x44); // 开始转换指令
Delay_OneWire(200); // 延时一段时间
Init_DS18B20(); // DS18B20 初始化
Write_DS18B20(0xcc); // 跳过 ROM 的字节命令
Write_DS18B20(0xbe); // 读取指令
Tl=Read_DS18B20(); // 读低八位
Th=Read_DS18B20(); // 读高八位
temp = (Th << 4) | (Tl >> 4); // 只取整数
return temp;
}
// DS1302
writeDS1302(0x8E, 0); // 关闭写保护
// 突发模式:必须一次性读写全部 8 个字节
// [0xBF]读 [0xBE]写
writeByte(0xBF); // 突发读
writeByte(0xBE); // 突发写
注:波特率为 9600bps
// UART
void UartInit(void) //[email protected]
{
SCON = 0x50; // 8位数据,可变波特率
AUXR |= 0x40; // 定时器1时钟为Fosc,即1T
AUXR &= 0xFE; // 串口1选择定时器1为波特率发生器
TMOD &= 0x0F; // 设定定时器1为16位自动重装方式
TL1 = 0xE0; // 设定定时初值
TH1 = 0xFE; // 设定定时初值
ET1 = 0; // 禁止定时器1中断
TR1 = 1; // 启动定时器1
ES = 1; // 允许串口中断
EA = 1; // 开启总中断
}
void UARTInter() interrupt 4
{
if (TI == 1)
{
TI = 0; // 手动清除中断标志
}
if (RI == 1)
{
RI = 0; // 手动清除中断标志
SBUF = SBUF + 1;
}
}
// 超声波测距
#define nop() {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}
uint echo()
{
uchar i;
uint distance;
initTimer();
// 起始信号
out = 0;
for (i = 0; i < 16; i++)
{
out = ~out;
nop();nop();nop();nop();nop();
nop();nop();nop();nop();nop();
}
// 接收信号
while (!in);
TR0 = 1;
while ((in == 1) && (TF0 == 0)); // 信号结束或计时器计满
TR0 = 0;
// 计算
if (TF0) // 计时器计满,超出测量范围
{
distance = 0;
TF0 = 0;
}
else
{
distance = (uint) (TH0 << 8) | TL0;
distance *= 0.017 * 1.085; // 单位 cm (1.085 为误差补偿)
}
return distance;
}
注:
OSC 框表示时钟频率,因为1个机器周期等于12个时钟周期,所以那个 d 就等于12。下边 GATE
右边的那个门是一个非门电路,再右侧是一个或门,再往右是一个与门电路。图上可以看出来,下边部分电路是控制了上边部分,那我们先来看下边是如何控制的,我们以定时器0为例。
TR0 和下边或门电路的结果要进行与运算,TR0 如果是0的话,与运算完了肯定是0,所以如果要让定时器工作,那么 TR0 就必须置1。
这里的与门结果要想得到1,那么前面的或门出来的结果必须也得是1才行。在 GATE
位为1的情况下,经过一个非门变成0,或门电路结果要想是1的话,那 INT0 即 P3.2 引脚必须是1的情况下,这个时候定时器才会工作,而
INT0 引脚是0的情况下,定时器不工作,这就是 GATE 位的作用。当 GATE 位为0的时候,经过一个非门会变成1,那么不管 INT0 引脚是什么电平,经过或门电路后都肯定是1,定时器就会工作。
要想让定时器工作,就是自动加1,从图上看有两种方式,第一种方式是那个开关打到上边的箭头,就是 C/T =0 的时候,一个机器周期 TL
就会加1一次,当开关打到下边的箭头,即 C/T =1 的时候,T0 引脚即 P3.4 引脚来一个脉冲,TL 就加1一次,这也就是计数器功能。
极客学院_5.3 单片机定时器的寄存器:http://wiki.jikexueyuan.com/project/mcu-tutorial-one/timer-register.html
// 频率测量 NE555
uint fre;
uchar count = 0;
void initNE555Fre()
{
TMOD = 0x15; // T1:定时器 T0:计数器
TH0 = TL0 = 0; // 计数清零
TL1 = 0x00; // 设置定时初值
TH1 = 0x4C; // 设置定时初值
TF0 = TF1 = 0;
TR0 = TR1 = 1;
ET1 = 1;
EA = 1;
}
void timer() interrupt 3
{
TL1 = 0x00; // 设置重装
TH1 = 0x4C; // 设置重装
if (count++ == 20)
{
count = 0;
fre = (uint) (TH0 << 8) | TL0;
TL0 = TH0 = 0;
}
}
sbit PWM = P1^0; // PWM 输出口
uchar HTH, HTL; // 高电平持续时间
uchar LTH, LTL; // 低电平持续时间
// 高电平计数, 低电平计数, 定时计数
uchar HC, LC, TC;
bit PWMF = 1; // PWM 标志
void changePWM(uint fre, uchar dc)
{
float HTime, LTime;
float time = 1090000.0 / fre; // 周期 = 10^6毫秒 / 频率(fre)
LTime = (time * dc / 100) * 11059200 / 12 / 1000000;
HTime = (time - LTime) * 11059200 / 12 / 1000000;
if (dc <= 0 || dc >= 100 || fre <= 0)
{
TC = 255;
if (dc <= 0) PWM = 0;
else PWM = 1;
return;
}
// 计数计算
if (65536 < HTime) HC = HTime / 65536;
else HC = 0;
TH0 = HTH = (65536 - HTime) / 256;
TL0 = HTL = (uint) (65536 - HTime) % 256;
if (65536 < LTime) LC = LTime / 65536;
else LC = 0;
LTH = (65536 - LTime) / 256;
LTL = (uint) (65536 - LTime) % 256;
// 初始化
TH0 = TL0 = 0;
TC = LC;
if (HC != 0)
TC = HC;
}
void startPWM(uint fre, uchar dc)
{
// PWM 初始化
PWM = 1;
TC = HC = LC = 0;
// 定时器初始化
TMOD &= 0xf0;
TMOD |= 0x01;
changePWM(fre, dc);
EA = 1;
TF0 = 0;
ET0 = 1;
TR0 = 1;
}
void stopPWM()
{
TR0 = 0;
PWM = 1;
}
void PWMTimer() interrupt 1
{
// 当占空比为 0 或 100 时保持电瓶不变
if (TC == 255) return;
if (!TC--)
{
if (PWM)
{
TH0 = HTH;
TL0 = HTL;
TC = HC;
}
else
{
TH0 = LTH;
TL0 = LTL;
TC = LC;
}
PWM = ~PWM;
}
}
单片机中文网:http://c.biancheng.net/cpp/danpianji/
单片机入门基础教程: https://wiki.jikexueyuan.com/list/microcontrollers/
Bkoak 博客:http://www.bkoak.com/
EEPW 论坛:http://forum.eepw.com.cn/thread/302835/1