9. 直流无刷驱动板温度电压采集

野火使用MOS管搭建的直流无刷驱动板做到了信号完全隔离,其他驱动板基本都只是使用光耦隔离了控制信号, 并没有对ADC采样电路进行隔离,野火不仅使用光耦对控制信号进行了隔离, 还使用AMC1200SDUBR隔离运放对ADC采样电路进行了隔离。

9.1. 电源电压采样电路

如下图所示是电源电压采样电路,在电源电压上并联R53和R54的串联电阻,R54两端的电压作为隔离运放的输入, 再经过隔离运放后差分输出,使用普通运放将差分输出转换成单端输出,连接到RA6T2的ADC采样通道。

电源电压采样电路

从上图中我们可以知道是一个负反馈电路,那么根据虚短和虚断可以知道Up=Un, p点和n点没有电流到运放的2脚和3脚,可以得:

差分转单端输出

将(1)式和(2)式整理可得:

差分转单端输出整理

因为Up=Un,所以有:

差分转单端输出合并

其中R55=R56=R51=R52=10KΩ, 将R55、R56、R51和R52阻值带入上式化简可得:

差分转单端输出化简

9.2. 温度采样电路

使用时需要外接NTC热敏采样电阻,将NTC电阻贴在电机表面,采集电机温度。 热敏电阻器是利用金属氧化物半导体具有较大温度系数的特性,对温度敏感的电阻器。 其电阻值的温度依赖性为:

Rt=R25 exp[B(1/T-1/T25)]
  • Rt:温度T时阻值

  • R25:温度T25时阻值

  • T25:标准温度298.15 K(25℃)

  • B:热敏电阻常数

B的计算公式如下:

B=ln(Rt/R25)/(1/T-1/T25)

由上式可解得:

T=B*R25/(B+T25*ln(Rt/R25))    (1)

上式结果单位为开尔文(K)。

如下图所示是电机温度采样电路,

无刷电机驱动温度采集电路图

由上图可知其中运放是一个电压跟随器,其中运放的5脚和7脚电压相等。

所以,Rt=(3.3-Temperature_ADC)/(Temperature_ADC/4700.0)。 将Rt带入上面的(1)式计算温度。配套的NTC温度传感器B为3950。

9.3. 硬件连接

本章实验需要连接开发板和驱动板,这里给出接线表。

9.3.1. MOS管搭建驱动板

电机主控板与无刷电机驱动板连接见下表所示。

电机与无刷电机驱动板连接

电机

无刷电机驱动板

粗黄

U

粗绿

V

粗蓝

W

细红

+(编码器电源)

细黑

-(编码器电源)

细黄

HIU

细绿

HIV

细蓝

HIW

无刷电机驱动板与主控板连接见下表所示。

无刷电机驱动板与主控板连接

无刷电机驱动板

主控板

5V_IN

5V

GND

GND

U+

PE10

U-

PE13

V+

PE11

V-

PE14

W+

PE12

W-

PE15

HU

PB06

HV

PB07

HW

PA10

SD

PB15

TEMP

PA03

VBUS

PA01

推荐使用配套的牛角排线直接连接驱动板和主控板。连接开发板的那端,请连接在“无刷电机驱动接口2”上。

在NTC接口上插入NTC采样电阻,并将另一头贴于电机表面。

9.4. 在RA6T2中实现温度和电源电压采集

从前面两节中我们知道了温度和电源电压计算方法,下面我们看代码如何实现部分的处理。

9.4.1. 软件设计

配套代码在下面目录中可以找到:

\base_code\basis_part\6T2\直流无刷电机-温度-电源电压读取

9.4.1.1. 编程要点

  1. 初始化ADC并进行数据的获取

  2. 编写函数对采集得到的数据进行处理

  3. 编写获取最终温度值、电压值的函数

  4. 测试代码

9.4.1.2. 新建工程

我们直接在上一章节的的 “400_Motor_BLDC_Hall_SixStep_SpeedControl” 例程的基础上修改程序。

对于 e2 studio 开发环境:

拷贝一份我们之前的 e2s 工程 “400_Motor_BLDC_Hall_SixStep_SpeedControl”, 然后将工程文件夹重命名为 “402_Motor_BLDC_Temp_Voltage_Collect”,最后再将它导入到我们的 e2 studio 工作空间中。

对于 Keil 开发环境:

拷贝一份我们之前的 Keil 工程 “400_Motor_BLDC_Hall_SixStep_SpeedControl”, 然后将工程文件夹重命名为 “402_Motor_BLDC_Temp_Voltage_Collect”,并进入该文件夹里面双击 Keil 工程文件,打开该工程。

工程新建好之后,在工程根目录的 “src” 文件夹下面新建 “adc” 文件夹, 再进入 “adc” 文件夹里面新建源文件和头文件:“motor_v_c_acquisition.c” 和 “motor_v_c_acquisition.h”。 工程文件结构如下。

文件结构
402_Motor_BLDC_Temp_Voltage_Collect
├─ ......
└─ src
   ├─ debug_uart
   │  ├─ bsp_debug_uart.c
   │  └─ bsp_debug_uart.h
   ├─ adc
   │  ├─ bsp_adc_vr.c
   │  └─ bsp_adc_vr.h
   ├─ led
   │  ├─ bsp_led.c
   │  └─ bsp_led.h
   ├─ motor_control
   │  ├─ bsp_motor_control.c   //新建文件
   │  └─ bsp_motor_control.h   //新建文件
   ├─ motor_GPT
   │  ├─ bsp_motor_gpt.c       //新建文件
   │  └─ bsp_motor_gpt.h       //新建文件
   └─ hal_entry.c

9.4.1.3. FSP配置

下面以野火启明6T2开发板为例来讲解相关的 FSP 配置。

根据原理图所示,需要打开 AN001 以及 AN003 引脚用作电压和电流的采集引脚。 ADC相关知识请参考《瑞萨RA系列FSP库开发实战指南》,这里不过多赘述。 这里来讲解使用FSP库进行电压电流采集时如何更便捷的使用偏置值计算,如下图:

偏置表

在图中,除了设置ADC模块的名称以及回调函数,还有两个特殊的表,分别代表 “用户偏移功能” 以及 “用户增益功能”, 用户偏移调整功能可在 A/D 转换数据中添加或减去一个常量值。Virtual channels 从用户指定的值表中选择一个偏移量。 用户增益调整功能将 A/D 转换数据乘以任意系数值。与偏移一样,虚拟通道可以从用户指定的表中选择一个增益值。

提示

当转换数据的数据长度选择14位/12位/10位时,偏移值的低位将根据数据格式的选择进行切割。

所以,使用常用的12位精度接收数据时,应该注意此处的偏移量为16位,要将偏移量切换成二进制,再统一切换成16位数值。 例如本实验中,电压的偏移值为1.24V,计算得到的偏移量为: 1.24V / 3.3V(参考电压) × 65535 ≈ 24625 ,由于是多的偏置值,要减去, 所以FSP偏移表第一项填写 -24625

随后在虚拟通道中从设定的表中选择一个偏移量,如下图所示:

偏置表选择

这样就省去了我们在软件中的计算量,另外还需要新建一个定时器模块,用来定时打印电压电流值。 有关定时器部分也可以看之前的“瑞萨RA系列定时器详解”章节,这里不再赘述。

9.4.1.4. ADC初始化函数

此函数主要的功能是初始化ADC模块

ADC_Init()函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @brief 初始化ADC模块
*
* 该函数用于初始化ADC模块,包括打开ADC、配置扫描组和启动扫描组。
* @note 在调用此函数之前,请确保ADC配置结构体已正确设置。
*/
void adc_Init(void)
{

      fsp_err_t err = FSP_SUCCESS;
      //ADC初始化
      err = R_ADC_B_Open(&g_adc_motor_ctrl, &g_adc_motor_cfg);
      assert(FSP_SUCCESS == err);

      err = R_ADC_B_ScanCfg(&g_adc_motor_ctrl, &g_adc_motor_scan_cfg);
      assert(FSP_SUCCESS == err);

      err = R_ADC_B_ScanGroupStart(&g_adc_motor_ctrl, ADC_GROUP_MASK_ALL);
      assert(FSP_SUCCESS == err);

}

9.4.1.5. 电压温度值读取函数

此函数的功能是去读取温度,电压的ADC值,并通过对于的公式去换算出温度、电压数据。

ADC_GPIO_Config()函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/**
* @brief 读取ADC电压值
*
* 该函数从ADC通道1读取电压值,并将其转换为实际电压值。
*
* @param[in] 无
* @return 返回读取到的电压值
* @note 此函数在调用前需确保ADC扫描已完成。
*/
double Read_ADC_Voltage_Value(void)
{
      uint16_t adc1_data;
      double c0;

      while (!scan_complete_flag)         //等待扫描完成
      {
         ;
      }
      R_ADC_B_Read(&g_adc_motor_ctrl,ADC_CHANNEL_1, &adc1_data);

      c0 = GET_ADC_VDC_VAL(adc1_data);

      c0 = GET_VBUS_VAL(c0);


      return c0;
}

/**
* @brief 读取ADC温度电压
*
* 该函数从ADC通道3读取温度电压
*
* @param[in] 无
* @return 返回读取到的温度电压
* @note 此函数在调用前需确保ADC扫描已完成。
*/
double Read_ADC_Temp_Value(void)
{
      uint16_t adc2_data;
      double c1;

      while (!scan_complete_flag)        //等待扫描完成
      {
         ;
      }

      R_ADC_B_Read(&g_adc_motor_ctrl,ADC_CHANNEL_3, &adc2_data);

      c1 = GET_ADC_VDC_VAL(adc2_data);//计算电流

      return c1;
}

/**
* @brief  获取温度传感器端的电阻值
* @param  无
* @retval 转换得到的电阻值
*/
double get_ntc_r_val(void)
{
   double r = 0;
   double vdc = Read_ADC_Temp_Value();

   r = ((VREF - vdc) / (vdc / (float)4700.0));

   return r;
}


/**
* @brief  获取温度传感器的温度
* @param  无
* @retval 转换得到的温度,单位:(℃)
*/
double get_ntc_t_val(void)
{
   double t = 0;             // 测量温度
   double Rt = 0;            // 测量电阻
   double Ka = 273.15;       // 0℃ 时对应的温度(开尔文)
   double R25 = 10000.0;     // 25℃ 电阻值
   double T25 = Ka + 25;     // 25℃ 时对应的温度(开尔文)
   double B = 3950.0;        /* B-常数:B = ln(R25 / Rt) / (1 / T – 1 / T25),
                           其中 T = 25 + 273.15 */

   Rt = get_ntc_r_val();    // 获取当前电阻值

   t = B * T25 / (B + log(Rt / R25) * T25) - Ka ;    // 使用公式计算

   return t;
}

9.4.1.6. 定时打印函数

此函数1s输出一次电压、温度值

ADC_Init()函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// GPT中断回调函数
void gpt0_timing_callback(timer_callback_args_t *p_args)
{
   // 定时器溢出事件
   if (TIMER_EVENT_CYCLE_END == p_args->event)
   {
      Time += 5;


      // 每秒计算一次平均值并打印
      if (Time == 100)
      {
            printf("电压 = %.2fV, 温度=%0.1f°\r\n", Read_ADC_Voltage_Value(),get_ntc_t_val());

            // 重置时间
            Time = 0;
      }
   }
}

9.4.1.7. hal_entry入口函数

此函数可以通过接收串口命令去控制电机,在电机运行时观察数据的变化。

hal_entry()
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
void hal_entry(void)
{
   /* TODO: add your own code here */
   /* LED初始化 */
   LED_Init();

   /* 串口初始化 */
   Debug_UART9_Init();

   adc_Init();

   /* 电机初始化 */
   bldcm_init();

   printf("这是一个无刷电机基础控制示例\r\n");
   printf("打开串口助手发送以下指令,可控制电机运行状态:\r\n");
   printf("s----------------电机开始旋转\r\n");
   printf("p----------------电机停止旋转\r\n");
   printf("u----------------电机加速旋转[PWM+10%%]\r\n");
   printf("d----------------电机减速旋转[PWM-10%%]\r\n");
   printf("r----------------电机反向旋转\r\n");

   R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS); //延时1秒



   while(1)
   {
      if(uart_recv_motor_enable == true)
      {
         uart_recv_motor_enable = false;

         /* 使能电机 */
         ChannelPulse = 10;
         set_bldcm_speed(ChannelPulse);
         set_bldcm_enable();
         GPT_Timing_Init();      //启动定时器
      }
      if(uart_recv_motor_disenable == true)
      {
            uart_recv_motor_disenable = false;

            /* 停止电机 */
            ChannelPulse = 0;
            set_bldcm_disable();
      }
      if(uart_recv_motor_speed_up == true)
      {
            uart_recv_motor_speed_up = false;

            static int is_run_flag;

            if(ChannelPulse==0)//占空比从零增加后 重新使能一次
            {
               is_run_flag=1;
            }

            /* 增大占空比 */
            ChannelPulse +=10;

            if(ChannelPulse >= 90)
               ChannelPulse = 100;

            set_bldcm_speed(ChannelPulse);

            if(is_run_flag==1)
            {
               set_bldcm_enable();
               is_run_flag=0;
            }
      }
      if(uart_recv_motor_speed_down == true)
      {
            uart_recv_motor_speed_down = false;

            /* 减小占空比 */
            if(ChannelPulse <= 10)
               ChannelPulse = 0;
            else
               ChannelPulse -= 10;

            set_bldcm_speed(ChannelPulse);
      }
      if(uart_recv_motor_reverse == true)
      {
            uart_recv_motor_reverse = false;

            Motor_Control_Reverse();
      }
   }


#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif
}

9.4.2. 下载验证

编译并下载程序后,复位开发板使程序重新运行,打开串口调试助手,连接好电机以及电机驱动板,启动电机,就可以看到开发板采集的电压值和电流值。

读取结果