在工业自动化控制领域,PIC16F1947凭借其丰富的外设资源和稳定的性能表现,成为众多控制系统的首选MCU。作为一名有着30年工控经验的工程师,我在农业物联网、产线控制等多个项目中都深度应用了这款芯片。今天要分享的是一个已经稳定运行5年的多功能控制板方案,重点剖析其中的I/O输入检测、继电器输出控制以及直流电机正反转驱动这三个核心功能模块。
这个控制板之所以能在多个工业现场可靠运行,关键在于其采用了模块化设计思路:将8种常用工业控制功能集成在单块板卡上,既降低了系统复杂度,又保证了各功能间的电气隔离。其中直流电机控制部分采用RZ7899驱动芯片配合光耦隔离,实测可稳定驱动24V/5A的直流电机或电磁阀,特别适合灌溉控制、传送带驱动等应用场景。

上图所示的H桥驱动电路是控制直流电机正反转的核心,其设计要点包括:
关键提示:调试时务必先断开电机负载,用万用表验证ctl_xr和ctl_xf电平组合是否符合预期:
- 正转:ctl_xr=3.3V, ctl_xf=0V
- 反转:ctl_xr=0V, ctl_xf=3.3V
- 停止:两者均为0V
6路继电器模块采用经典的三极管驱动方案:
c复制// 继电器控制引脚定义
#define RELAY1 PORTFbits.RF0
#define RELAY2 PORTFbits.RF1
// ...其余继电器定义类似
void relay_control(uint8_t ch, uint8_t state) {
switch(ch) {
case 1: RELAY1 = state; break;
case 2: RELAY2 = state; break;
// ...其他通道
}
__delay_ms(10); // 确保继电器稳定吸合
}
实际布线时需注意:
2路无源开关量输入采用施密特触发电路设计:
code复制 +3.3V
|
[10K]
|
IN_PIN ----+----> PIC_GPIO
|
[100K]
|
GND
软件处理时需添加20ms防抖延时:
c复制uint8_t read_switch(uint8_t pin) {
uint8_t stable_cnt = 0;
while(stable_cnt < 5) {
if(GPIO_READ(pin)) stable_cnt++;
else stable_cnt = 0;
__delay_ms(4);
}
return 1;
}
为安全起见,电机控制采用状态机模式,禁止直接电平控制:
c复制typedef enum {
MOTOR_STOP,
MOTOR_FWD,
MOTOR_REV,
MOTOR_BRAKE
} motor_state;
void motor_control(uint8_t ch, motor_state cmd) {
static uint32_t last_change[2] = {0};
// 状态变更最小间隔保护
if(GetTick() - last_change[ch] < 500) return;
switch(ch) {
case 0: // 电机1
if(cmd == MOTOR_FWD) {
RF0 = 1; RF1 = 0;
} else if(cmd == MOTOR_REV) {
RF0 = 0; RF1 = 1;
} else {
RF0 = 0; RF1 = 0;
}
break;
// 电机2控制类似
}
last_change[ch] = GetTick();
}
通过05功能码控制电机状态的典型数据帧示例:
code复制从机地址 | 功能码 | 起始地址 | 输出值 | CRC校验
01 | 05 | 0008 | FF00 | 8D5A
对应代码实现:
c复制void modbus_process(uint8_t *frame) {
uint16_t crc = crc16(frame, 6);
if(memcmp(&frame[6], &crc, 2) != 0) return;
uint16_t addr = (frame[2] << 8) | frame[3];
uint16_t value = (frame[4] << 8) | frame[5];
if(frame[1] == 0x05) { // 写单线圈
switch(addr) {
case 8: // 电机1正转
motor_control(0, value ? MOTOR_FWD : MOTOR_STOP);
break;
case 9: // 电机1反转
motor_control(0, value ? MOTOR_REV : MOTOR_STOP);
break;
// 其他地址处理...
}
}
// 回送响应帧
memcpy(tx_buf, frame, 6);
crc = crc16(tx_buf, 6);
memcpy(&tx_buf[6], &crc, 2);
uart_send(tx_buf, 8);
}
在多个农业大棚控制项目中,我们遇到电机干扰导致MCU复位的问题,最终通过以下措施解决:
电源隔离:
信号处理:
c复制// 在IO中断服务函数中添加滤波
void __interrupt() ISR() {
static uint8_t filter_cnt = 0;
if(IO_IF) {
if(++filter_cnt > 3) {
// 确认为有效信号
filter_cnt = 0;
}
} else {
filter_cnt = 0;
}
}
PCB布局要点:
在控制交流负载时,继电器触点寿命从10万次降至不足2万次,改进措施包括:
RC吸收电路:
软件优化:
c复制void relay_operate(uint8_t ch, uint8_t state) {
// 先断开再闭合策略
if(state) {
RELAY_OFF(ch);
__delay_ms(15);
RELAY_ON(ch);
} else {
RELAY_OFF(ch);
}
}
c复制#pragma config WDTE = ON // 硬件看门狗使能
void main() {
while(1) {
asm("clrwdt"); // 喂狗
// 任务调度
if(tick_1ms) task_1ms();
if(tick_10ms) task_10ms();
if(tick_100ms) {
task_100ms();
asm("clrwdt"); // 关键任务二次喂狗
}
}
}
添加TPS3823电压监控芯片,配合软件处理:
c复制void power_check() {
if(RCONbits.POR || RCONbits.BOR) {
log_error("电源异常复位");
RCON = 0;
}
if(ADRESL < 0x40) { // 检测3.3V电源
emergency_shutdown();
}
}
增加电机电流检测:
c复制void motor_current_check() {
ADCON0 = 0x01; // 选择电流检测通道
__delay_us(10);
GO_nDONE = 1;
while(GO_nDONE);
if(ADRESH > 0x80) { // 超过阈值
motor_control(current_ch, MOTOR_STOP);
}
}
引入PID速度控制:
c复制typedef struct {
float Kp, Ki, Kd;
float integral, prev_error;
} PID_Controller;
void pid_update(PID_Controller *pid, float error) {
pid->integral += error;
float derivative = error - pid->prev_error;
float output = pid->Kp*error + pid->Ki*pid->integral + pid->Kd*derivative;
pid->prev_error = error;
pwm_set_duty(output);
}
无线升级功能:
这个控制系统在实际项目中展现出极佳的可靠性,在新疆某农业项目中连续运行3年无故障。其中电机控制部分的关键在于:硬件上做好隔离与保护,软件上实现状态机管理,通信层面采用标准MODBUS协议确保兼容性。对于需要更复杂控制的场合,可以引入PWM调速和电流反馈,这部分内容将在后续专题中详细展开。