PIC芯片所用编译器是MPLAB X IDE,刚开始接触PIC单片机,也是一脸茫然,然后查阅资料逐渐了解、运用编译器,这里我就不说编译器怎么使用了,下面我将以PIC12LF1822芯片为例,简单来说说我见解,分别说说时钟、串口、定时器、ADC、触摸按键。
一、时钟
时钟一般有内部时钟(系统时钟)、外部时钟。单片机时钟可选择系统时钟、外部时钟。时钟相当于单片机的心脏,每跳动一下,整个单片机的各个电路就同步的动作一下。
时钟配置
配置PIC12LF1822时钟,一般只配置OSCCON寄存器就可以实现一些简单的功能,OSCCON寄存器如下图

时钟初始化:
void Init_Fosc()
{OSCCON = 0xf0;//时钟频率32MH,内部时钟;
}
二、串口通信(EUSART)
当今单片机一般都是用的异步串行通信,至于什么是异步串口通信这里就不累赘了。(关于串口可以看我另一个文章)
配置串口
一般配置串口会去配置单片机的哪个管脚为数据发送脚、数据接收脚、设置波特率、发送中断使能位、接受中断使能位、发送中断标志位、接收中断标志位。
PIC12LF1822的波特率计算公式:
波特率 = 系统时钟/(4[n+1]); 将计算出来的n值放入,波特率的高8位和低8位;
比如我们把波特率定位9600,时钟定为4MHz。计算出 n = 25,则波特率寄存器装值如下:
SPBRGH=0;
SPBRGL=0x19;//Baud Rate is 9600
串口通信代码如下:
#include <xc.h>
char ch;
void Init_Uart(void)
{RXDTSEL = 0; //RA1->RxTXCKSEL = 0; //RA0->TxTXSTA = 0x2c; // Slave mode and Transmit enabledRCSTA = 0x90; // Enables receiverBAUDCON = 0x80; //Uart enabled SPBRGH = 0;SPBRGL = 0x19; //Baud Rate is 9600RCIE = 1; //open Uart recive interputTXIE = 0; //close Uart trismit interput
}void main(void)
{Init_Uart();while(1);return;
}void interrupt ISR(void) //?????????
{ if(RCIE==1) //串口接受中断{ch = RCREG;RCIE = 0;TXIE = 1; //open Uart trismit interputTXREG = d while(TXIF == 0); //wait TrismitTXIF = 0;}}
三、定时器
定时器是用来计时、计数的,几乎任何单片机程序都会用到定时器。配置定时器一般会配置时钟选择、中断使能、中断标志位、装初值、预分频等。定时器一般溢出中断后需要重新赋初值。
定时器与预分频器
我在最开始学单片机始终没搞明白预分频,其实预分频也挺好理解的。在没有预分频器情况下。开启定时器每隔一个指令周期定时器就加一。假设时钟是4MHz ,定时器时钟 = 系统时钟/4,也就是每隔 1us 定时器加一。如果有了预分频器假设预分频器设置成2分频,定时器就 每隔2us定时器加一。如果预分频器设置成4分频,定时器就 每隔4us定时器加一,以此类推。下图以预分频和周期来分析定时器:

定时器0(Time0)的简单代码如下:
void Init_Timer0(void)
{ OPTION_REG=0x87; //设置预分频位256TMR0IE=1; //Timer0溢出中断使能 TMR0IF=0; //清空Timer0中断标志位TMR0=61; //给Timer0赋初值,使Timer0溢出时间为50ms
}
void interrupt ISR(void) //?中断函数
{ if(TMR0IF==1){/*该处写中断后你想实现的代码,并清零中断标志位、给Timer0重新装初值*/TMR0IF=0;TMR0=61; //重新装初值 }
}
四、ADC
ADC即模/数转换器,把模拟信号转化为数字信号。数字量就是用 0和1 组成二进制代码表示某个信号大小的量。AD转换一般通过采样、量化、编码三个步骤。
配置ADC
ADC控制寄存器图如下:


ADC简单代码如下:
void AD_Init()
{ ANSELA = 0x01; //RA0为模拟口TRISA = 0x01; //RA0为输入ADCON0 = 0x01; //选择通道AN0即RA0管脚,使能ADCADCON1 = 0x20; //左对齐,时钟为F OSC /32
}
unsigned int AD_get_value()
{unsigned int value = 0;ADCON0bits.ADGO=1; //开始转换while(ADCON0bits.GO==1); //等待转换结束value=(unsigned int)ADRESH; //强制类型转换,因为ADRESH是字符型的只能表示8位二进制。//所以必须转换成可以容纳10位二进制的整型value= value<<2; // 将高两位左移8位value += ADRESL; //低八位加入ADRESL的值return value;
}
unsigned int AD_average_value()
{unsigned int AD_value[10] = {0};unsigned int AD_average_value = 0;unsigned int i;for(i=0;i<10;i++){AD_value[i] = AD_get_value();AD_average_value += AD_value[i];}AD_average_value = AD_average_value/10;return AD_average_value;
}
五、触摸按键
按键触摸原理
记录电容充放电的次数,当手触摸时电容充放电次数低于平均值,具体原理看《AN1101》、《AN1103电容触摸传感的软件处理》。
配置触摸按键步骤
- 设置芯片哪个管脚为触摸脚


- 将Timer1时钟源设置为电容式传感振荡器。
Timer1来计电容充放电的次数,由Timer0来计时。Timer0每隔段时间触发一次中断,来读取Timer1充放电的次数。必须设置Timer0的预分频器,使得Timer0比Timer1先溢出。value = TMR1L + (unsigned int)(TMR1H << 8)。

3.判断当前频率次数是否低于正常的未触压的平均值。
触摸按键代码如下
#include <xc.h>
#define true 1
#define false 0
unsigned char flag;
static int index;
static int num;
long raw[2] = {0};
long average[2] = {0};
long trip[2] = {2256,7000};
unsigned int CPS[2] = {0x02,0x03};
void init(void)
{/*init_fosc*/OSCCON = 0x6a;//4MHz/*init_interrupt*/INTCON = 0xc0;//open interrupts/*init_gpio*/ANSELA = 0x14;//RA0,RA5 is Digital,RA2,RA4 is Analog inputTRISA = 0x16; //RA1,RA2,RA4 is input,RA0,RA5 is output/*init_uart*//*RXDTSEL=0;//RA1->RxTXCKSEL=0;//RA0->TxTXSTA=0x2c;//?????????RCSTA=0x90;//?????????BAUDCON=0x80;//baud raie control registerSPBRGH=0;SPBRGL=0x19;//Baud Rate is 9600RCIE=1; //open Uart recive interputTXIE=0; //close Uart trismit interput*//*init_Timer0; interrupt function need clear interrupt Flag bit*/OPTION_REG=0x87; //Prescaler is 256TMR0IE=1;//Timer0 Overflow Interrupt Enable TMR0IF=0;//clear Timer0 Overflow Interrupt Flag bitTMR0=61;//give Timer0 initial value,so 50ms/*init_timer1*/T1CON=0xc5; //enable Timer1 , Clock Prescale is 1:1TMR1IF=0;//clear Timer1 Overflow Interrupt Flag bitTMR1IE=1;//Enables the Timer1 overflow interruptTMR1H=0;//give Timer1 initial value,so 500msTMR1L=0;/*touch_init*/CPSCON0=0x8c;CPSCON1=0x02;
}void putch(unsigned char byte) //重定义
{TXREG = byte; //Uart_Data while(TXIF == 0); //等待发送TXIF = 0;
}void main(void)
{init();flag = false;index = 0;num = 0;//PORTA = 0x21;unsigned int i;while(1){ if(flag){TMR0IE=0; //close Timer0 Overflow Interruptflag = false;if(raw[index] < average[index] - trip[index]){switch(index){case 0://printf("The bot button is pressed\r\n");PORTA |= 0x20;break;case 1://printf("The top button is pressed\r\n");PORTA |= 0x01;break;default:break; }}else{if((raw[0] > average[0] - trip[0]) && (raw[1] > average[1] - trip[1])){PORTA = 0x00;}average[index] = average[index] + (raw[index] - average[index])/16;}index++;if(index >= 2)index = 0;TMR0IE=1; //close Timer0 Overflow InterruptTMR0IF=0; //clear Timer0 Overflow Interrupt Flag bitTMR0=61; //give Timer0 initial value,so 50msTMR1IF=0;TMR1H=0; //give Timer1 initial valueTMR1L=0;} }return;
}void interrupt ISR(void)
{ if(TMR0IF==1){flag = true;raw[num] = TMR1L + (unsigned int)(TMR1H << 8);num++;if(num>=2){num = 0;}CPSCON1 = CPS[num];TMR0IF=0;//clear Timer0 Overflow Interrupt Flag bitTMR0=61;//give Timer0 initial value,so 50msTMR1IF=0;TMR1H=0;//give Timer1 initial valueTMR1L=0;}if(TMR1IF==1){TMR1IF=0;//clear Timer1 Overflow Interrupt Flag bitTMR1H=0;//give Timer1 initial value,so 100msTMR1L=0;}
}












