PIC16系列通信接口(UART、I2C、SPI)
UART通信接口
UART简介
UART(Universal Asynchronous Receiver/Transmitter)是一种通用串行通信接口,用于实现两个设备之间的异步数据传输。PIC16系列单片机通常集成了一个或多个UART模块,可以用于与外部设备(如计算机、传感器、其他单片机等)进行数据交换。UART通信具有以下特点:
异步通信:发送和接收数据时不需要同步时钟信号。
全双工通信:可以同时发送和接收数据。
字符帧:数据以字符帧的形式传输,每个字符帧通常包含起始位、数据位、奇偶校验位(可选)和停止位。
波特率:通信速率,单位是bps(bits per second),可以通过配置寄存器来设置。
UART硬件结构
PIC16系列单片机的UART模块通常包括以下几个主要部件:
发送移位寄存器:用于将数据转换为串行格式并发送。
接收移位寄存器:用于将接收到的串行数据转换为并行格式。
发送缓冲器:用于存储待发送的数据。
接收缓冲器:用于存储接收到的数据。
波特率生成器:用于生成通信所需的时钟频率。
UART寄存器配置
在使用UART进行通信之前,需要对相关寄存器进行配置。以下是一些常见的UART寄存器及其功能:
TXSTA:发送状态和控制寄存器
TX9:选择9位或8位数据传输
TXEN:使能发送
SYNC:选择同步或异步模式
BRGH:选择波特率高或低速模式
TRMT:发送完成标志位
RCSTA:接收状态和控制寄存器
RX9:选择9位或8位数据接收
CREN:使能接收
SREN:使能同步模式下的接收
ADDEN:使能地址检测
BAUDCON:波特率控制寄存器
BRG16:选择16位或8位波特率生成器
WUE:唤醒功能使能
ABDEN:自动波特率检测使能
ABDOVF:自动波特率检测溢出标志位
BAUDCTL:高级波特率控制寄存器(某些PIC16系列单片机可能没有此寄存器)
SMP:采样位选择
ABDEN:自动波特率检测使能
BDDSMP:波特率检测采样位选择
TXREG:发送数据寄存器
RCREG:接收数据寄存器
UART波特率设置
波特率是UART通信中的重要参数,可以通过以下公式计算:
其中,F osc 是单片机的振荡器频率,SPBRG是波特率生成寄存器。例如,如果使用4MHz的振荡器频率,希望设置波特率为9600bps,可以通过以下步骤设置SPBRG:
将4MHz转换为Hz:4000000 Hz
代入公式计算SPBRG值:
UART初始化
在使用UART进行通信之前,需要进行初始化。以下是一个初始化UART的示例代码:
#include <xc.h>
// 定义波特率生成寄存器值
#define SPBRG_VAL 25
void UART_Init(void) {
// 设置波特率
SPBRG = SPBRG_VAL;
// 配置发送状态和控制寄存器
TXSTA = 0b00100100; // 高速模式,8位数据,使能发送
TXSTAbits.TX9 = 0; // 8位数据
TXSTAbits.BRGH = 1; // 高速模式
TXSTAbits.TXEN = 1; // 使能发送
// 配置接收状态和控制寄存器
RCSTA = 0b10010000; // 使能接收,8位数据
RCSTAbits.SPEN = 1; // 使能串行端口
RCSTAbits.CREN = 1; // 使能接收
// 配置波特率控制寄存器
BAUDCON = 0b00000000; // 使用8位波特率生成器
BAUDCONbits.BRG16 = 0; // 8位波特率生成器
}
void UART_WriteChar(char data) {
while (!TXIF); // 等待发送缓冲器为空
TXREG = data; // 发送数据
}
char UART_ReadChar(void) {
while (!RCIF); // 等待接收缓冲器有数据
return RCREG; // 读取数据
}
void UART_WriteString(char *str) {
while (*str) {
UART_WriteChar(*str++);
}
}
int main(void) {
// 初始化UART
UART_Init();
// 发送字符串
UART_WriteString("Hello, World!");
while (1) {
// 读取字符并回显
char receivedChar = UART_ReadChar();
UART_WriteChar(receivedChar);
}
return 0;
}
UART中断处理
UART可以配置中断,以便在数据发送或接收完成时触发特定的处理函数。以下是一个使用UART中断的示例代码:
#include <xc.h>
#include <interrupts.h>
// 定义波特率生成寄存器值
#define SPBRG_VAL 25
// 接收缓冲区
char receiveBuffer[100];
int bufferIndex = 0;
void __interrupt() UART_ISR(void) {
if (RCIF) { // 接收中断
char receivedChar = RCREG;
receiveBuffer[bufferIndex++] = receivedChar;
if (receivedChar == '\n' || bufferIndex >= 100) {
receiveBuffer[bufferIndex] = '\0'; // 终止字符串
bufferIndex = 0; // 重置缓冲区索引
// 处理接收到的数据
UART_WriteString(receiveBuffer);
}
RCIF = 0; // 清除接收中断标志
}
if (TXIF) { // 发送中断
if (bufferIndex < 100 && receiveBuffer[bufferIndex] != '\0') {
TXREG = receiveBuffer[bufferIndex++];
} else {
TXIF = 0; // 清除发送中断标志
}
}
}
void UART_Init(void) {
// 设置波特率
SPBRG = SPBRG_VAL;
// 配置发送状态和控制寄存器
TXSTA = 0b00100100; // 高速模式,8位数据,使能发送
TXSTAbits.TX9 = 0; // 8位数据
TXSTAbits.BRGH = 1; // 高速模式
TXSTAbits.TXEN = 1; // 使能发送
// 配置接收状态和控制寄存器
RCSTA = 0b10010000; // 使能接收,8位数据
RCSTAbits.SPEN = 1; // 使能串行端口
RCSTAbits.CREN = 1; // 使能接收
// 配置波特率控制寄存器
BAUDCON = 0b00000000; // 使用8位波特率生成器
BAUDCONbits.BRG16 = 0; // 8位波特率生成器
// 开启接收中断
PIE1bits.RCIE = 1; // 使能接收中断
PIE1bits.TXIE = 1; // 使能发送中断
INTCONbits.PEIE = 1; // 使能外围中断
INTCONbits.GIE = 1; // 使能全局中断
}
void UART_WriteChar(char data) {
while (!TXIF); // 等待发送缓冲器为空
TXREG = data; // 发送数据
}
char UART_ReadChar(void) {
while (!RCIF); // 等待接收缓冲器有数据
return RCREG; // 读取数据
}
void UART_WriteString(char *str) {
while (*str) {
UART_WriteChar(*str++);
}
}
int main(void) {
// 初始化UART
UART_Init();
while (1) {
// 主循环可以处理其他任务
}
return 0;
}
UART通信示例
以下是一个简单的UART通信示例,展示如何在PIC16单片机和PC之间传输数据。假设使用4MHz的振荡器频率,波特率为9600bps。
硬件连接:
PIC16的TX引脚连接到PC的RX引脚
PIC16的RX引脚连接到PC的TX引脚
使用合适的电平转换器(如MAX232)进行电平转换
软件配置:
使用 MPLAB X IDE 进行开发
配置项目为4MHz振荡器频率
编写上述初始化和中断处理代码
测试:
使用串口调试助手(如Putty、TeraTerm等)连接到PC的串口
在调试助手中发送字符串,例如“Hello, PIC16!”
观察PIC16单片机通过UART回显接收到的字符串
I2C通信接口
I2C简介
I2C(Inter-Integrated Circuit)是一种串行通信协议,用于在多个设备之间进行简单的双向数据传输。PIC16系列单片机通常集成了I2C模块,可以用于与外部传感器、存储器等设备进行通信。I2C通信具有以下特点:
两线通信:SCL(时钟线)和SDA(数据线)
主从模式:一个主设备可以与多个从设备通信
多主设备:多个主设备可以共享总线
地址识别:每个从设备都有一个唯一的7位或10位地址
I2C硬件结构
PIC16系列单片机的I2C模块通常包括以下几个主要部件:
SCL引脚:时钟线
SDA引脚:数据线
RC2/SDA和RC3/SCL:通常用于连接I2C总线
控制寄存器:用于配置I2C模块的工作模式和参数
I2C寄存器配置
在使用I2C进行通信之前,需要对相关寄存器进行配置。以下是一些常见的I2C寄存器及其功能:
SSPCON1:I2C控制寄存器1
CKP:时钟极性
SSPEN:使能I2C模块
SSPOV:溢出标志位
WCOL:写冲突标志位
SSP1CKP:时钟极性选择
SSP1EN:使能I2C模块
SSPCON2:I2C控制寄存器2
ACKEN:使能ACK生成
ACKSTAT:ACK状态
SEN:启动条件
RSEN:重复启动条件
PEN:停止条件
RCEN:使能接收
ACKDT:ACK数据位
SSPSTAT:I2C状态寄存器
BF:缓冲器满标志位
UA:未决地址标志位
RW:读写标志位
S:启动条件标志位
P:停止条件标志位
D:数据方向标志位
R:重复启动条件标志位
SSPADD:I2C地址寄存器
SSPBUF:I2C数据缓冲器
I2C初始化
在使用I2C进行通信之前,需要进行初始化。以下是一个初始化I2C的示例代码:
#include <xc.h>
#include <stdint.h>
// 定义I2C时钟频率
#define I2C_CLOCK 100000
// 计算I2C时钟分频值
uint8_t I2C_CalculateBRG(uint32_t Fosc, uint32_t I2C_CLOCK) {
return (Fosc / (4 * I2C_CLOCK)) - 1;
}
void I2C_Init(void) {
// 计算波特率生成值
SSPADD = I2C_CalculateBRG(4000000, I2C_CLOCK);
// 配置I2C控制寄存器
SSPCON1 = 0b00101000; // 主模式,I2C使能
SSPCON2 = 0b00000000; // 默认配置
// 配置I2C引脚
TRISC2 = 1; // SDA引脚设为输入
TRISC3 = 1; // SCL引脚设为输入
}
void I2C_Start(void) {
SEN = 1; // 发送启动条件
while (SEN); // 等待启动条件完成
}
void I2C_RepeatedStart(void) {
RSEN = 1; // 发送重复启动条件
while (RSEN); // 等待重复启动条件完成
}
void I2C_Stop(void) {
PEN = 1; // 发送停止条件
while (PEN); // 等待停止条件完成
}
void I2C_WriteByte(uint8_t data) {
SSPBUF = data; // 写入数据
while (!BF); // 等待数据传输完成
BF = 0; // 清除缓冲器标志位
}
uint8_t I2C_ReadByte(void) {
RCEN = 1; // 使能接收
while (!BF); // 等待数据接收完成
uint8_t data = SSPBUF; // 读取数据
BF = 0; // 清除缓冲器标志位
return data;
}
uint8_t I2C_ReadByteWithACK(uint8_t ack) {
RCEN = 1; // 使能接收
while (!BF); // 等待数据接收完成
uint8_t data = SSPBUF; // 读取数据
BF = 0; // 清除缓冲器标志位
ACKDT = ack; // 设置ACK数据位
ACKEN = 1; // 使能ACK生成
while (ACKEN); // 等待ACK生成完成
return data;
}
void I2C_WriteRegister(uint8_t slaveAddress, uint8_t registerAddress, uint8_t data) {
I2C_Start();
I2C_WriteByte(slaveAddress << 1); // 发送从设备地址和写操作位
I2C_WriteByte(registerAddress); // 发送寄存器地址
I2C_WriteByte(data); // 发送数据
I2C_Stop();
}
uint8_t I2C_ReadRegister(uint8_t slaveAddress, uint8_t registerAddress) {
I2C_Start();
I2C_WriteByte(slaveAddress << 1); // 发送从设备地址和写操作位
I2C_WriteByte(registerAddress); // 发送寄存器地址
I2C_RepeatedStart();
I2C_WriteByte((slaveAddress << 1) | 1); // 发送从设备地址和读操作位
uint8_t data = I2C_ReadByteWithACK(0); // 读取数据并发送NACK
I2C_Stop();
return data;
}
int main(void) {
// 初始化I2C
I2C_Init();
// 写入一个寄存器
I2C_WriteRegister(0x50, 0x01, 0xAA);
// 读取一个寄存器
uint8_t data = I2C_ReadRegister(0x50, 0x01);
UART_WriteString("Read data: ");
UART_WriteChar(data);
while (1) {
// 主循环可以处理其他任务
}
return 0;
}
I2C通信示例
以下是一个简单的I2C通信示例,展示如何在PIC16单片机和一个I2C从设备(如EEPROM)之间传输数据。假设使用4MHz的振荡器频率,I2C时钟频率为100kHz。
硬件连接:
PIC16的SCL引脚连接到EEPROM的SCL引脚
PIC16的SDA引脚连接到EEPROM的SDA引脚
使用合适的上拉电阻
软件配置:
使用 MPLAB X IDE 进行开发
配置项目为4MHz振荡器频率
编写上述初始化和通信代码
测试:
使用I2C调试工具(如I2C总线分析器)或示波器观察I2C通信
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2401_87715305/article/details/145273599
|