10. 无刷有刷驱动板温度电压三相电流采集

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

10.1. 电流采样电路

如下图所示是电流采样电路,在电机驱动电路中串入一个0.02Ω、2W的采样电阻,将电流信号转换成电压信号, 再经过隔离运放放大8倍后差分输出,使用普通运放将差分输出转换成单端输出给STM32的ADC采样通道。

电流采样电路

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

差分转单端输出

R108与后面的电容组成RC滤波电路,R108上流过的电流很小,压降也小,可以忽略不计,Vo等于Vcurrent_adc

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

差分转单端输出整理

因为Up=Un,所以有:

差分转单端输出合并

其中R67=R66=R69=R68=10KΩ, 将R67、R66、R69和R68阻值带入上式化简可得:

差分转单端输出化简

因为隔离运放将Vi放大8倍后输出,所以有U7-U6=8*Vi, 带入上式可得:

差分转单端输出结果

在下图中使用电压比较器LMV331SE实现10A过流保护电路

比较器逻辑:IN+电压大于IN-,则输出高电平。反之则相反。

正常情况下:Motor_IU/V/W_ADC采集的电压低于R86和R87分压值2V84_IN,通过比较器U19/U20/U21输出高电平3.3V,U33引脚3处于高电平3.3V,与R142和R143分压值(3V3_IN/2)比较输出低电平0V,(或者短时间内Motor_IU/V/W_ADC电压高于分压值2V84_IN,通过比较器U19/U20/U21输出低电平0V,通过R133、R134、C33器件RC充放电作用,始终U33引脚3电压高于R142和R143分压值(3V3_IN/2),比较器输出低电平0V),则Q8处于关闭状态,C99两端为3.3V,U34比较器引脚1电压高于引脚3,则输出高电平3.3V,Motor_SD_B为3.3V,表示正常工作,SS8550是PNP管,过流指示灯也不亮。

过流保护过程:Motor_IU/V/W_ADC采集的电压高于R86和R87分压值2V84_IN,通过比较器U19/U20/U21输出低电平0V,通过R133、R134、C33器件RC充放电(触发时间与阻容值有关),使U33引脚3电压低于R142和R143分压值(3V3_IN/2)比较输出高电平3.3V,则Q8处于导通状态,U34引脚1电压迅速变为0V,输出低电平,Motor_SD_B为0V,表示电路处于过流状态,SS8550是PNP管,过流指示灯亮起。

过流保护恢复正常:Motor_IU/V/W_ADC采集的电压低于R86和R87分压值2V84_IN,通过比较器U19/U20/U21输出高电平3.3V,通过R133、R134、C33器件RC充放电(触发时间与阻容值有关),U33引脚3始终高于R142和R143分压值(3V3_IN/2)比较输出低电平0V,则Q8处于关闭状态,R145和C99阻容RC充电,充电时间长短反应到单次保护时间,当R145和C99阻容RC充电电压高于R146和R147分压,则U34比较器引脚1电压高于引脚3,则输出高电平3.3V,Motor_SD_B为3.3V,表示正常工作,SS8550是PNP管,过流指示灯也不亮。

R144起到触发电压缓冲作用:

正常时:U33引脚3处于高电平3.3V,U33输出0V,则U33分压值为:U=((R143//R144)*3.3V)/(R142+R143//R144)≈1.571V,触发保护电压为1.571V。

过流触发:当U33引脚3低于1.571V,则已触发保护,U33输出3.3V,则U33分压值为:U=(R143*3.3V)/(R142//R144+R143)≈1.729V,触发恢复电压为1.729V。

电压比较器

10.2. 编程要点

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

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

  3. 编写获取最终电流值的函数

  4. 测试代码

10.2.1. 软件分析

时钟等其他相关的初始化与前面工程相同,这里不过多赘述,我们直接看ADC初始化的代码,看ADC初始化结构体各个参数的配置,如果对ADC配置有疑问,请看《野火STM32库开发实战指南》,有针对ADC外设的细致讲解。


10.2.1.1. ADC_Init()函数

ADC_Init()函数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/**
* @brief  电流采集初始化
* @param  无
* @retval 无
*/
void ADC_Init(void)
{
   ADC_GPIO_Config();
   adc_dma_init();
   ADC_Mode_Config();
}

在ADC_Init()函数中,我们对ADC采集涉及到的相关GPIO进行了初始化,对DMA获取数据进行了配置,也配置了ADC采集的模式,再具体看每一个函数的实现。

ADC_GPIO_init()函数

adc_dma_init()
 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
void adc_dma_init(void)
{
   // ------------------DMA Init 结构体参数 初始化--------------------------
   // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的
   // 开启DMA时钟
  TEMP_ADC_DMA_CLK_ENABLE();
   // 数据传输通道
   DMA_Init_Handle.Instance = TEMP_ADC_DMA_STREAM;
   // 数据传输方向为外设到存储器
   DMA_Init_Handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
   // 外设寄存器只有一个,地址不用递增
   DMA_Init_Handle.Init.PeriphInc = DMA_PINC_DISABLE;
   // 存储器地址固定
   DMA_Init_Handle.Init.MemInc = DMA_MINC_ENABLE;
   // 外设数据大小为半字,即两个字节
   DMA_Init_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
   //        存储器数据大小也为半字,跟外设数据大小相同
   DMA_Init_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
   // 循环传输模式
   DMA_Init_Handle.Init.Mode = DMA_CIRCULAR;
   // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
   DMA_Init_Handle.Init.Priority = DMA_PRIORITY_HIGH;
   // 禁止DMA FIFO     ,使用直连模式
   DMA_Init_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
   // FIFO 大小,FIFO模式禁止时,这个不用配置
   DMA_Init_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
   DMA_Init_Handle.Init.MemBurst = DMA_MBURST_SINGLE;
   DMA_Init_Handle.Init.PeriphBurst = DMA_PBURST_SINGLE;
   // 选择 DMA 通道,通道存在于流中
   DMA_Init_Handle.Init.Channel = CURR_ADC_DMA_CHANNEL;
   //初始化DMA流,流相当于一个大的管道,管道里面有很多通道
   HAL_DMA_Init(&DMA_Init_Handle);

   __HAL_LINKDMA(&ADC_Handle,DMA_Handle,DMA_Init_Handle);
}
adc_dma_init()配置为半字传输,方向配置为从ADC外设搬运数据到内存中,最后初始化DMA。

10.2.1.2. ADC_Mode_Config()函数

ADC_Mode_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
/**
* @brief  ADC 和 DMA 初始化
* @param  无
* @retval 无
*/
static void ADC_Mode_Config(void)
{
   // 开启ADC时钟
   CURR_ADC_CLK_ENABLE();
   // -------------------ADC Init 结构体 参数 初始化------------------------
   // ADC3
   ADC_Handle.Instance = CURR_ADC;
   // 时钟为fpclk 4分频
   ADC_Handle.Init.ClockPrescaler = ADC_CLOCKPRESCALER_PCLK_DIV4;
   // ADC 分辨率
   ADC_Handle.Init.Resolution = ADC_RESOLUTION_12B;
   // 使能扫描模式,多通道采集才需要
   ADC_Handle.Init.ScanConvMode = ENABLE;
   // 连续转换
   ADC_Handle.Init.ContinuousConvMode = ENABLE;
   // 非连续转换
   ADC_Handle.Init.DiscontinuousConvMode = DISABLE;
   // 非连续转换个数
   ADC_Handle.Init.NbrOfDiscConversion   = 0;
   //禁止外部边沿触发
   ADC_Handle.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
   //使用软件触发
   ADC_Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
   //数据左对齐
   ADC_Handle.Init.DataAlign = ADC_DATAALIGN_LEFT;
   //转换通道 2个
   ADC_Handle.Init.NbrOfConversion = 5;
   //使能连续转换请求
   ADC_Handle.Init.DMAContinuousRequests = ENABLE;
   //转换完成标志
   ADC_Handle.Init.EOCSelection          = ADC_EOC_SINGLE_CONV;
   // 初始化ADC
   HAL_ADC_Init(&ADC_Handle);

   //---------------------------------------------------------------------------
   ADC_ChannelConfTypeDef ADC_Config;

   ADC_Config.Channel      = CURR_ADC_CHANNEL;
   ADC_Config.Rank         = 1;
   // 采样时间间隔
   ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
   ADC_Config.Offset       = 0;
   // 配置 ADC 通道转换顺序为1,第一个转换,采样时间为3个时钟周期
   HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

   ADC_Config.Channel      = VBUS_ADC_CHANNEL;
   ADC_Config.Rank         = 2;
   ADC_Config.Offset       = 0;
   HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);
   // 采样时间间隔
   ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
   ADC_Config.Offset       = 0;

   ADC_Config.Channel      = CURR_U_ADC_CHANNEL;
   ADC_Config.Rank         = 3;
   // 采样时间间隔
   ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
   ADC_Config.Offset       = 0;
   HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

   ADC_Config.Channel      = CURR_V_ADC_CHANNEL;
   ADC_Config.Rank         = 4;
   // 采样时间间隔
   ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
   ADC_Config.Offset       = 0;
      HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config);

   ADC_Config.Channel      = CURR_W_ADC_CHANNEL;
   ADC_Config.Rank         = 5;
   // 采样时间间隔
   ADC_Config.SamplingTime = ADC_SAMPLETIME_3CYCLES;
   ADC_Config.Offset       = 0;
   if (HAL_ADC_ConfigChannel(&ADC_Handle, &ADC_Config) != HAL_OK)
   {
      while(1);
   }

   // 外设中断优先级配置和使能中断配置
   HAL_NVIC_SetPriority(ADC_DMA_IRQ, 1, 1);
   HAL_NVIC_EnableIRQ(ADC_DMA_IRQ);

   HAL_ADC_Start_DMA(&ADC_Handle, (uint32_t*)&adc_buff, ADC_NUM_MAX);
}

ADC_Mode_Config()函数对ADC进行了配置,具体看代码中各个参数的注释。将ADC配置为循环采集,因实际工程中也进行了电压采集、三相电流采集、温度采集,所以配置了5个转换通道,最后分别配置5个通道参数,就完成了ADC的配置。再配置的最后,使用HAL_ADC_Start_DMA使能DMA传输,就可以开始采集数据了,但是我们还需要对数据进行更多的处理,才能使数据稳定可靠。

10.2.2. 数据处理部分

10.2.2.1. HAL_ADC_ConvCpltCallback()函数

当DMA搬运ADC_NUM_MAX个数据后,我们预设的BUF已经被填满了ADC采集得到的数据,这时DMA会产生一个中断,最终回调HAL_ADC_ConvCpltCallback通知用户,一轮ADC的采集已经完成。这时我们就可以先使用HAL_ADC_Stop_DMA停止DMA和ADC的工作,进行对数据的处理,当数据处理完成,我们再开启ADC的采集。

在HAL_ADC_ConvCpltCallback()回调函数中,我们对电流采集的原始数据进行 累加 给赋值adc_mean,再将adc_mean除以采集数据的个数(ADC_NUM_MAX/5),以获得采集数据的原始数据平均值。将其 累加 赋值给adc_mean_sum,并对adc_mean_count+1,记录采集次数。完成这些操作后,再重新调用HAL_ADC_Start_DMA以开启新一轮的采集。

在电流数据处理下面,我们可以看到对电压也进行了同样的采集操作,并且添加了宏定义#if,因为同时采集电流电压较为耗时。我们实验时可以将#if置为0,方便我们观察实验现象。

get_curr_val()函数


前面我们通过回调函数获得了采集的原始数据,现在我们对采集得到的原始数据进行处理,以得到真实的电流值。

get_curr_val()函数
  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
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/**
* @brief  获取电流值
* @param  无
* @retval 转换得到的电流值
*/
int32_t get_curr_val(void)
{
static uint8_t flag = 0;
static uint32_t adc_offset = 0;    // 偏置电压
int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

curr_adc_mean = adc_mean_sum / adc_mean_count;    // 保存平均值


   adc_mean_count = 0;
   adc_mean_sum = 0;

   if (flag < 17)
   {
      adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
      flag += 1;
   }
   if(curr_adc_mean>=adc_offset)
   {
      curr_adc_mean -= adc_offset;                     // 减去偏置电压
   }else
   {
      curr_adc_mean=0;
   }

   float vdc = GET_ADC_VDC_VAL(curr_adc_mean);      // 获取电压值

   return GET_ADC_CURR_VAL(vdc);
   }

/**
* @brief  获取温度传感器的温度
* @param  无
* @retval 转换得到的温度,单位:(℃)
*/

float get_ntc_t_val(void)
{
float t = 0;             // 测量温度
float Rt = 0;            // 测量电阻
float Ka = 273.15;       // 0℃ 时对应的温度(开尔文)
float R25 = 10000.0;     // 25℃ 电阻值
float T25 = Ka + 25;     // 25℃ 时对应的温度(开尔文)
float 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;
}
/**
* @brief  获取V相的电流值
* @param  无
* @retval 转换得到的电流值
*/
int32_t get_curr_val_v(void)
{
static uint8_t flag = 0;
static uint32_t adc_offset = 0;    // 偏置电压
int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

curr_adc_mean = adc_mean_sum_v / adc_mean_count_v;    // 保存平均值


   adc_mean_count_v = 0;
   adc_mean_sum_v = 0;

   if (flag < 17)
   {
      adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
      flag += 1;
   }
   if(curr_adc_mean>=adc_offset)
   {
      curr_adc_mean -= adc_offset;                     // 减去偏置电压
   }else
   {
      curr_adc_mean=0;
   }

float vdc = GET_ADC_VDC_VAL(curr_adc_mean);      // 获取电压值

return GET_ADC_CURR_VAL(vdc);
}
/**
* @brief  获取U相的电流值
* @param  无
* @retval 转换得到的电流值
*/
int32_t get_curr_val_u(void)
{
static uint8_t flag = 0;
static uint32_t adc_offset = 0;    // 偏置电压
int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

curr_adc_mean = adc_mean_sum_u / adc_mean_count_u;    // 保存平均值


   adc_mean_count_u = 0;
   adc_mean_sum_u = 0;

   if (flag < 17)
   {
      adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
      flag += 1;
   }
   if(curr_adc_mean>=adc_offset)
   {
      curr_adc_mean -= adc_offset;                     // 减去偏置电压
   }else
   {
      curr_adc_mean=0;
   }

float vdc = GET_ADC_VDC_VAL(curr_adc_mean);      // 获取电压值

return GET_ADC_CURR_VAL(vdc);
}
/**
* @brief  获取W相的电流值
* @param  无
* @retval 转换得到的电流值
*/
int32_t get_curr_val_w(void)
{
static uint8_t flag = 0;
static uint32_t adc_offset = 0;    // 偏置电压
int16_t curr_adc_mean = 0;         // 电流 ACD 采样结果平均值

curr_adc_mean = adc_mean_sum_w / adc_mean_count_w;    // 保存平均值


   adc_mean_count_w = 0;
   adc_mean_sum_w = 0;

   if (flag < 17)
   {
      adc_offset = curr_adc_mean;    // 多次记录偏置电压,待系统稳定偏置电压才为有效值
      flag += 1;
   }
   if(curr_adc_mean>=adc_offset)
   {
      curr_adc_mean -= adc_offset;                     // 减去偏置电压
   }else
   {
      curr_adc_mean=0;
   }

float vdc = GET_ADC_VDC_VAL(curr_adc_mean);      // 获取电压值

return GET_ADC_CURR_VAL(vdc);
}
/**
* @brief  获取电源电压值
* @param  无
* @retval 转换得到的电压值
*/
float get_vbus_val(void)
{
float vdc = GET_ADC_VDC_VAL(vbus_adc_mean);      // 获取电压值
return GET_VBUS_VAL(vdc);
}

在get_curr_val()中,我们对采集得到的原始数据累加的和(adc_mean_sum)除以累加次数(adc_mean_count)来求平均值,进行滤波操作,保证数据的稳定性。然后我们将以前的累加次数清零,为后面采集的数据做重新开始累加、滤波的准备。在实际情况中,每采集10次数据做一次滤波,得到的电流数据比较稳定,但是在程序设定时间内可能无法达到每采集10次做一次滤波操作,所以用户可以根据实际的实际需求来设定采集次数,采集间隔等。

必须注意的是,在硬件设计中,并不是以0v为起始电压而是加了1.24v的偏置,具体看图 差分转单端输出结果 中的V1.24。所以我们程序中,使用flag,在开发板上电后电机没有启动时,就先采集17次偏置电压数据(实测中采集17次后的偏执电压较为准确,这个次数和电路达到稳定状态的时间有关),然后将偏置电压保存起来,这里使用static定义adc_offset。最后在每次获取电流值计算时,都会将偏置电压减去,保证数据是正确的。

在得到有效的电流转换电压的采集值后,我们对该电压值进行转换操作,将其转换回电流,调用GET_ADC_CURR_VAL。

GET_ADC_VDC_VAL()函数
1
2
3
4
5
#define VREF                            3.3f     // 参考电压,理论上是3.3,可通过实际测量得3.258
#define ADC_NUM_MAX                     320    // ADC 转换结果缓冲区最大值
#define GET_ADC_VDC_VAL(val)            ((float)val/(float)4096.0f*VREF)          // 得到电压值
#define GET_ADC_CURR_VAL(val)           (((float)val)/(float)8.0/(float)0.02*(float)1000.0)          // 得到电流值,电压放大8倍,0.02是采样电阻,单位mA。
#define GET_VBUS_VAL(val)               (((float)val - 1.24f) * 37.0f )      // 获取电压值(测量电压是电源电压的1/37)

只是简单宏定义即可实现。

10.3. 主函数

电流main函数
 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
98
/**
* @brief  主函数
* @param  无
* @retval 无
*/
int main(void)
{
   __IO uint16_t ChannelPulse = PWM_MAX_PERIOD_COUNT/10;
   uint8_t i = 0;
   uint8_t flag = 0;
   uint8_t t_max_count = 0;

    /* 初始化系统时钟为168MHz */
   SystemClock_Config();

   /* HAL 初始化 */
   HAL_Init();

   /* 初始化按键GPIO */
   Key_GPIO_Config();

   /* LED 灯初始化 */
   LED_GPIO_Config();

   /* 调试串口初始化 */
   DEBUG_USART_Config();

   /* ADC 初始化 */
   ADC_Init();

   printf("野火直流无刷电机温度采样与电源电压与三相电流采样例程\r\n");

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

   while(1)
   {
      /* 扫描KEY1 */
      if( Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON)
      {
         /* 使能电机 */
         set_bldcm_speed(ChannelPulse);
         set_bldcm_enable();
      }

      /* 扫描KEY2 */
      if( Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON)
      {
         /* 停止电机 */
         set_bldcm_disable();
      }

      /* 扫描KEY3 */
      if( Key_Scan(KEY3_GPIO_PORT, KEY3_PIN) == KEY_ON)
      {
         /* 增大占空比 */
         ChannelPulse += PWM_MAX_PERIOD_COUNT/10;

         if(ChannelPulse > PWM_MAX_PERIOD_COUNT)
         ChannelPulse = PWM_MAX_PERIOD_COUNT;

         set_bldcm_speed(ChannelPulse);
      }

      /* 扫描KEY4 */
      if( Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON)
      {
         if(ChannelPulse < PWM_MAX_PERIOD_COUNT/10)
         ChannelPulse = 0;
         else
         ChannelPulse -= PWM_MAX_PERIOD_COUNT/10;

         set_bldcm_speed(ChannelPulse);
      }

            /* 扫描KEY5 */
      if( Key_Scan(KEY5_GPIO_PORT, KEY5_PIN) == KEY_ON)
      {
         /* 转换方向 即Forward,正转运行;相对地,REV,即Reverse,反转运行
            采用三元运算符      i是奇数是反转运行,i是偶数是正转运行,*/
         set_bldcm_direction( (++i % 2) ? MOTOR_FWD : MOTOR_REV);
      }

      if (HAL_GetTick()%50 == 0 && flag == 0)    // 每50毫秒读取一次温度、电压
      {
         flag = 1;
            int32_t current_v = get_curr_val_v();
            int32_t current_u = get_curr_val_u();
            int32_t current_w = get_curr_val_w();
         printf("电源电压=%0.1fV, T=%0.1f℃,U相电流=%dmA,V相电流=%dmA,W相电流=%dmA\r\n",
               get_vbus_val(),  get_ntc_t_val(),current_v,current_u,current_w);
      }
      else if (HAL_GetTick()%50 != 0 && flag == 1)
      {
         flag = 0;
      }
   }
}

保证开发板相关硬件连接正确,用USB线连接开发板“USB转串口”接口跟电脑,在电脑端打开串口调试助手,把编译好的程序下载到开发板,串口调试助手会显示程序输出的信息。 我们通过开发板上的五个按键控制电机加减速和方向,在串口调试助手的接收区即可看到信息三相电流、电压、温度等信息。

野火直流有刷电机电流读取实验

注意

注意:电机正在运行时应该先停止电机再复位,而不建议直接复位开发板,因为这属于非正常操作,复位的瞬间电机还在继续运动,产生的反电动势有损坏硬件的风险。