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->Rx
TXCKSEL = 0; //RA0->Tx
TXSTA = 0x2c; // Slave mode and Transmit enabled
RCSTA = 0x90; // Enables receiver
BAUDCON = 0x80; //Uart enabled
SPBRGH = 0;
SPBRGL = 0x19; //Baud Rate is 9600
RCIE = 1; //open Uart recive interput
TXIE = 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 interput
TXREG = d
while(TXIF == 0); //wait Trismit
TXIF = 0;
}
}
三、定时器
定时器是用来计时、计数的,几乎任何单片机程序都会用到定时器。配置定时器一般会配置时钟选择、中断使能、中断标志位、装初值、预分频等。定时器一般溢出中断后需要重新赋初值。
定时器与预分频器
我在最开始学单片机始终没搞明白预分频,其实预分频也挺好理解的。在没有预分频器情况下。开启定时器每隔一个指令周期定时器就加一。假设时钟是4MHz ,定时器时钟 = 系统时钟/4,也就是每隔 1us 定时器加一。如果有了预分频器假设预分频器设置成2分频,定时器就 每隔2us定时器加一。如果预分频器设置成4分频,定时器就 每隔4us定时器加一,以此类推。下图以预分频和周期来分析定时器:
定时器0(Time0)的简单代码如下:
void Init_Timer0(void)
{
OPTION_REG=0x87; //设置预分频位256
TMR0IE=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管脚,使能ADC
ADCON1 = 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 input
TRISA = 0x16; //RA1,RA2,RA4 is input,RA0,RA5 is output
/*init_uart*/
/*
RXDTSEL=0;//RA1->Rx
TXCKSEL=0;//RA0->Tx
TXSTA=0x2c;//?????????
RCSTA=0x90;//?????????
BAUDCON=0x80;//baud raie control register
SPBRGH=0;
SPBRGL=0x19;//Baud Rate is 9600
RCIE=1; //open Uart recive interput
TXIE=0; //close Uart trismit interput
*/
/*init_Timer0; interrupt function need clear interrupt Flag bit*/
OPTION_REG=0x87; //Prescaler is 256
TMR0IE=1;//Timer0 Overflow Interrupt Enable
TMR0IF=0;//clear Timer0 Overflow Interrupt Flag bit
TMR0=61;//give Timer0 initial value,so 50ms
/*init_timer1*/
T1CON=0xc5; //enable Timer1 , Clock Prescale is 1:1
TMR1IF=0;//clear Timer1 Overflow Interrupt Flag bit
TMR1IE=1;//Enables the Timer1 overflow interrupt
TMR1H=0;//give Timer1 initial value,so 500ms
TMR1L=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 Interrupt
flag = 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 Interrupt
TMR0IF=0; //clear Timer0 Overflow Interrupt Flag bit
TMR0=61; //give Timer0 initial value,so 50ms
TMR1IF=0;
TMR1H=0; //give Timer1 initial value
TMR1L=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 bit
TMR0=61;//give Timer0 initial value,so 50ms
TMR1IF=0;
TMR1H=0;//give Timer1 initial value
TMR1L=0;
}
if(TMR1IF==1)
{
TMR1IF=0;//clear Timer1 Overflow Interrupt Flag bit
TMR1H=0;//give Timer1 initial value,so 100ms
TMR1L=0;
}
}