PIC学习心得_pic使用注意
PIC学习心得由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“pic使用注意”。
PIC学习心得
本次心得为PIC18F25K22上的代码
零、配置CPU 的特殊功能,地址从300000开始,如以下,有晶振选择,复位功能,看门狗配置,调试口rb7-rb6的配置,因为RB6-RB7为串口2的端口,需要对应为位=1才能使得他们可以用于普通io,其实单片机默认也是1.对于已经用于特殊功能的引脚,比如已经配置了引脚为I2C功能,如果要配置为普通IO时,需要将I2C失能掉,SSPXCON1=0X00;这样才能正常使用这2个IO口了
如果要使单片机使用内部晶振,就需要将对应的OSCCON位置1 IRCF:内部RC 振荡器频率选择位(2)IRCF=111 = HFINTOSC –(16 MHz)OSTS=0 = 器件依靠内部振荡器(HFINTOSC、MFINTOSC 或LFINTOSC)运行 OSCCON2默认
使用内部晶振时,频率会随温度漂移,使用波特率的时候要注意了,最好使用外部晶振
PWDX默认,使外围功能使能
#pragma romdata CONFIG1H=0x300001
//晶振 const rom unsigned char config1H = 0x0a;//setting for HS oscillator中档功耗 #pragma romdata CONFIG2L=0x300002
//复位 const rom unsigned char config2L = 0x00;#pragma romdata CONFIG2H=0x300003 const rom unsigned char config2H = 0x1e;//WDT #pragma romdata CONFIG3H=0x300005//配置一些端口的映射
const rom unsigned char config3H = 0x00;//禁用复位脚,开始为 CPU提供时钟而无需等待
#pragma romdata CONFIG4L=0x300006//最高位为1,RB6 和 RB7用于普通IO口
const rom unsigned char config4L = 0x80;//RB6 和 RB7专用于在线调试 #pragma romdata
一、中断配置,PIC只有高低2个优先级,对应的函数有2个,函数地址为0X08和0X18中断配置,一下为高低优先级配置
RCONbits.IPEN=1;//使能中断优先级,如果IPEN配置为0,没有中断优先级,所有优先级都为高,调到0x08地址的中断
INTCONbits.GIE=1;//=允许所有高优先级中断 INTCONbits.PEIE=1;//=允许所有低优先级的中断
2、中断函数写法 高优先级地址为0X08 #pragma code InterruptVectorHigh = 0x08 void InterruptVectorHigh(void){
_asm
goto InterruptHandlerHigh
//jump to interrupt routine,goto之后的InterruptHandlerHigh函数名可以自己取名
_endasm } // High priority interrupt routine #pragma code #pragma interrupt InterruptHandlerHigh //InterruptHandlerHigh函数名可以自己取名
void InterruptHandlerHigh()//中断函数内容 低优先级函数地址为0X18 #pragma code InterruptVectorLow= 0x18 void InterruptVectorLow(void){
_asm
goto InterruptHandlerLow //jump to interrupt routine,goto之后的InterruptHandlerHigh函数名可以自己取名
_endasm } // High priority interrupt routine #pragma code #pragma interruptlow InterruptHandlerLow //InterruptHandlerHigh函数名可以自己取名
void InterruptHandlerLow(void)//中断函数内容
3、例如串口中断配置:
IPR1bits.RC1IP=0;//设为低优先级,让他跳入低优先级中断函数,为1将跳入高优先级函数
PIR1bits.RC1IF=0;//清中断标志
PIE1bits.RC1IE=1;//接收中断允许
PIE1bits.TX1IE=0;//发送中断禁止
4、这样配置完成后,如果串口其他寄存器配置无误,接收到数据后就会跳入低优先级函数InterruptHandlerLow中
二、ADC1、ADC采集为了不浪费MCU资源,不用中断函数来采集 配置如下,(1)先将要用于ADC采集的IO口配置为输入(2)将对应IO扣配置为ADC输入(3)配置ADCON0-3寄存器
TRISA|=0x01;//引脚方向寄存器,1输入,0输出。将对应引脚定义为输入
ANSELA|=0x01;//对应的引脚定义为ADC输入
ADCON1=0b00000000;
//正向参考电压VDD
ADCON2=0b10001101;
//ADC结果右对齐保存,采集时间2TAD,时钟Fosc/16
ADCON0|=0x01;
//使能ADC1、开始采集(1)、ADCON0&=(~0x7c);ADCON0|=(channel
//数模转换结果AD值
{
uint16 adval;uint8 k;
ADCON0&=(~0x7c);
ADCON0|=(channel
//配置转换通道
adval=0;for(k=0;k
{
ClrWdt();
ADCON0bits.GO_DONE=1;//开ADC转换
while(ADCON0bits.GO_DONE);//当ADGO为0表示这次ADC转换完成?
adval=adval+((ADRESH&0X03)*256)+(ADRESL);//00000011 11111111 把转换结果的高位和低位放进同一个16位数里面
}
adval=adval/ADC_SAMPLETIME;
//取样50次,取平均值
return(adval);//将结果返回
}
三、UART配置
1、配置(1)、对应IO口配置为输入TRISC(2)、对应的IO口配置为数字端口ANSELC(3)、配置SYNC=0,使得串口为异步的(4)、配置波特率,公式根据BRG16,BRGH来配置,都=0,公式为((SPBRGH1
void Uart1_initial(void)//test { TRISCbits.TRISC7=1;//RX1,将IO两个端口设置为输入
TRISCbits.TRISC6=1;//TX1, ANSELCbits.ANSC7=0;ANSELCbits.ANSC6=0;
//自动波特率溢出位1溢出,接收空闲标志位1空闲,接收极性选择位1为低,1发送口空闲为高、// 1使能16为波特率发生器,无,唤醒使能位,自动波特率检测使能位
//BAUDCTL=0b00000000;//??? //((16000000/9600)/64)-1=25;SPBRGH1=0x00;
//((16000000/9600)/16)-1=103;SPBRG1 =103;//根据SYNC BRG16 BRGH这3位来判断计算公式,具体看手册//0X19;
//9600
25=0X19;////////波特率说明:波特率配置时,先使能外部时钟,16M外部时钟不能设置56k以上波特率,误差很大
RCSTA1=0b10010000;//串口使能
//使能串口,9-8接收位,单字节接收使能(异步无关),连续接收使能,//地址检测使能,帧错误标志位,溢出标志,接收第9为
TXSTA1=0b00100100;//选择异步,高速方式,传输8位数据, //异步无关位,8位,使能发送,异步,发送间隔字符位,高低波特率,发送寄存器满,第9位
//RCONbits.IPEN=1;//使能中断优先级。
//IPR1bits.TX1IP=0;//设为高优先级
BAUDCON1=0b00000000;//自动波特率检测使能不要值1 IPR1bits.RC1IP=0;//设为低优先级
PIR1bits.RC1IF=0;//清中断标志
PIR1bits.TX1IF=0;PIE1bits.RC1IE=1;//接收中断允许
PIE1bits.TX1IE=0;//发送中断禁止
UART1Receive.ReceiveDataLen=0;UART1Receive.IndexPointer=UART1Receive.ProPointer=0;}
四、定时器0 定时器就比较简单了,处理中断的3个位(在INTCON,INTCON2中),就一个控制寄存器T0CON,和2个计数寄存器TMR0H, TMR0L。
1、Timer0预分频的初始时钟=Fosc/4,于是计算的时候不能以FOSC来计算
2、在中断后,如果不需要再中断,失能定时器,失能中断。如果还需要中断,需要将数据再次填入计数寄存器中,并且中断标志清0。配置如下 void Timer0_init(void)//不用作定时器,计风扇周期个数 { //定时器0时钟源为FOSC/4
////Timer1/3/5 时钟源为FOSC和FOSC/4 T0CON=0b10000111;//开启定时器0,256预分频
TMR0H=0xe7;
//TMRX=65536-T*时钟源/分频数,如果定时器0定时1s,fosc=16M的话TMR0=65536-1*16000000/4/256=0xc2f7.//TMR0L=0xdc;
//对TMR0写入一个调整值,因为写入TMR0后接
TMR0L=0x96;
//着的两个周期不能增量,中断需要3个周期的响应
//时间,以及C语言自动进行现场保护要消耗周期
INTCONbits.TMR0IE=1;//中断允许 INTCONbits.TMR0IF=0;//中断标志
INTCON2bits.TMR0IP=0;//优先级为低,然后再低优先级函数通过中断标志写上对应的函数就可以了 }
五、I2C 参考百度中的笔记
#pragma udata DataArray5
//一个数据块的开始 uint8 I2CCommuSaveBuf[SaveBufLen];//通讯缓冲区 #pragma udata //一个数据块的结束
PIC的一个块为256个字节,不能定义比这个更长的数组了。如果直接定义一个256数组,编译器可能会出错,需要以上配置成一个块才可以用
六、外部触发中断
void ExternINT0_Init(){ TRISBbits.TRISB0=1;//将IO两个端口设置为输入
PORTBbits.RB0=0;ANSELBbits.ANSB0=0;//数字信号
INTCON2bits.INTEDG0=1;//上升沿中断,0=下降沿中断
INTCONbits.INT0IF=0;//中断清0 INTCONbits.INT0IE=1;//中断使能
//没有优先级位,跳高优先级中断 }
void ExternINT1_Init(){ TRISBbits.TRISB1=1;//将IO两个端口设置为输入
ANSELBbits.ANSB1=0;//数字信号
PORTBbits.RB1=0;INTCON2bits.INTEDG1=1;//上升沿中断,0=下降沿中断
INTCON3bits.INT1IF=0;//中断清0 INTCON3bits.INT1IE=1;//中断使能
INTCON3bits.INT1IP=0;//优先级低
}
七、内部EEPROM读写
对于18F25K22:EEADR 寄存器用于寻址数据EEPROM 以进行读写操作。8 位的寄存器可寻址256 字节(00h 至FFh)的存储器范围。通过增加2个额外的地址位,EEADRH 寄存器将范围扩展到1024 字节。以下为256字节的EEPROM读写
u8 EEPROM_ReadOneData(u8 readADD){ PIE2bits.EEIE=0;//将中断失能掉
EEADR=readADD;//写入地址。//也可以EEADRH= readADD>>8,EEADR= readADD,(readADD需要16位),来实现1024字节的读
EECON1bits.CFGS=0;//访问闪存程序存储器或数据EEPROM 存储器
EECON1bits.EEPGD=0;//访问数据EEPROM 存储器
EECON1bits.RD=1;//启动EEPROM 读操作
Nop();Nop();//等待读取完成,读EEPROM是很快的return(EEDATA);}
void EEPROM_WriteOneData(u8 WriteADD,u8 WriteData){ PIE2bits.EEIE=0;//将中断失能掉
EEADR=WriteADD;//也可以EEADRH= readADD>>8,EEADR= readADD,(readADD需要16位),来实现1024字节的写
EEDATA=WriteData;//放入要写入的数据
EECON1bits.CFGS=0;//访问闪存程序存储器或数据EEPROM 存储器
EECON1bits.EEPGD=0;//访问数据EEPROM 存储器
//EECON1bits.EEPGD=0;//访问数据EEPROM 存储器
EECON1bits.WREN=1;//写使能
EECON2=0x55;//写操作命令
EECON2=0xaa;//这两句话是必须的EECON1bits.WR=1;//开始写操作,必须先使能写WREN位,再开启写操作
//WR由硬件清0 while(!PIR2bits.EEIF);//等待写完成PIR2bits.EEIF=0;//必须软件清0 EECON1bits.WREN=0;//写失能 }
八、18B20读温度
1、首先初始化设备,根据以上时序图,先将对应脚输出置高,然后置低,保持480-960US,然后再置高,设置引脚输入等待18B20应答:等待18B20置低15-60US,再等待置高60-240us。
2、写命令:写入0xcc,这个命令允许总线控制器不用提供64位ROM编码就使用功能指令。然后写入0x44,开始转换温度。每写一个位如上图所示,先将端口输出置高>1US-置低,维持>1US,然后写入该位,等待18B20接收数据,维持15-45US,再写下一位
3、读命令:先要将总线输出置高>1us,置低,维持1-10us。然后读入数据,等待15-45US,在读下一位
4、读温度:每次发命令之前,需要将首先初始化设备,然后写入指令。比如每次读一次温度顺序:初始化-写入0XCC(不用提供64位ROM编码使用)-写入0X44(开始温度转换)-初始化-写入0XCC-写入0XBE(读温度指令)-读温度字节低-读温度字节高=完成。
九、PWM设计
//////////////////////////////////////////////////////////////////////////// //说明:18f25k22有5个10位CCP,CCP2和CCP3需要通过配置config3H_image和config3H定义输出脚
//PWM配置,如果2个ccp用一个定时器,那么他们2个的PWM周期是必定一样
//但是他们的占空比可以分别调制
/////////////////////////////////////////////////////////////////////////// 以下对CCP2进行解说:
1、首先你需要将CCP口映射到其他端口上需要将特殊寄存器config3H对应位置12、将对应的端口设置为输入
3、选择CCP需要的定时器,他是在2/4/6定时器选择
4、设置定时器周期,5、配置定时器,CCP不需要中断,根据实际情况配置器分频,使能
6、配置占空比
7、在程序运行过程中需要调节占空比,配置CCP2CON的4-5位为10位的第2位,CCPR2L为10位的高8位。如:
CCP2CON|=((receivebuf[2]&0x03)
CCPR2L=receivebuf[1];以下为CCP2实用定时器4的配置:
void CCP2_PWM_init(void){
u8 tem1;tem1=config3H_image;if(!(config3H_image&0x01))
TRISB|=0x08;//手册说设置为输入,输出在PB3,根据CONFIG3H else
TRISC|=0x02;//手册说设置为输入,输出在PC1 CCPTMRS0&=(~0B00010000);//使用定时器4 CCPTMRS0|=
0B00001000;//使用定时器4
PR4=0Xff;//设置pwm工作周期
//PWM频率=Fosc/4/(PRX+1)/(TMR2预分频值)CCP2CON=0x0f;//PWM模式,占空比低2位为0 CCPR2L=0x80;//占空比高8位
//占空比=(CCPR1L*4+CCP1CON)/(4*(PRx + 1))
//因为PWM是用定时器246来做的,一下配置用到的2、4、6定时器
T4CON=0B00000100;//预分频为1/4 ,使能定时器2 //T4CON=0B00000111;
//0000 0100 //T4CON=0X04;
//打开TMR2,且使其前后分频为1,同时开始输出PWM波形
PIE2bits.CCP2IE=0;//禁止CCP2中断
PIE5bits.TMR4IE=0;//失能定时器4中断
PIR5bits.TMR4IF=0;//清定时器4中断标志
while(!PIR5bits.TMR4IF);if(!(config3H_image&0x01))
TRISB&=(~0x08);//最后将端口设置为输出,输出在PB3 else
TRISC&=(~0x02);//最后将端口设置为输出,输出在PC1 }
十、普通IO配置
本以为普通IO口配置是很简单的,但是实际用起来之后才发现并不是那样的,普通IO口无论是输入还是输出都需要配置TRISX方向寄存器,和ANSELX模拟IO口,ANSELX默认是1,也就是说ANSELX默认是模拟IO口,除了我们用到ADC或者DAC时才ANSELX=1;其他情况下都要添加ANSELX=0语句。
TRISB|=0x21;//0x21pgin,protetion ANSELB&=(~0x21);
TRISA|=0x01;//pson ANSELA&=(~0x01);
//out TRISC&=(~0x87);//0,1,2,7 ANSELC&=(~0x87);
TRISA&=(~0xfc);// 2,3,4,5,6,7 ANSELA&=(~0xfc);
TRISB&=(~0x14);// 2.4 ANSELC&=(~0x14);// 2.4