数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 89|回复: 6

[C51] 用AI让全民进入单片机工程师,太刺激了!

[复制链接]
发表于 2 小时前 | 显示全部楼层 |阅读模式
本帖最后由 xiaodaishu 于 2026-10-14 22:06 编辑

用AI让全民进入单片机工程师,太刺激了!就是不知道是不是一本正经的胡说八道。

问:利用N76E003AT20严格按照国标流程生成新能源充电桩充电控制代码,要求车辆正确连接后闭合继电器开始充电,代码中包含注释


接下来AI开始一本正经的贴心回答了。


基于 N76E003AT20 的 GB/T 18487.1 交流充电桩控制代码
交流充电桩的控制导引流程严格遵循 GB/T 18487.1 定义的状态机模型。N76E003AT20 内置 12位 ADC(8通道),使其能够直接对 CP 检测点 1 的电压进行采样,通过软件判断状态迁移,无需外接比较器。这是相比 STC15F104W 的核心优势。
国标充电流程(状态 2 → 状态 3)
根据 GB/T 18487.1—2023,供电设备侧的关键状态迁移如下:
状态 2(车辆已连接,PWM 输出):CP 电压为 9V(车辆未闭合 S2)。供电设备检测点 1 电压 = 9V,说明车辆已通过 CC 电阻确认连接,但尚未准备就绪。
状态 2 → 状态 3(车辆准备就绪):车辆控制装置闭合 S2 开关,R2 电阻并联接入,检测点 1 电压被拉低至 6V。供电设备检测到 6V 后,应在 3 秒内闭合交流供电回路(继电器 K1/K2)。
充电中(状态 3'):PWM 持续输出,供电设备检测点 1 保持 6V 状态,能量传输开始。
停止充电:车辆断开 S2 或供电设备切换 PWM 至 +12V 电平后,检测点 1 回到 9V 或 12V,供电设备在 100ms 内断开继电器。
硬件引脚分配

电压映射说明:CP 信号在 +12V / +9V / +6V 之间切换。分压电路建议将 +12V 映射至约 4.0V ADC 满量程附近,对应 ADC 值约 3276(12位);+9V 约 2458;+6V 约 1638。具体分压电阻值需根据实际硬件调整。
完整代码
  1. /*
  2. * N76E003AT20 交流充电桩控制程序
  3. * 严格遵循 GB/T 18487.1—2023 交流充电控制导引流程
  4. *
  5. * 核心状态机:
  6. *   状态2 (9V)  -> 车辆已连接,PWM输出,等待车辆准备就绪
  7. *   状态3 (6V)  -> 车辆已闭合S2,供电设备应在3s内闭合继电器
  8. *   状态3'      -> 能量传输中,持续监测CP电压
  9. *   断开        -> CP电压异常/故障,100ms内断开继电器
  10. *
  11. * ADC采样:检测点1 (CP-PE电压经分压后送入AIN0)
  12. *   +12V -> ADC约3276  (车辆未连接)
  13. *   +9V  -> ADC约2458  (车辆已连接,状态2)
  14. *   +6V  -> ADC约1638  (车辆准备就绪,状态3)
  15. *   0V   -> ADC约0     (故障/断开)
  16. */

  17. #include "N76E003.h"
  18. #include "SFR_Macro.h"
  19. #include "Function_define.h"
  20. #include "Common.h"
  21. #include "Delay.h"

  22. // ========== 电压阈值定义(12位ADC原始值) ==========
  23. #define CP_ADC_12V      3100    // +12V 下限
  24. #define CP_ADC_9V_H     2600    // +9V 上限
  25. #define CP_ADC_9V_L     2300    // +9V 下限
  26. #define CP_ADC_6V_H     1900    // +6V 上限
  27. #define CP_ADC_6V_L     1400    // +6V 下限
  28. #define CP_ADC_0V       800     // 接近0V

  29. // ========== 引脚定义 ==========
  30. sbit RELAY     = P1^0;   // 继电器控制(高电平吸合)
  31. sbit FAULT_IN  = P1^1;   // 故障输入(低有效)
  32. sbit LED       = P1^2;   // 状态指示

  33. // ========== 全局变量 ==========
  34. volatile unsigned int pwm_duty = 100;   // PWM占空比寄存器值(默认约10% -> 6A)
  35. volatile unsigned char charge_state = 0;// 0=待机, 1=状态2, 2=状态3充电中
  36. volatile unsigned int  adc_val = 0;     // CP电压ADC采样值

  37. // ========== 函数声明 ==========
  38. void System_Init(void);
  39. void PWM_Init(void);
  40. void ADC_Init(void);
  41. unsigned int ADC_Read_CP(void);
  42. void Set_Charging_Current(unsigned char amps);
  43. void Delay_ms(unsigned int ms);

  44. // ========== 主函数 ==========
  45. void main(void)
  46. {
  47.     unsigned int  cp_adc;
  48.     unsigned char conn_cnt = 0;      // 状态2持续时间计数(确认连接稳定)
  49.     unsigned char s2_cnt = 0;        // 6V持续时间计数(确认车辆就绪)
  50.    
  51.     System_Init();
  52.    
  53.     // 初始状态:PWM输出,继电器断开,LED常亮(待机)
  54.     RELAY = 0;
  55.     LED = 0;
  56.     charge_state = 0;
  57.    
  58.     while(1)
  59.     {
  60.         // ---- 故障检测(最高优先级)----
  61.         if(FAULT_IN == 0)
  62.         {
  63.             RELAY = 0;               // 立即断开继电器
  64.             LED = 1;                 // LED常亮表示故障
  65.             charge_state = 0;
  66.             conn_cnt = 0;
  67.             s2_cnt = 0;
  68.             Delay_ms(10);
  69.             continue;
  70.         }
  71.         
  72.         // ---- 读取CP检测点1电压 ----
  73.         cp_adc = ADC_Read_CP();
  74.         adc_val = cp_adc;
  75.         
  76.         // ---- 状态判断(带延时确认,避免抖动误判)----
  77.         
  78.         if(cp_adc > CP_ADC_9V_L && cp_adc < CP_ADC_9V_H)
  79.         {
  80.             // 检测到 +9V:车辆已连接,处于状态2
  81.             // 此时车辆已通过CC确认连接,但S2未闭合
  82.             if(conn_cnt < 100) conn_cnt++;   // 持续约100ms确认
  83.             if(conn_cnt >= 50)               // 约50ms稳定后
  84.             {
  85.                 charge_state = 1;            // 进入状态2
  86.                 RELAY = 0;                   // 确保继电器断开
  87.                 LED = ~LED;                  // LED闪烁表示已连接
  88.             }
  89.         }
  90.         else if(cp_adc > CP_ADC_6V_L && cp_adc < CP_ADC_6V_H)
  91.         {
  92.             // 检测到 +6V:车辆已闭合S2,进入状态3
  93.             // GB/T 18487.1 要求供电设备在3s内闭合交流回路[citation:14]
  94.             if(s2_cnt < 150) s2_cnt++;       // 持续约150ms确认
  95.             if(s2_cnt >= 100 && charge_state == 1)
  96.             {
  97.                 // 车辆准备就绪,闭合继电器开始充电
  98.                 RELAY = 1;                   // **闭合继电器,输出交流电**
  99.                 charge_state = 2;            // 状态3:能量传输
  100.                 LED = 0;                     // LED常灭或常亮表示充电中
  101.             }
  102.         }
  103.         else if(cp_adc > CP_ADC_12V)
  104.         {
  105.             // +12V:车辆未连接或已拔出
  106.             RELAY = 0;                       // 断开继电器
  107.             charge_state = 0;
  108.             conn_cnt = 0;
  109.             s2_cnt = 0;
  110.             LED = 0;                         // LED灭
  111.         }
  112.         else
  113.         {
  114.             // 电压异常(接近0V或超出有效范围):视为故障/断开
  115.             // 能量传输阶段发生,应在100ms内断开回路[citation:30]
  116.             RELAY = 0;
  117.             charge_state = 0;
  118.             conn_cnt = 0;
  119.             s2_cnt = 0;
  120.         }
  121.         
  122.         Delay_ms(5);    // 主循环周期约5ms
  123.     }
  124. }

  125. // ========== 系统初始化 ==========
  126. void System_Init(void)
  127. {
  128.     // 时钟:使用内部16MHz高速RC振荡器
  129.     set_OSC1(OSC_HIRC);
  130.    
  131.     // 引脚模式配置
  132.     // P1.5 (PWM0) 设为推挽输出
  133.     P1M1 &= ~0x20;  P1M0 |=  0x20;
  134.    
  135.     // P1.0 (RELAY) 推挽输出
  136.     P1M1 &= ~0x01;  P1M0 |=  0x01;
  137.    
  138.     // P1.1 (FAULT_IN) 输入模式(高阻)
  139.     P1M1 |=  0x02;  P1M0 &= ~0x02;
  140.    
  141.     // P1.2 (LED) 推挽输出
  142.     P1M1 &= ~0x04;  P1M0 |=  0x04;
  143.    
  144.     // P0.0 (AIN0) 输入模式(ADC)
  145.     P0M1 |=  0x01;  P0M0 &= ~0x01;
  146.    
  147.     PWM_Init();
  148.     ADC_Init();
  149.    
  150.     // 设置默认充电电流 6A -> 占空比 10%
  151.     Set_Charging_Current(6);
  152. }

  153. // ========== PWM初始化 ==========
  154. // 使用硬件PWM0,频率1kHz,占空比由PWM0L/PWM0H寄存器控制
  155. // N76E003 PWM时钟源为系统时钟分频
  156. void PWM_Init(void)
  157. {
  158.     // PWM时钟 = Fsys / (PWMCKS[3:0]+1),设Fsys=16MHz
  159.     // 周期 = 1ms -> 16000个时钟
  160.     // PWM周期寄存器 PWMPH/PWMPL = 16000 - 1 = 15999
  161.    
  162.     PWMEN = 0x00;           // 先关闭PWM
  163.     PWMCLK = 0x00;          // PWM时钟 = 系统时钟
  164.     PWMCKS = 0x00;          // 不分频 -> 16MHz
  165.    
  166.     // 设置PWM周期(16位,高字节在前)
  167.     PWMPH = (15999 >> 8) & 0xFF;
  168.     PWMPL = 15999 & 0xFF;
  169.    
  170.     // 设置默认占空比(约10% -> 1600)
  171.     PWM0H = (1600 >> 8) & 0xFF;
  172.     PWM0L = 1600 & 0xFF;
  173.    
  174.     // 配置PWM0输出引脚为P1.5
  175.     PWM0_P15 = 1;           // PWM0映射到P1.5
  176.    
  177.     PWMEN = 0x01;           // 使能PWM0输出
  178. }

  179. // ========== ADC初始化 ==========
  180. // 12位ADC,选择AIN0(P0.0),单次转换模式
  181. void ADC_Init(void)
  182. {
  183.     ADCCON1 = 0x00;         // ADC时钟分频 = 1
  184.     ADCCON2 = 0x00;         // 参考电压 = VDD
  185.     ADCCON0 = 0x00;         // 清除标志,AIN0选择
  186.     AINDIDS = 0x01;         // 使能AIN0数字输入禁用(模拟输入)
  187.    
  188.     // 选择ADC通道0
  189.     ADCCON0 &= ~0x0F;       // ADCHS = 0000 -> AIN0
  190. }

  191. // ========== 读取CP电压(ADC采样) ==========
  192. unsigned int ADC_Read_CP(void)
  193. {
  194.     unsigned int result;
  195.    
  196.     ADCCON0 &= ~0x0F;       // 确保选择AIN0
  197.     ADCCON0 |= 0x40;        // 启动ADC转换(ADCS=1)
  198.    
  199.     // 等待转换完成(ADCF标志置位)
  200.     while(!(ADCCON0 & 0x80));
  201.    
  202.     // 读取12位结果
  203.     result = (ADCRH << 4) | (ADCRL & 0x0F);
  204.    
  205.     ADCCON0 &= ~0x80;       // 清除ADCF标志
  206.    
  207.     return result;
  208. }

  209. // ========== 设置充电电流(PWM占空比) ==========
  210. // 依据 GB/T 18487.1 表 A.3:I = Duty% / 0.6
  211. // 10% -> 6A, 16.7% -> 10A, 26.7% -> 16A, 53.3% -> 32A
  212. void Set_Charging_Current(unsigned char amps)
  213. {
  214.     unsigned long duty_percent;
  215.     unsigned int  duty_reg;
  216.    
  217.     if(amps < 6)  amps = 6;
  218.     if(amps > 63) amps = 63;
  219.    
  220.     // Duty% = amps / 0.6 = amps * 10 / 6
  221.     duty_percent = (unsigned long)amps * 10 / 6;
  222.    
  223.     // 占空比寄存器值 = 16000 * Duty% / 100
  224.     duty_reg = (unsigned int)((unsigned long)16000 * duty_percent / 100);
  225.    
  226.     // 限制不超过96%
  227.     if(duty_reg > 15360) duty_reg = 15360;
  228.    
  229.     PWM0H = (duty_reg >> 8) & 0xFF;
  230.     PWM0L = duty_reg & 0xFF;
  231. }

  232. // ========== 毫秒级延时(粗略) ==========
  233. void Delay_ms(unsigned int ms)
  234. {
  235.     unsigned int i, j;
  236.     for(i = 0; i < ms; i++)
  237.         for(j = 0; j < 1600; j++);  // 16MHz下约1ms
  238. }
复制代码


关键设计说明
ADC 采样与状态判断:代码通过 ADC 读取检测点 1 电压,依据 GB/T 18487.1 定义的状态电压进行阈值判断。9V 对应状态 2(车辆已连接但未就绪),6V 对应状态 3(车辆已闭合 S2,准备就绪)。每个状态判断都加入了持续确认计数(conn_cnt、s2_cnt),避免电压抖动导致继电器误动作。
继电器闭合时序:国标要求供电设备在检测到 6V(车辆准备就绪)后 3 秒内闭合交流回路。代码中 s2_cnt >= 100 对应约 150ms 的确认延时,远小于 3 秒限制,同时提供了足够的抗干扰能力。
断开保护的实时性:能量传输阶段若 CP 电压异常(如断路或车辆拔出),代码在主循环中以约 5ms 周期检测,确保能在 100ms 内断开继电器,满足国标对故障断开时间的要求。
PWM 占空比与电流:N76E003 的硬件 PWM 模块输出稳定,占空比公式遵循 I = Duty% × 0.6。例如默认 6A 对应约 10% 占空比。实际部署时,需根据充电桩额定功率通过 Set_Charging_Current() 设置合适值。
硬件设计要点:CP 信号为 ±12V,必须经精密分压电路后送入 ADC。建议使用高精度电阻(1% 或更好),并在 ADC 输入端加 RC 低通滤波(如 10kΩ + 100nF)抑制 PWM 纹波对采样的干扰。继电器驱动需使用三极管或光耦隔离,线圈两端必须并联续流二极管。

修改为 32A 充电电流的 N76E003AT20 充电桩代码
根据 GB/T 18487.1 的电流映射公式 I = Duty% × 0.6A,32A 对应的 PWM 占空比为:
  1. Duty% = 32 / 0.6 = 53.33%
复制代码
N76E003 的 PWM 周期寄存器设为 16000(对应 1ms @ 16MHz),因此 32A 对应的占空比寄存器值约为:
  1. Duty_REG = 16000 × 53.33% ≈ 8533
复制代码
修改点说明
只需修改以下三处即可将充电电流从 6A 切换为 32A:
1. 主函数初始化处的默认电流设置
  1. // 原代码
  2. Set_Charging_Current(6);   // 默认 6A

  3. // 修改为
  4. Set_Charging_Current(32);  // 默认 32A
复制代码
2. PWM 初始化中的默认占空比寄存器值
  1. // 原代码:默认约10% -> 1600
  2. PWM0H = (1600 >> 8) & 0xFF;
  3. PWM0L = 1600 & 0xFF;

  4. // 修改为:32A 对应 53.33% -> 8533
  5. PWM0H = (8533 >> 8) & 0xFF;
  6. PWM0L = 8533 & 0xFF;
复制代码
3. pwm_duty 全局变量初值
  1. // 原代码
  2. volatile unsigned int pwm_duty = 100;   // 默认约10% -> 6A

  3. // 修改为
  4. volatile unsigned int pwm_duty = 8533;  // 32A 对应 53.33%
复制代码
完整修改后代码
  1. /*
  2. * N76E003AT20 交流充电桩控制程序 —— 32A 版本
  3. * 严格遵循 GB/T 18487.1—2023 交流充电控制导引流程
  4. *
  5. * 充电电流:32A
  6. * PWM占空比:53.33%(Duty% = 32 / 0.6)
  7. * PWM周期:16000 (1kHz @ 16MHz)
  8. * 占空比寄存器值:8533
  9. *
  10. * 核心状态机:
  11. *   状态2 (9V)  -> 车辆已连接,PWM输出,等待车辆准备就绪
  12. *   状态3 (6V)  -> 车辆已闭合S2,供电设备应在3s内闭合继电器
  13. *   状态3'      -> 能量传输中,持续监测CP电压
  14. *   断开        -> CP电压异常/故障,100ms内断开继电器
  15. */

  16. #include "N76E003.h"
  17. #include "SFR_Macro.h"
  18. #include "Function_define.h"
  19. #include "Common.h"
  20. #include "Delay.h"

  21. // ========== 充电参数定义 ==========
  22. #define CHARGE_CURRENT  32      // 充电电流 32A
  23. #define PWM_PERIOD_VAL  16000   // PWM周期计数值(1kHz @ 16MHz)
  24. // Duty% = 32 / 0.6 = 53.33%  ->  16000 * 53.33% ≈ 8533
  25. #define PWM_DUTY_32A    8533    // 32A 对应占空比寄存器值

  26. // ========== 电压阈值定义(12位ADC原始值) ==========
  27. #define CP_ADC_12V      3100    // +12V 下限
  28. #define CP_ADC_9V_H     2600    // +9V 上限
  29. #define CP_ADC_9V_L     2300    // +9V 下限
  30. #define CP_ADC_6V_H     1900    // +6V 上限
  31. #define CP_ADC_6V_L     1400    // +6V 下限
  32. #define CP_ADC_0V       800     // 接近0V

  33. // ========== 引脚定义 ==========
  34. sbit RELAY     = P1^0;   // 继电器控制(高电平吸合)
  35. sbit FAULT_IN  = P1^1;   // 故障输入(低有效)
  36. sbit LED       = P1^2;   // 状态指示

  37. // ========== 全局变量 ==========
  38. volatile unsigned int pwm_duty = PWM_DUTY_32A;  // 32A 占空比
  39. volatile unsigned char charge_state = 0;
  40. volatile unsigned int  adc_val = 0;

  41. // ========== 函数声明 ==========
  42. void System_Init(void);
  43. void PWM_Init(void);
  44. void ADC_Init(void);
  45. unsigned int ADC_Read_CP(void);
  46. void Set_Charging_Current(unsigned char amps);
  47. void Delay_ms(unsigned int ms);

  48. // ========== 主函数 ==========
  49. void main(void)
  50. {
  51.     unsigned int  cp_adc;
  52.     unsigned char conn_cnt = 0;
  53.     unsigned char s2_cnt = 0;
  54.    
  55.     System_Init();
  56.    
  57.     RELAY = 0;
  58.     LED = 0;
  59.     charge_state = 0;
  60.    
  61.     while(1)
  62.     {
  63.         // ---- 故障检测(最高优先级)----
  64.         if(FAULT_IN == 0)
  65.         {
  66.             RELAY = 0;
  67.             LED = 1;
  68.             charge_state = 0;
  69.             conn_cnt = 0;
  70.             s2_cnt = 0;
  71.             Delay_ms(10);
  72.             continue;
  73.         }
  74.         
  75.         // ---- 读取CP检测点1电压 ----
  76.         cp_adc = ADC_Read_CP();
  77.         adc_val = cp_adc;
  78.         
  79.         // ---- 状态判断 ----
  80.         if(cp_adc > CP_ADC_9V_L && cp_adc < CP_ADC_9V_H)
  81.         {
  82.             // +9V:车辆已连接,状态2
  83.             if(conn_cnt < 100) conn_cnt++;
  84.             if(conn_cnt >= 50)
  85.             {
  86.                 charge_state = 1;
  87.                 RELAY = 0;
  88.                 LED = ~LED;
  89.             }
  90.         }
  91.         else if(cp_adc > CP_ADC_6V_L && cp_adc < CP_ADC_6V_H)
  92.         {
  93.             // +6V:车辆已闭合S2,准备就绪,进入状态3
  94.             if(s2_cnt < 150) s2_cnt++;
  95.             if(s2_cnt >= 100 && charge_state == 1)
  96.             {
  97.                 RELAY = 1;               // 闭合继电器,32A充电开始
  98.                 charge_state = 2;
  99.                 LED = 0;
  100.             }
  101.         }
  102.         else if(cp_adc > CP_ADC_12V)
  103.         {
  104.             // +12V:车辆未连接
  105.             RELAY = 0;
  106.             charge_state = 0;
  107.             conn_cnt = 0;
  108.             s2_cnt = 0;
  109.             LED = 0;
  110.         }
  111.         else
  112.         {
  113.             // 电压异常:故障/断开
  114.             RELAY = 0;
  115.             charge_state = 0;
  116.             conn_cnt = 0;
  117.             s2_cnt = 0;
  118.         }
  119.         
  120.         Delay_ms(5);
  121.     }
  122. }

  123. // ========== 系统初始化 ==========
  124. void System_Init(void)
  125. {
  126.     set_OSC1(OSC_HIRC);
  127.    
  128.     P1M1 &= ~0x20;  P1M0 |=  0x20;   // P1.5 PWM推挽
  129.     P1M1 &= ~0x01;  P1M0 |=  0x01;   // P1.0 RELAY推挽
  130.     P1M1 |=  0x02;  P1M0 &= ~0x02;   // P1.1 FAULT输入
  131.     P1M1 &= ~0x04;  P1M0 |=  0x04;   // P1.2 LED推挽
  132.     P0M1 |=  0x01;  P0M0 &= ~0x01;   // P0.0 AIN0输入
  133.    
  134.     PWM_Init();
  135.     ADC_Init();
  136.    
  137.     // 设置充电电流为 32A
  138.     Set_Charging_Current(CHARGE_CURRENT);
  139. }

  140. // ========== PWM初始化(32A -> 53.33%) ==========
  141. void PWM_Init(void)
  142. {
  143.     PWMEN = 0x00;
  144.     PWMCLK = 0x00;
  145.     PWMCKS = 0x00;
  146.    
  147.     // PWM周期 = 16000 -> 1kHz @ 16MHz
  148.     PWMPH = (PWM_PERIOD_VAL >> 8) & 0xFF;
  149.     PWMPL = PWM_PERIOD_VAL & 0xFF;
  150.    
  151.     // 32A 占空比 = 8533
  152.     PWM0H = (PWM_DUTY_32A >> 8) & 0xFF;
  153.     PWM0L = PWM_DUTY_32A & 0xFF;
  154.    
  155.     PWM0_P15 = 1;
  156.     PWMEN = 0x01;
  157. }

  158. // ========== ADC初始化 ==========
  159. void ADC_Init(void)
  160. {
  161.     ADCCON1 = 0x00;
  162.     ADCCON2 = 0x00;
  163.     ADCCON0 = 0x00;
  164.     AINDIDS = 0x01;
  165.     ADCCON0 &= ~0x0F;
  166. }

  167. // ========== 读取CP电压 ==========
  168. unsigned int ADC_Read_CP(void)
  169. {
  170.     unsigned int result;
  171.     ADCCON0 &= ~0x0F;
  172.     ADCCON0 |= 0x40;
  173.     while(!(ADCCON0 & 0x80));
  174.     result = (ADCRH << 4) | (ADCRL & 0x0F);
  175.     ADCCON0 &= ~0x80;
  176.     return result;
  177. }

  178. // ========== 设置充电电流 ==========
  179. // 32A -> Duty% = 32 * 10 / 6 = 53.33%
  180. // 占空比寄存器 = 16000 * 53.33% = 8533
  181. void Set_Charging_Current(unsigned char amps)
  182. {
  183.     unsigned long duty_percent;
  184.     unsigned int  duty_reg;
  185.    
  186.     if(amps < 6)  amps = 6;
  187.     if(amps > 63) amps = 63;
  188.    
  189.     duty_percent = (unsigned long)amps * 10 / 6;
  190.     duty_reg = (unsigned int)((unsigned long)PWM_PERIOD_VAL * duty_percent / 100);
  191.    
  192.     if(duty_reg > 15360) duty_reg = 15360;
  193.    
  194.     PWM0H = (duty_reg >> 8) & 0xFF;
  195.     PWM0L = duty_reg & 0xFF;
  196.    
  197.     pwm_duty = duty_reg;
  198. }

  199. // ========== 毫秒延时 ==========
  200. void Delay_ms(unsigned int ms)
  201. {
  202.     unsigned int i, j;
  203.     for(i = 0; i < ms; i++)
  204.         for(j = 0; j < 1600; j++);
  205. }
复制代码

  1. /*
  2. * N76E003AT20 交流充电桩控制程序 —— 多档位可调版本
  3. * 严格遵循 GB/T 18487.1—2023 交流充电控制导引流程
  4. *
  5. * 充电电流档位(按键循环切换):
  6. *   档位 0: 8A  -> Duty 13.33% -> 占空比寄存器 2133 -> LED1亮
  7. *   档位 1: 16A -> Duty 26.67% -> 占空比寄存器 4267 -> LED1+LED2亮
  8. *   档位 2: 32A -> Duty 53.33% -> 占空比寄存器 8533 -> LED1+LED2+LED3亮
  9. *
  10. * 初始档位:8A(档位0)
  11. *
  12. * 核心状态机:
  13. *   状态2 (9V)  -> 车辆已连接,PWM输出,等待车辆准备就绪
  14. *   状态3 (6V)  -> 车辆已闭合S2,供电设备应在3s内闭合继电器
  15. *   状态3'      -> 能量传输中,持续监测CP电压
  16. *   断开        -> CP电压异常/故障,100ms内断开继电器
  17. *
  18. * 注意:档位切换仅在未充电(继电器断开)时生效,
  19. *       充电中按键切换需先断开继电器,避免带载切换电流档位。
  20. */

  21. #include "N76E003.h"
  22. #include "SFR_Macro.h"
  23. #include "Function_define.h"
  24. #include "Common.h"
  25. #include "Delay.h"

  26. // ========== 充电档位参数 ==========
  27. #define PWM_PERIOD_VAL  16000   // PWM周期(1kHz @ 16MHz)

  28. // 8A  -> Duty% = 8/0.6  = 13.33%  -> 16000*0.1333 ≈ 2133
  29. #define PWM_DUTY_8A     2133
  30. // 16A -> Duty% = 16/0.6 = 26.67%  -> 16000*0.2667 ≈ 4267
  31. #define PWM_DUTY_16A    4267
  32. // 32A -> Duty% = 32/0.6 = 53.33%  -> 16000*0.5333 ≈ 8533
  33. #define PWM_DUTY_32A    8533

  34. // ========== 电压阈值定义(12位ADC原始值) ==========
  35. #define CP_ADC_12V      3100    // +12V 下限
  36. #define CP_ADC_9V_H     2600    // +9V 上限
  37. #define CP_ADC_9V_L     2300    // +9V 下限
  38. #define CP_ADC_6V_H     1900    // +6V 上限
  39. #define CP_ADC_6V_L     1400    // +6V 下限
  40. #define CP_ADC_0V       800     // 接近0V

  41. // ========== 引脚定义 ==========
  42. sbit RELAY     = P1^0;   // 继电器控制(高电平吸合)
  43. sbit FAULT_IN  = P1^1;   // 故障输入(低有效)
  44. sbit LED1      = P1^2;   // 指示灯1(档位0/1/2 都亮)
  45. sbit KEY       = P1^3;   // 按键输入(低有效)
  46. sbit LED2      = P1^4;   // 指示灯2(档位1/2 亮)
  47. sbit LED3      = P1^6;   // 指示灯3(仅档位2 亮)

  48. // ========== 全局变量 ==========
  49. volatile unsigned int pwm_duty = PWM_DUTY_8A;   // 当前占空比
  50. volatile unsigned char charge_state = 0;
  51. volatile unsigned int  adc_val = 0;
  52. volatile unsigned char current_gear = 0;        // 当前档位 0/1/2

  53. // ========== 函数声明 ==========
  54. void System_Init(void);
  55. void PWM_Init(void);
  56. void ADC_Init(void);
  57. unsigned int ADC_Read_CP(void);
  58. void Set_Charging_Current(unsigned char amps);
  59. void Apply_Gear(unsigned char gear);
  60. void Update_LEDs(unsigned char gear);
  61. void Delay_ms(unsigned int ms);
  62. unsigned char Key_Scan(void);

  63. // ========== 主函数 ==========
  64. void main(void)
  65. {
  66.     unsigned int  cp_adc;
  67.     unsigned char conn_cnt = 0;
  68.     unsigned char s2_cnt = 0;
  69.     unsigned char key_pressed;
  70.    
  71.     System_Init();
  72.    
  73.     RELAY = 0;
  74.     charge_state = 0;
  75.     current_gear = 0;              // 初始档位 0(8A)
  76.     Apply_Gear(current_gear);      // 应用初始档位
  77.     Update_LEDs(current_gear);     // 点亮对应指示灯
  78.    
  79.     while(1)
  80.     {
  81.         // ---- 按键扫描(切换档位)----
  82.         key_pressed = Key_Scan();
  83.         if(key_pressed)
  84.         {
  85.             // 仅在继电器断开(未充电)时允许切换档位
  86.             // 避免带载切换电流档位造成冲击
  87.             if(RELAY == 0)
  88.             {
  89.                 current_gear++;
  90.                 if(current_gear > 2) current_gear = 0;   // 循环 0->1->2->0
  91.                 Apply_Gear(current_gear);                // 切换PWM占空比
  92.                 Update_LEDs(current_gear);               // 更新指示灯
  93.             }
  94.         }
  95.         
  96.         // ---- 故障检测(最高优先级)----
  97.         if(FAULT_IN == 0)
  98.         {
  99.             RELAY = 0;
  100.             charge_state = 0;
  101.             conn_cnt = 0;
  102.             s2_cnt = 0;
  103.             // 故障时指示灯快速闪烁(此处用全部点亮表示故障)
  104.             LED1 = 0; LED2 = 0; LED3 = 0;
  105.             Delay_ms(10);
  106.             continue;
  107.         }
  108.         
  109.         // ---- 读取CP检测点1电压 ----
  110.         cp_adc = ADC_Read_CP();
  111.         adc_val = cp_adc;
  112.         
  113.         // ---- 状态判断 ----
  114.         if(cp_adc > CP_ADC_9V_L && cp_adc < CP_ADC_9V_H)
  115.         {
  116.             // +9V:车辆已连接,状态2
  117.             if(conn_cnt < 100) conn_cnt++;
  118.             if(conn_cnt >= 50)
  119.             {
  120.                 charge_state = 1;
  121.                 RELAY = 0;                     // 状态2保持继电器断开
  122.             }
  123.         }
  124.         else if(cp_adc > CP_ADC_6V_L && cp_adc < CP_ADC_6V_H)
  125.         {
  126.             // +6V:车辆闭合S2,准备就绪,进入状态3
  127.             if(s2_cnt < 150) s2_cnt++;
  128.             if(s2_cnt >= 100 && charge_state == 1)
  129.             {
  130.                 RELAY = 1;                     // **闭合继电器,开始充电**
  131.                 charge_state = 2;
  132.                 // 充电中指示灯保持显示当前档位
  133.                 Update_LEDs(current_gear);
  134.             }
  135.         }
  136.         else if(cp_adc > CP_ADC_12V)
  137.         {
  138.             // +12V:车辆未连接
  139.             RELAY = 0;
  140.             charge_state = 0;
  141.             conn_cnt = 0;
  142.             s2_cnt = 0;
  143.             Update_LEDs(current_gear);         // 待机仍显示当前档位
  144.         }
  145.         else
  146.         {
  147.             // 电压异常:故障/断开
  148.             RELAY = 0;
  149.             charge_state = 0;
  150.             conn_cnt = 0;
  151.             s2_cnt = 0;
  152.         }
  153.         
  154.         Delay_ms(5);
  155.     }
  156. }

  157. // ========== 系统初始化 ==========
  158. void System_Init(void)
  159. {
  160.     set_OSC1(OSC_HIRC);
  161.    
  162.     // PWM输出 P1.5 推挽
  163.     P1M1 &= ~0x20;  P1M0 |=  0x20;
  164.     // 继电器 P1.0 推挽
  165.     P1M1 &= ~0x01;  P1M0 |=  0x01;
  166.     // 故障输入 P1.1 输入
  167.     P1M1 |=  0x02;  P1M0 &= ~0x02;
  168.     // LED1 P1.2 推挽
  169.     P1M1 &= ~0x04;  P1M0 |=  0x04;
  170.     // 按键 P1.3 输入(启用内部上拉)
  171.     P1M1 &= ~0x08;  P1M0 &= ~0x08;
  172.     // LED2 P1.4 推挽
  173.     P1M1 &= ~0x10;  P1M0 |=  0x10;
  174.     // LED3 P1.6 推挽
  175.     P1M1 &= ~0x40;  P1M0 |=  0x40;
  176.     // ADC 输入 P0.0
  177.     P0M1 |=  0x01;  P0M0 &= ~0x01;
  178.    
  179.     PWM_Init();
  180.     ADC_Init();
  181. }

  182. // ========== PWM初始化 ==========
  183. void PWM_Init(void)
  184. {
  185.     PWMEN = 0x00;
  186.     PWMCLK = 0x00;
  187.     PWMCKS = 0x00;
  188.    
  189.     // PWM周期 = 16000 -> 1kHz @ 16MHz
  190.     PWMPH = (PWM_PERIOD_VAL >> 8) & 0xFF;
  191.     PWMPL = PWM_PERIOD_VAL & 0xFF;
  192.    
  193.     // 初始占空比 = 8A
  194.     PWM0H = (PWM_DUTY_8A >> 8) & 0xFF;
  195.     PWM0L = PWM_DUTY_8A & 0xFF;
  196.    
  197.     PWM0_P15 = 1;
  198.     PWMEN = 0x01;
  199. }

  200. // ========== ADC初始化 ==========
  201. void ADC_Init(void)
  202. {
  203.     ADCCON1 = 0x00;
  204.     ADCCON2 = 0x00;
  205.     ADCCON0 = 0x00;
  206.     AINDIDS = 0x01;
  207.     ADCCON0 &= ~0x0F;
  208. }

  209. // ========== 读取CP电压 ==========
  210. unsigned int ADC_Read_CP(void)
  211. {
  212.     unsigned int result;
  213.     ADCCON0 &= ~0x0F;
  214.     ADCCON0 |= 0x40;
  215.     while(!(ADCCON0 & 0x80));
  216.     result = (ADCRH << 4) | (ADCRL & 0x0F);
  217.     ADCCON0 &= ~0x80;
  218.     return result;
  219. }

  220. // ========== 设置充电电流(直接计算占空比) ==========
  221. void Set_Charging_Current(unsigned char amps)
  222. {
  223.     unsigned long duty_percent;
  224.     unsigned int  duty_reg;
  225.    
  226.     if(amps < 6)  amps = 6;
  227.     if(amps > 63) amps = 63;
  228.    
  229.     duty_percent = (unsigned long)amps * 10 / 6;
  230.     duty_reg = (unsigned int)((unsigned long)PWM_PERIOD_VAL * duty_percent / 100);
  231.    
  232.     if(duty_reg > 15360) duty_reg = 15360;
  233.    
  234.     PWM0H = (duty_reg >> 8) & 0xFF;
  235.     PWM0L = duty_reg & 0xFF;
  236.    
  237.     pwm_duty = duty_reg;
  238. }

  239. // ========== 应用档位(切换PWM占空比) ==========
  240. // gear = 0 -> 8A
  241. // gear = 1 -> 16A
  242. // gear = 2 -> 32A
  243. void Apply_Gear(unsigned char gear)
  244. {
  245.     switch(gear)
  246.     {
  247.         case 0:
  248.             Set_Charging_Current(8);    // 8A  -> 2133
  249.             break;
  250.         case 1:
  251.             Set_Charging_Current(16);   // 16A -> 4267
  252.             break;
  253.         case 2:
  254.             Set_Charging_Current(32);   // 32A -> 8533
  255.             break;
  256.         default:
  257.             Set_Charging_Current(8);
  258.             break;
  259.     }
  260. }

  261. // ========== 更新指示灯 ==========
  262. // 档位0:LED1亮
  263. // 档位1:LED1+LED2亮
  264. // 档位2:LED1+LED2+LED3亮
  265. // 本电路假设LED为低电平点亮(共阳接法),高电平熄灭
  266. void Update_LEDs(unsigned char gear)
  267. {
  268.     switch(gear)
  269.     {
  270.         case 0:
  271.             LED1 = 0;  LED2 = 1;  LED3 = 1;   // 只亮LED1
  272.             break;
  273.         case 1:
  274.             LED1 = 0;  LED2 = 0;  LED3 = 1;   // 亮LED1+LED2
  275.             break;
  276.         case 2:
  277.             LED1 = 0;  LED2 = 0;  LED3 = 0;   // 亮LED1+LED2+LED3
  278.             break;
  279.         default:
  280.             LED1 = 0;  LED2 = 1;  LED3 = 1;
  281.             break;
  282.     }
  283. }

  284. // ========== 按键扫描(带消抖) ==========
  285. // 返回 1 表示检测到一次有效按下,否则返回 0
  286. unsigned char Key_Scan(void)
  287. {
  288.     static unsigned char key_lock = 0;   // 按键锁存,防止连按
  289.     unsigned char ret = 0;
  290.    
  291.     if(KEY == 0)          // 检测到低电平(按下)
  292.     {
  293.         if(key_lock == 0)
  294.         {
  295.             Delay_ms(20);  // 消抖延时
  296.             if(KEY == 0)   // 再次确认按下
  297.             {
  298.                 ret = 1;
  299.                 key_lock = 1;
  300.             }
  301.         }
  302.     }
  303.     else
  304.     {
  305.         key_lock = 0;      // 松开后释放锁
  306.     }
  307.    
  308.     return ret;
  309. }

  310. // ========== 毫秒延时 ==========
  311. void Delay_ms(unsigned int ms)
  312. {
  313.     unsigned int i, j;
  314.     for(i = 0; i < ms; i++)
  315.         for(j = 0; j < 1600; j++);
  316. }
复制代码

真是太厉害了,真这样小白也能成为工程师啦,哈哈




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
 楼主| 发表于 2 小时前 | 显示全部楼层
游客请登录后查看回复内容

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2 小时前 来自手机浏览器 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 1 小时前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 1 小时前 来自手机浏览器 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 24 分钟前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|数码之家-技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2026-10-14 23:53 , Processed in 0.093600 second(s), 9 queries , Gzip On, Redis On.

Powered by Discuz!

© MyDigit.Net Since 2006

快速回复 返回顶部 返回列表