[success]最近好不容易有空,所以就想起了之前和学长的一个项目,刚好最近有时间,所以就打算在这里记下一些笔记,方便自己查阅[/success]
STC15F2K60S2芯片
这个是我们项目使用的芯片,我之前一直用的是51的芯片第一次用15的也很好奇。不过简单的看了一下芯片手册,感觉还是很简单的。芯片手册下载:[ypbtn]https://pan.xiaoyou66.com/s/mvjk61pd[/ypbtn]
芯片引脚图
芯片的特点
1.不需要外接晶振,内部集成了晶振,只需要接上电源就可以使用了,比51方便很多2.有8通道10位高速ADC,不需要自己接什么ADC芯片,很实用
3.还有其他的地方我也不多BB,直接上手实战
新建项目
因为自己把keil4给卸载了,所以这里以keil5来演示如何新建一个项目。默认keil中是不能写STC系列的程序的,所以我们这里需要让keil5支持15.1.先到官网下载依赖(要梯子才能下载)
下载好后直接安装一下就可以了,注意目录要选择keil5的安装目录,注意一般目录名是MDK(看不懂看一下下面的选芯片型号那里)。
后面就是运行破解机(自己复制CID号,具体在file->License Management)
注意芯片型号和软件版本
然后把获得的注册码也添加进去(之前keil5破解一次,这里51也破解了一次)
2.我们打开STC软件,把芯片库给加进去
编写程序
东西搞的差不多了,现在来写一个非常简单的流水灯实验。[highlight lanaguage=”C”]
#include "STC15F2K60S2.h" #include typedef unsigned int u16; typedef unsigned char u8; #define led P0 void delay(u16 i) { while(i--); } void main() { u8 i; led=0x01; delay(50000); //´óÔ¼ÑÓʱ450ms while(1) { for(i=0;i<7;i++) { led=_crol_(led,1); delay(50000); } for(i=0;i<7;i++) { led=_cror_(led,1); delay(50000); } } }
[/highlight]
我们自己新建项目(这里注意选择库还有芯片型号)
注意:自己写程序的时候要把文件也加进去要不然就会像我一样倒腾了半天没有任何效果。
(这里一定要记得把c文件给加进去。。。)
AD转换器
理论知识这里不想讲了,我直接说怎么用把。单片机的P1口为ADC的转换口。在使用的时候我们需要配置好寄存器就好了。我们需要先配置好模拟功能控制寄存器(P1ASF)
ADRES是A/D转换结果寄存器,里面存放着高位和低位的结果。
ADC_CONTR是A/D的控制寄存器,里面的内容如下(具体内容我不详解)。下面的代码里面其实就是做了一件事,就是打开ADC的电源,然后在使用了540个时钟进行ADC转换。
[highlight lanaguage=”C”]
P1ASF = 0xff; //设置P1口为AD口 ADC_RES = 0; //清除结果寄存器 ADC_CONTR = ADC_POWER | ADC_SPEEDLL; Delay(2);
[/highlight]
显示读取ADC结果的函数。ch是通道号(ADC总共有7个通道),下面这个代码大概是这样运行的。
[block]
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
[/block]
这个代码主要用于打开ADC转换电源,同时使用540个时钟周期来进行转换,因为这个寄存器后3位是用来控制通道号的,所以可以直接与上通道号。最后那个就是开始ADC转换。
转换过程中需要等待4个时钟周期,最后我们可以通过获取ADC控制寄存器的flag来判断转换是否完成,如果完成,那么我们就关闭ADC,最后把结果寄存器的内容返回。
[highlight lanaguage=”C”]
BYTE GetADCResult(BYTE ch) { ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START; _nop_(); //等待4个NOP _nop_(); _nop_(); _nop_(); while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成 ADC_CONTR &= ~ADC_FLAG; //Close ADC return ADC_RES; //返回ADC结果 }
[/highlight]
最后直接给上例程
[highlight lanaguage=”C”]
#include "STC15F2K60S2.H" #include "intrins.h" typedef unsigned char BYTE; typedef unsigned int WORD; #define FOSC 11059200L //系统频率 #define BAUD 115200 //串口波特率 #define NONE_PARITY 0 //无校验 #define ODD_PARITY 1 //奇校验 #define EVEN_PARITY 2 //偶校验 #define MARK_PARITY 3 //标记校验 #define SPACE_PARITY 4 //空白校验 #define ADC_POWER 0x80 //ADC电源控制位 #define ADC_FLAG 0x10 //ADC完成标志 #define ADC_START 0x08 //ADC起始控制位 #define ADC_SPEEDLL 0x00 //540个时钟 #define ADC_SPEEDL 0x20 //360个时钟 #define ADC_SPEEDH 0x40 //180个时钟 #define ADC_SPEEDHH 0x60 //90个时钟 #define PARITYBIT EVEN_PARITY //定义校验位 #define S1_S0 0x40 //P_SW1.6 #define S1_S1 0x80 //P_SW1.7 //sbit P22 = P2^2; bit busy; void InitADC(); void SendData(BYTE dat); void SendString(char *s); void Delay(WORD n); void ShowResult(BYTE ch); char chagneInt(int a); BYTE GetADCResult(BYTE ch); void main() { P0M0 = 0x00; P0M1 = 0x00; P1M0 = 0x00; P1M1 = 0x00; P2M0 = 0x00; P2M1 = 0x00; P3M0 = 0x00; P3M1 = 0x00; P4M0 = 0x00; P4M1 = 0x00; P5M0 = 0x00; P5M1 = 0x00; P6M0 = 0x00; P6M1 = 0x00; P7M0 = 0x00; P7M1 = 0x00; ACC = P_SW1; ACC &= ~(S1_S0 | S1_S1); //S1_S0=0 S1_S1=0 P_SW1 = ACC; //(P3.0/RxD, P3.1/TxD) SCON = 0x50; //8位可变波特率 AUXR = 0x40; //定时器1为1T模式 TMOD = 0x20; //定时器1为模式2(8位自动重载) TL1 = (256 - (FOSC/32/BAUD)); //设置波特率重装值 TH1 = (256 - (FOSC/32/BAUD)); TR1 = 1; //定时器1开始工作 ES = 1; //使能串口中断 EA = 1; InitADC(); //初始化ADC while (1) { Delay(300); ShowResult(0); //显示通道0 } } /*---------------------------- 发送ADC结果到PC ----------------------------*/ void ShowResult(BYTE ch) { BYTE d; float v; char sendh,sendl,sendll; SendString("1."); d=GetADCResult(ch);//获取通道1的数据 //这里是使用公式计算出来的ADC的值 v=(d*5)/256; //这里是分别获取到这个数据的个数,小数位 sendh=chagneInt((int)v%10); sendl=chagneInt((v-(int)v)*10); sendll=chagneInt((int)((v-(int)v)*100)%10); SendData(sendh); SendString("."); SendData(sendl); SendData(sendll); SendString("v "); // SendData(ADC_LOW2); //显示低2位结果 } char chagneInt(int a) { return a+48; } /*---------------------------- 读取ADC结果 ----------------------------*/ BYTE GetADCResult(BYTE ch) { ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START; _nop_(); //等待4个NOP _nop_(); _nop_(); _nop_(); while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成 ADC_CONTR &= ~ADC_FLAG; //Close ADC return ADC_RES; //返回ADC结果 } /*---------------------------- UART 中断服务程序 -----------------------------*/ void Uart() interrupt 4 using 1 { if (RI) { RI = 0; //清除RI位 P0 = SBUF; //P0显示串口数据 P22 = RB8; //P2.2显示校验位 } if (TI) { TI = 0; //清除TI位 busy = 0; //清忙标志 } } /*---------------------------- 发送串口数据 ----------------------------*/ void SendData(BYTE dat) { while (busy); //等待前面的数据发送完成 ACC = dat; //获取校验位P (PSW.0) if (P) //根据P来设置校验位 { TB8 = 0; //设置校验位为0 } else { TB8 = 1; //设置校验位为1 } busy = 1; SBUF = ACC; //写数据到UART数据寄存器 } /*---------------------------- 发送字符串 ----------------------------*/ void SendString(char *s) { while (*s) //检测字符串结束标志 { SendData(*s++); //发送当前字符 } } /*---------------------------- 初始化ADC ----------------------------*/ void InitADC() { P1ASF = 0xff; //设置P1口为AD口 ADC_RES = 0; //清除结果寄存器 ADC_CONTR = ADC_POWER | ADC_SPEEDLL; Delay(2); //ADC上电并延时 } //延时函数 void Delay(WORD n) { WORD x; while (n--) { x = 5000; while (x--); } }
[/highlight]
代码效果图(这个代码的功能就是读出ADC通道1的数据,然后通过串口通信把数据打印出来)
定时器
下图是定时器的相关寄存器。先来讲一下TCON
还有TMOD
为了兼容传统的51,STC15有一个辅助寄存器。如果不用AUXR的话,速度就是12分频。当然也可以不进行12分频,这样的话速度就会提高很多。
还有一些其他的平时用的比较少,所以这里不讲。。。
模式选择问题,默认的模式0其实就已经足够我们使用了,我们只需要学习这个就可以了。
这里简单的讲一下寄存器的配置。
当C/T=0时,T0为计时模式为1的时候就是计数模式。
15的单片机有两个计数模式,一个是12T模式,这个与传统的51单片机相同。还有一个是1T模式,速度是传统51的12倍。T0的速率由特殊功能寄存器AUXR中的T0x12决定的,如果为1就是工作在1T模式。
下面是这个模式的说明。
来一个简短的C语言代码:
[highlight lanaguage=”C”]
#include "reg51.h" //#define PWM6BIT 64 //6-bit PWM 周期数 #define PWM8BIT 256 //8-bit PWM 周期数 //#define PWM10BIT 1024 //10-bit PWM 周期数 //#define PWM16BIT 65536 //16-bit PWM 周期数 #define HIGHDUTY 64 //高电平周期数(占空比64/256=25%) #define LOWDUTY (PWM8BIT-HIGHDUTY) //低电平周期数 sfr P0M1 = 0x93; sfr P0M0 = 0x94; sfr P1M1 = 0x91; sfr P1M0 = 0x92; sfr P2M1 = 0x95; sfr P2M0 = 0x96; sfr P3M1 = 0xb1; sfr P3M0 = 0xb2; sfr P4M1 = 0xb3; sfr P4M0 = 0xb4; sfr P5M1 = 0xC9; sfr P5M0 = 0xCA; sfr P6M1 = 0xCB; sfr P6M0 = 0xCC; sfr P7M1 = 0xE1; sfr P7M0 = 0xE2; sfr AUXR = 0x8e; //辅助寄存器 sfr INT_CLKO = 0x8f; //时钟输出控制寄存器 sbit T0CLKO = P3^5; //定时器0的时钟输出口 bit flag; //定时器0中断服务程序 void tm0() interrupt 1 { flag = !flag; //反转PWM的输出标志 if (flag) { //中断的时候就重装值就可以了 TL0 = (65536-HIGHDUTY); //准备高电平的重载值 TH0 = (65536-HIGHDUTY) >> 8; } else { TL0 = (65536-LOWDUTY); //准备低电平的重载值 TH0 = (65536-LOWDUTY) >> 8; } } void main() { P0M0 = 0x00; P0M1 = 0x00; P1M0 = 0x00; P1M1 = 0x00; P2M0 = 0x00; P2M1 = 0x00; P3M0 = 0x00; P3M1 = 0x00; P4M0 = 0x00; P4M1 = 0x00; P5M0 = 0x00; P5M1 = 0x00; P6M0 = 0x00; P6M1 = 0x00; P7M0 = 0x00; P7M1 = 0x00; AUXR = 0x80;//(1000 0000) 最前面那个是定时器 1T模式 //定时器0为1T模式 INT_CLKO = 0x01; //(这个是外部中断允许和时钟输出的寄存器,这里是打开定时器0) //使能定时器0的时钟输出功能 //设置定时器0为模式0(16位自动重装载) TMOD &= 0xf0; //这个是定时器计数器的工作模式 (1111 0000)把定时器的0的寄存器全部置为0 //这里解释一下 GATE=0 C/T=0 M1=0 Mo=0 //GATE是定时器的控制位 C/T为0进行计时模式 然后M1M0都是0就是选择了16位自动重装定时器 //下面两个是定时器的值的配置 TL0 = (65536-LOWDUTY); //初始化定时器初值和重装值 TH0 = (65536-LOWDUTY) >> 8; // T0CLKO = 1; //这里是把P3.4作为时钟输出口 //初始化时钟输出脚(软PWM口) flag = 0; //初始化标志位 TR0 = 1; //这个是TCON的配置部分了这里是开启定时器1 //定时器0开始计时 ET0 = 1; // 这个是T0溢出中断允许位,为1时允许中断 //使能定时器0中断 EA = 1; //这里是cpu总的中断允许控制位 while (1); }
[/highlight]
串口通信
串口通信也是15的一个非常重要的知识点。15上面有两个串口,一个是P3.1(TXD)P3.0(RXD)还有一个是P3.7(TXD)P3.6(RXD).我使用的是STCF2K60S2的芯片,所以只有两个串口,还有一种芯片有四个串口,这里就不介绍了。串口相关的寄存器:
单片机有两个控制寄存器一个是串行控制寄存器SCON还有一个是特殊寄存器PCON。
这里两个寄存器,我也不多讲(自己看数据手册就看的懂)
SBUF是串行口数据缓冲寄存器(有一个是写寄存器,还有一个是读寄存器)
与串行口1中断相关的寄存器有ES和PS(ES为1允许串口中断,PS为1串口1中断为最高优先级)
OLED显示屏
参考文章
1.keil5添加stc芯片库