目录
- 为什么用C语言开发PIC?
- 开发环境搭建
- C语言基础与PIC特殊关键字
- 核心外设编程(GPIO, 定时器, UART等)
- 实战项目:LED闪烁与串口通信
- 进阶主题
- 总结与学习资源
为什么用C语言开发PIC?
虽然PIC可以用汇编语言编程,但C语言在现代嵌入式开发中占据绝对主导地位,主要原因如下:
- 可读性与可维护性:C语言更接近人类语言,代码结构清晰,易于阅读、修改和团队协作。
- 开发效率高:无需关心复杂的寄存器位操作,C编译器会帮你处理,你只需专注于“做什么”,而不是“怎么做”。
- 可移植性强:为某个PIC系列(如PIC16)编写的代码,经过少量修改(主要是头文件和配置位),就可以移植到同系列或不同架构(如PIC18, PIC32)的单片机上。
- 丰富的库函数:Microchip官方和第三方提供了大量的库函数(如MPLAB Harmony),极大地简化了驱动外设的开发工作。
- 模块化编程:通过
.c和.h文件,可以轻松地将代码划分为功能模块,使项目结构更清晰。
开发环境搭建
要进行PIC单片机C语言开发,你需要以下两个核心软件:
- MPLAB X IDE:这是Microchip官方的集成开发环境,它是一个免费的代码编辑器、项目管理器和编译器前端。
- XC编译器:这是Microchip的官方C语言编译器,你需要根据你使用的PIC芯片架构来选择:
- XC8:用于8位PIC单片机(如PIC10, PIC12, PIC16, PIC18)。
- XC16:用于16位PIC单片机(如PIC24, dsPIC)。
- XC32:用于32位PIC单片机(如PIC32)。
安装步骤:
- 下载并安装 MPLAB X IDE。
- 在安装MPLAB X时,它会提示你安装所需的编译器,勾选你需要的XC编译器(对于初学者,XC8是必须的)。
- 安装完成后,打开MPLAB X,创建一个新项目,选择你的目标芯片型号,环境就搭建好了。
C语言基础与PIC特殊关键字
标准的C语言语法同样适用于PIC,但PIC的C编译器(如XC8)扩展了一些特殊的关键字,用于直接操作单片机的硬件,这些是PIC C编程的核心。
1 关键数据类型
除了标准的int, char, float等,XC8还定义了一些与硬件相关的数据类型,在头文件(如<xc.h>)中定义:
unsigned char:通常用于8位寄存器操作,如端口寄存器。unsigned int:通常用于16位寄存器操作,如定时器寄存器。volatile:非常重要的关键字!告诉编译器这个变量可能会被硬件(如中断)改变,因此编译器不要对其进行优化(不要把它放在寄存器里缓存),所有与硬件寄存器和中断服务程序相关的变量都应该声明为volatile。
2 特殊功能寄存器
在PIC中,控制外设(如GPIO、定时器)是通过读写特殊的内存地址——特殊功能寄存器来实现的,在C语言中,我们通过宏定义来访问它们。
在<xc.h>头文件中,你会找到类似这样的定义:
#define PORTA 0xF80 // PORTA寄存器的地址 #define TRISA 0xF92 // PORTA方向寄存器的地址
你不需要直接使用地址,而是使用更具可读性的名称:
#include <xc.h> // 必须包含这个头文件
void main() {
TRISA = 0x00; // 将PORTA的所有引脚设置为输出
PORTA = 0xFF; // 将PORTA的所有引脚置高电平
}
3 核心关键字
-
#pragma config:这是最重要的指令之一,用于配置单片机的配置位,配置位是单片机上电时的一些基本设置,FOSC:选择时钟源(外部晶振、内部振荡器等)。WDTEN:看门狗是否使能。LVP:是否允许低电压编程。PBADEN:PORTB的引脚是否在低电平时用作模拟输入。- 配置位必须在代码的最开始设置,因为它们决定了单片机的基本行为。
示例:
#pragma config FOSC = HS // 使用高速外部晶振 #pragma config WDTEN = OFF // 关闭看门狗 #pragma config PBADEN = OFF // 将PORTB<5:0>设置为数字I/O
-
__CONFIG:这是旧版(如PIC16)的配置位设置方式,XC8仍然支持,但推荐使用#pragma config。 -
__interrupt:用于定义中断服务程序,当中断事件发生时(如定时器溢出、外部引脚电平变化),程序会跳转到这个函数执行。示例:
void __interrupt myISR() { // 中断处理代码 if (TMR0IF) { // 检查TMR0溢出中断标志 // ...处理代码... TMR0IF = 0; // 清除中断标志,非常重要! } }
核心外设编程
下面我们以最常用的PIC16F877A为例,讲解如何用C语言控制几个核心外设。
1 GPIO (通用输入/输出) - 控制LED
这是最简单的入门实验,假设我们要连接一个LED到RC0引脚。
- 设置方向:
TRISC寄存器控制PORTC的引脚方向。0为输出,1为输入。TRISC = 0x00; // 将PORTC的所有8个引脚都设置为输出
- 输出电平:
PORTC寄存器控制PORTC的引脚电平。1为高电平(VDD),0为低电平(VSS)。PORTC.F0 = 1; // 将RC0引脚置高,LED亮 PORTC.F0 = 0; // 将RC0引脚置低,LED灭 // 或者 RC0 = 1; // 使用位操作语法,更简洁
2 定时器 - 实现精确延时
PIC的定时器是其核心功能之一,我们可以用它来产生精确的延时或周期性事件,这里以Timer0为例。
-
配置Timer0:
T0CS:时钟源选择。0为内部指令周期,1为外部T0CKI引脚。T0SE:边沿选择。0为上升沿,1为下降沿。PS2:PS0:预分频器选择,用于减慢定时器的计数速度。
示例配置:使用内部时钟,1:256预分频
OPTION_REGbits.T0CS = 0; // 使用内部时钟 OPTION_REGbits.PS = 0b111; // 设置预分频比为1:256 // 或者直接写值 OPTION_REG = 0b11000111; // T0CS=0, PSA=0(分频给TMR0), PS2-PS0=111(1:256)
-
使用Timer0延时:
TMR0寄存器从0开始向上计数,计满(255)后会溢出,并置位TMR0IF中断标志。- 我们可以通过计算溢出次数来实现延时。
示例:延时约1秒
void delay_1s() { unsigned char i; for (i = 0; i < 61; i++) { // 循环61次,每次约16.384ms TMR0 = 0x3C; // 装入一个初值,例如60 (256-60=196 counts) while(!TMR0IF); // 等待TMR0溢出 TMR0IF = 0; // 清除中断标志 } }
3 UART (串行通信) - 与PC通信
UART可以让PIC与PC或其他设备进行串行通信,方便调试和数据显示。
-
配置UART:
- 波特率:需要根据时钟频率和SPBRG寄存器的值计算。
- 数据位/停止位:通过
TXSTA和RCSTA寄存器配置。
示例:配置9600波特率,8位数据,1位停止位,无校验
// 假设使用4MHz晶振 TXSTA = 0x20; // TXEN=1 (发送使能), SYNC=0 (异步模式), BRGH=1 (高速波特率) RCSTA = 0x90; // SPEN=1 (串口使能), CREN=1 (连续接收使能) SPBRG = 25; // 4MHz / (64 * (25+1)) ≈ 2403.8 bps (接近9600,需更精确计算) // 对于4MHz, 9600bps, BRGH=1, SPBRG = (4000000 / (16 * 9600)) - 1 = 25.04, 取25
-
发送和接收数据:
// 发送一个字符 void UART_SendChar(char data) { while(!TXIF); // 等待发送缓冲区空 TXREG = data; // 将数据写入发送寄存器 } // 接收一个字符 char UART_ReceiveChar() { while(!RCIF); // 等待接收到数据 return RCREG; // 读取接收寄存器 }
实战项目:LED闪烁与串口信息
这个项目结合了GPIO和UART,实现LED闪烁,并通过串口打印信息。
硬件: PIC16F877A, 4MHz晶振, LED接RC0, MAX232芯片用于串口电平转换。
代码:
#include <xc.h>
#pragma config FOSC = HS // 高速外部晶振
#pragma config WDTE = OFF // 关闭看门狗
#pragma config PWRTE = ON // 上电延时定时器使能
#pragma config BOREN = ON // 掉电检测使能
#pragma config LVP = OFF // 禁用低电压编程
#pragma config CPD = OFF // 禁止数据EEPROM代码保护
#pragma config WRT = OFF // 禁止Flash程序存储器写保护
#pragma config CP = OFF // 禁止代码保护
#define _XTAL_FREQ 4000000 // 定义晶振频率,方便延时函数使用
// 函数声明
void UART_Init();
void UART_SendString(const char *str);
void main() {
TRISC = 0x00; // 设置PORTC为输出
PORTC = 0x00; // 初始状态,LED灭
UART_Init(); // 初始化串口
UART_SendString("PIC UART Test\r\n"); // 发送欢迎信息
while(1) {
RC0 = 1; // LED亮
__delay_ms(500); // 使用XC8内置的延时函数
RC0 = 0; // LED灭
__delay_ms(500);
UART_SendString("LED Toggle\r\n"); // 每次闪烁都发送信息
}
}
// UART初始化函数
void UART_Init() {
TXSTA = 0x20; // 发送使能, 异步模式, 高速波特率
RCSTA = 0x90; // 串口使能, 连续接收使能
SPBRG = 25; // 4MHz晶振, 9600波特率
}
// UART发送字符串函数
void UART_SendString(const char *str) {
while (*str != '\0') {
while(!TXIF); // 等待发送缓冲区空
TXREG = *str; // 发送当前字符
str++; // 指向下一个字符
}
}
进阶主题
当你掌握了基础后,可以探索更高级的主题:
- MPLAB Harmony:Microchip官方的、现代化的嵌入式软件框架,它提供了图形化配置工具,可以自动生成驱动代码,大大简化了复杂项目的开发。
- ADC (模数转换器):用于读取传感器(如温度、光线)的模拟信号。
- PWM (脉宽调制):用于控制电机速度、调节LED亮度等。
- I2C / SPI:常用的串行通信协议,用于连接OLED屏幕、EEPROM存储器、传感器等外设。
- 中断:让CPU在执行主程序的同时,能够及时响应外部事件,提高系统效率。
总结与学习资源
PIC单片机的C语言开发,核心在于理解其硬件架构(寄存器)和编译器扩展(#pragma config, __interrupt等),通过标准C语言逻辑来组织代码,通过操作寄存器来控制硬件,掌握GPIO、定时器、UART是入门的关键。
推荐学习资源:
-
官方资料:
- Microchip官网:所有数据手册、参考手册、应用笔记的最终来源。
- MPLAB X IDE和XC编译器文档:官方是最好的老师。
- Microchip Developer Portal:包含大量教程、示例代码和Harmony框架资料。
-
在线社区:
- Microchip官方论坛:可以提问,与官方工程师和开发者交流。
- Stack Overflow:搜索C语言和嵌入式开发相关的问题。
-
书籍:
- 《PIC Microcontroller and Embedded Systems: Using Assembly and C for PIC18》 by Muhammad Ali Mazidi。
- 《Programming 8-bit PIC Microcontrollers in C》 by Martin P. Bates。
希望这份指南能帮助你顺利开启PIC单片机的C语言编程之旅!祝你学习愉快!
