5. 直流有刷驱动板电流电压采集 您所在的位置:网站首页 电流电路图片 5. 直流有刷驱动板电流电压采集

5. 直流有刷驱动板电流电压采集

2024-07-18 08:56| 来源: 网络整理| 查看: 265

5. 直流有刷驱动板电流电压采集¶

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

5.1. 电流采样电路¶

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

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

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

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

因为Up=Un,所以有:

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

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

在下图中使用电压比较器LMV331SE实现10A过流保护电路,电流采样电路中Vi经过隔离运放和普通运放后变成Vcurrent_adc输入到下图比较器的IN-, 当IN-的电压超过IN+时,比较器的OUT将输出低电平到74HC1G66GW(模拟开关)的Y端口和左下端的NPN型MOS管中。当OUT将输出低电平时,NPN-MOS截止,此时3V3电压会供给74HC1G66GW,让其使能,这样模拟开关就会闭合,74HC1G66GW的Y端口与Z端口相当于连接在一起,低电平信号从Z端口,输出到下一级锁存器SN74LVC1G373DBVR,锁存器对输入信号进行锁存并输出到与门。

光耦隔离部分电路图如下图所示。

与门输入输出与MOS管状态真值表如下表所示。

与门输入输出与MOS管状态真值表¶

A

B

Y

MOS

H

H

H

可导通

H

L

L

关断(过流保护)

L

H

L

关断(单片机控制关断)

L

L

L

关断(单片机控制关断,过流保护)

5.2. 电压采样电路¶

如下图所示是电源电压采样电路,在电源电压上并联R18和R19、R59的串联电阻,R19两端的电压作为隔离运放的输入, 再经过隔离器件后差分输出,使用普通运放将差分输出转换成单端输出,连接到STM32的ADC采样通道。 隔离运放的输入电压为Vi,则有:Vi/R19=POWER/(R18+R59+R19),带入电阻值可得:Vi=POWER/37, 通过上一节中电流采样电流的计算方法可以计算得到POWER_ADC=POWER/37+1.24,不同的是,电压检测部分的隔离器件是没有进行放大的。

5.3. 硬件连接¶

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

5.3.1. MOS管搭建驱动板¶

电机与MOS管搭建驱动板连接见下表所示。

电机与MOS管搭建驱动板连接¶

电机

MOS管搭建驱动板

M+

M+

5V

编码器电源:+

GND

编码器电源:-

A

A

B

B

M-

M-

MOS管搭建驱动板与主控板连接见下表所示。

MOS管搭建驱动板与主控板连接¶

MOS管搭建驱动板

主控板

PWM1

PA9

PWM2

PA8

SD

PG12

A

PC6

B

PC7

电源输入:5V

5V

电源输入:GND

GND

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

5.4. 在STM32中实现电流电压采集¶

从第一节电流采样电路中,我们可以知道,想要对电流进行采集,需要将电流信号转换为电压信号。我们通过硬件部分完成了对该信号转换、放大处理,这样一来就可以很方便的在STM32使用ADC外设对该信号进行采集。在STM32中采集到了数据,最终再通过的一些数据的处理,我们就可以得到所需的电流值。当然同理可得,我对电压信号的采集也是类似地,下面我们看代码如何进行这部分的处理。

5.4.1. 软件设计¶

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

basis_part\F407\直流有刷减速电机-电流电压读取-MOS管搭建板 5.4.1.1. 编程要点¶

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

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

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

测试代码

5.4.2. 软件分析¶

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

5.4.2.1. ADC初始化¶ 5.4.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采集的模式,再具体看每一个函数的实现。

5.4.2.1.2. ADC_GPIO_Config()函数¶ ADC_GPIO_Config()函数¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20/** * @brief ADC 通道引脚初始化 * @param 无 * @retval 无 */ static void ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能 GPIO 时钟 CURR_ADC_GPIO_CLK_ENABLE(); VBUS_GPIO_CLK_ENABLE(); // 配置 IO GPIO_InitStructure.Pin = CURR_ADC_GPIO_PIN; GPIO_InitStructure.Mode = GPIO_MODE_ANALOG; GPIO_InitStructure.Pull = GPIO_NOPULL ; //不上拉不下拉 HAL_GPIO_Init(CURR_ADC_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.Pin = VBUS_GPIO_PIN; HAL_GPIO_Init(VBUS_GPIO_PORT, &GPIO_InitStructure); }

ADC_GPIO_Config()中的部分配置使用了宏定义,具体定义内容到工程中查看。

5.4.2.1.3. adc_dma_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 35void adc_dma_init(void) { // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 CURR_ADC_DMA_CLK_ENABLE(); // 数据传输通道 DMA_Init_Handle.Instance = CURR_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。

5.4.2.1.4. 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/** * @brief ADC 和 DMA 初始化 * @param 无 * @retval 无 */ static void ADC_Mode_Config(void) { // 开启ADC时钟 CURR_ADC_CLK_ENABLE(); // -------------------ADC Init 结构体 参数 初始化------------------------ // ADC1 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 = 2; //使能连续转换请求 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); /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */ ADC_Config.Channel = VBUS_ADC_CHANNEL; ADC_Config.Rank = 2; // 采样时间间隔 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配置为循环采集,因实际工程中也进行了电压采集,所以配置了两个转换通道,最后分别配置两个通道参数,就完成了ADC的配置。再配置的最后,使用HAL_ADC_Start_DMA使能DMA传输,就可以开始采集数据了,但是我们还需要对数据进行更多的处理,才能使数据稳定可靠。

5.4.2.2. 数据处理部分¶ 5.4.2.2.1. HAL_ADC_ConvCpltCallback()函数¶ HAL_ADC_ConvCpltCallback()函数¶ 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/** * @brief 常规转换在非阻塞模式下完成回调 * @param hadc: ADC 句柄. * @retval 无 */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) { int32_t adc_mean = 0; HAL_ADC_Stop_DMA(hadc); // 停止 ADC 采样,处理完一次数据在继续采样 /* 计算电流通道采样的平均值 */ for(uint32_t count = 0; count PWM_MAX_PERIOD_COUNT) ChannelPulse = PWM_MAX_PERIOD_COUNT; set_motor_speed(ChannelPulse); } /* 扫描KEY4 */ if( Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON) { if(ChannelPulse VBUS_HEX_MIN && vbus_adc_mean ADC_NUM_MAX) // 电源电压超过阈值电压10次 { set_motor_disable(); flag_num = 0; LED1_ON; printf("电源电压超过限制!请检查原因,复位开发板在试!\r\n"); while(1); } }

当看门狗检测到溢出时,会触发此中断回调函数。在函数中,我们使用了flag_num来记录触发异常的次数,如果flag_num超过阈值,我们则认为电路异常了,这时采取电机停机、LED灯显示提示、串口打印等来提示用户电路异常的情况,在实际应用中,我们则根据实际需要来调整这部分代码。

5.5.2.3. 主函数¶

在上面我们配置了ADC模拟看门狗来做电压保护,我们现在用另一种方式,来处理限制电流的情况,就是在主函数中轮询。

限电流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 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113/** * @brief 主函数 * @param 无 * @retval 无 */ int main(void) { __IO uint16_t ChannelPulse = PWM_MAX_PERIOD_COUNT*0.5; uint8_t curr_max_count = 0; uint8_t flag = 0; uint8_t dir = 0; HAL_Init(); /* 初始化系统时钟为168MHz */ SystemClock_Config(); /* 初始化按键GPIO */ Key_GPIO_Config(); /* 初始化 LED */ LED_GPIO_Config(); /* 电机初始化 */ motor_init(); /* 串口初始化 */ DEBUG_USART_Config(); /* ADC 始化 */ ADC_Init(); set_motor_speed(ChannelPulse); set_motor_disable(); // 禁用电机 printf("野火直流有刷电机-限流-过压-欠压保护实验\r\n"); while(1) { /* 扫描KEY1 */ if( Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON) { /* 使能电机 */ set_motor_enable(); } /* 扫描KEY2 */ if( Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON) { /* 禁用电机 */ set_motor_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_motor_speed(ChannelPulse); } /* 扫描KEY4 */ if( Key_Scan(KEY4_GPIO_PORT, KEY4_PIN) == KEY_ON) { if(ChannelPulse CURR_MAX) // 判断是不是超过限定的值 { if (curr_max_count++ > 5) // 连续5次超过 { LED2_ON; set_motor_disable(); curr_max_count = 0; printf("电流超过限制!请检查原因,复位开发板在试!\r\n"); while(1); } } } else if (HAL_GetTick()%50 != 0 && flag == 1) { flag = 0; } } }

同样地,我们在主函数中通过不断轮询电压值是否超过预设值,来记录超限的次数,如果电流超过限制次数到达预设数,则认为电路可能发生故障,提示用户处理。

5.5.3. 下载验证¶

将对应程序下载到开发板上运行,启动电机后,串口正常打印电压电流信息,当用手捏电机时,电机电流增大,超过预设值,发生报警并停机如下图所示。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有