5. 直流有刷驱动板电流电压采集 | 您所在的位置:网站首页 › 电流电路图片 › 5. 直流有刷驱动板电流电压采集 |
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 实验室设备网 版权所有 |