#include "stm32f10x.h" // Device header
#include "motor.h"
#include "Encoder.h"
#include "AD.h"
//高级定时器1初始化
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器1!
void TIM3_IRQHandler(void) //中断处理函数,此处为定时器3,10ms中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
Encoder_Get(); //得到编码器读数
guiyi();
adcwork();
weizhi_pid();
pid_1(20-dif);
pid_r(20+dif);
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);// 清除中断标志
}
}
void TIM1_PWM_Init(uint16_t arr, uint16_t psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 1. 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
/* 2. 配置 PA8、PA9、PA10、PA11 为复用推挽输出 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 3. 配置 TIM1 基本定时参数 */
TIM_TimeBaseStructure.TIM_Period = arr; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = psc; // 预分频
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/* 4. 配置 PWM 模式 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM1 模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平有效
TIM_OC1Init(TIM1, &TIM_OCInitStructure); // CH1 -> PA8
TIM_OC2Init(TIM1, &TIM_OCInitStructure); // CH2 -> PA9
TIM_OC3Init(TIM1, &TIM_OCInitStructure); // CH3 -> PA10
TIM_OC4Init(TIM1, &TIM_OCInitStructure); // CH4 -> PA11
/* 5. 启动定时器 */
TIM_Cmd(TIM1, ENABLE);
/* 6. 高级定时器必须开启主输出才能有 PWM 波形 */
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
// 定时器3中断初始化函数
void TIM3_ISR_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 使能TIM3时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 2. 定时器参数配置
TIM_TimeBaseStructure.TIM_Period = 10000 - 1; // 自动重装载值,定时周期
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 预分频,假设72MHz时钟,分频720后变100kHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割,不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 3. 配置NVIC中断分组和优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 建议在主程序初始化时调用一次即可
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // TIM3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断
NVIC_Init(&NVIC_InitStructure);
// 4. 使能TIM3更新中断
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
// 5. 使能定时器
TIM_Cmd(TIM3, ENABLE);
}
/**************************************************************************
Function: Initialize TIM2 to encoder interface mode
Input : none
Output : none
函数功能:把TIM2初始化为编码器接口模式
**************************************************************************/
void TIM2_Encoder_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 1. 使能时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); // GPIOA, GPIOB和AFIO时钟
/* 2. 禁用JTAG,释放PA15和PB3 ,将TIM2的引脚重映射到PA15和PB3*/
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
/* 3. 配置PA15和PB3为上拉输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* 4. 定时器基本参数配置 */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStructure.TIM_Period = 65535; // 最大计数值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* 5. 编码器接口配置,模式3 */
TIM_EncoderInterfaceConfig(
TIM2,
TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising,
TIM_ICPolarity_Rising
);
/* 6. 输入捕获滤波器 */
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10; // 滤波10,防抖
TIM_ICInit(TIM2, &TIM_ICInitStructure);
/* 7. 计数器清零并启动 */
TIM_SetCounter(TIM2, 0);
TIM_Cmd(TIM2, ENABLE);
}
/**************************************************************************
Function: Initialize TIM4 to encoder interface mode
Input : none
Output : none
函数功能:把TIM4初始化为编码器接口模式
**************************************************************************/
void TIM4_Encoder_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 1. 使能时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//使能定时器4的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);//使能PB端口时钟
/* 2. 配置PB6和PB7为上拉输入 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
/* 3. 定时器基本参数配置 */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Prescaler = 0; // 预分频器
TIM_TimeBaseStructure.TIM_Period = 65535; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频:不分频
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;////TIM向上计数
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
/* 4. 编码器接口配置,模式3 */
TIM_EncoderInterfaceConfig(TIM4,
TIM_EncoderMode_TI12,
TIM_ICPolarity_Rising,
TIM_ICPolarity_Rising);//使用编码器模式3
/* 5. 输入捕获滤波器 */
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_ICFilter = 10;
TIM_ICInit(TIM4, &TIM_ICInitStructure);
/* 6. 计数器清零并启动 */
TIM_SetCounter(TIM4,0);
TIM_Cmd(TIM4, ENABLE);
}
这个是我的timer。c文件,#include "stm32f10x.h" // Device header
#include "Encoder.h"
#include "AD.h"
int err=0;
int dif=0;
int err_old;
float kp= 0.5 ;
float kd= 0;
void motor_control_L(int pwm_l)
{
if(pwm_l>=0)
{
TIM_SetCompare1(TIM1,0);
TIM_SetCompare2(TIM1,pwm_l);
}
else if(pwm_l<0)
{
TIM_SetCompare1(TIM1,-pwm_l);
TIM_SetCompare2(TIM1,0);
}
}
void motor_control_R(int pwm_r)
{
if(pwm_r>=0)
{
TIM_SetCompare3(TIM1,0);
TIM_SetCompare4(TIM1,pwm_r);
}
else if(pwm_r<0)
{
TIM_SetCompare3(TIM1,-pwm_r);
TIM_SetCompare4(TIM1,0);
}
}
float kp_1 = 4 ;
float ki_1 = 1.6;
int pwm_out_1,error_1,error_1_old;
void pid_1(int expect_1)
{
error_1=expect_1-Encoder_Left;
pwm_out_1+=kp_1*(error_1-error_1_old)+ki_1 * error_1;
error_1_old=error_1;
if(pwm_out_1>750)
{
pwm_out_1=750;
}
if(pwm_out_1<-750)
{
pwm_out_1=-750;
}
motor_control_L(pwm_out_1);
}
float kp_r = 4 ;
float ki_r = 1.6;
int pwm_out_r,error_r,error_r_old;
void pid_r(int expect_r)
{
error_r=expect_r-Encoder_Right;
pwm_out_r+=kp_r*(error_r-error_r_old)+ki_r * error_r;
error_r_old=error_r;
if(pwm_out_r>750)
{
pwm_out_r=750;
}
if(pwm_out_r<-750)
{
pwm_out_r=-750;
}
motor_control_R(pwm_out_r);
}
void weizhi_pid()
{
err=adcerror;
dif=kp*err+kd*(err-err_old);
if(dif>20)
{
dif=20;
}
if(dif<-20)
{
dif=-20;
}
err_old=err;
}
这个是我的motor。c文件#include "stm32f10x.h" // Device header
#include "math.h"
uint16_t AD_Value[5]; //定义用于存放AD转换结果的全局数组
uint16_t L; // 最左侧传感器归一化值
uint16_t L_M; // 左中侧传感器归一化值
uint16_t R_M; // 右中侧传感器归一化值
uint16_t R; // 最右侧传感器归一化值
uint16_t ADC_min = 10; // 传感器无信号时的最小ADC值(需实测)
uint16_t ADC_max = 2000; // 传感器在赛道上的最大ADC值(需实测)
int32_t fenzi;
uint32_t fenmu;
int16_t adcerror; // 最终输出的转向偏差(范围-30~30)
/**
* 函 数:AD初始化
* 参 数:无
* 返 回 值:无
*/
void AD_Init(void)
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开启DMA1的时钟
/*设置ADC时钟*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0、PA1、PA2和PA3引脚初始化为模拟输入
/*规则组通道配置*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5); //规则组序列2的位置,配置为通道1
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_55Cycles5); //规则组序列3的位置,配置为通道4
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 4, ADC_SampleTime_55Cycles5); //规则组序列4的位置,配置为通道6
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 5, ADC_SampleTime_55Cycles5); //规则组序列4的位置,配置为通道7
/*ADC初始化*/
ADC_InitTypeDef ADC_InitStructure; //定义结构体变量
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换,使能,每转换一次规则组序列后立刻开始下一次转换
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式,使能,扫描规则组的序列,扫描数量由ADC_NbrOfChannel确定
ADC_InitStructure.ADC_NbrOfChannel = 5; //通道数,为4,扫描规则组的前4个通道
ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1
/*DMA初始化*/
DMA_InitTypeDef DMA_InitStructure; //定义结构体变量
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设基地址,给定形参AddrA
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据宽度,选择半字,对应16为的ADC数据寄存器
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址自增,选择失能,始终以ADC数据寄存器为源
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器基地址,给定存放AD转换结果的全局数组AD_Value
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据宽度,选择半字,与源数据宽度对应
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址自增,选择使能,每次转运后,数组移到下一个位置
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,选择由外设到存储器,ADC数据寄存器转到数组
DMA_InitStructure.DMA_BufferSize = 5; //转运的数据大小(转运次数),与ADC通道数一致
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //模式,选择循环模式,与ADC的连续转换一致
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //存储器到存储器,选择失能,数据由ADC外设触发转运到存储器
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //优先级,选择中等
DMA_Init(DMA1_Channel1, &DMA_InitStructure); //将结构体变量交给DMA_Init,配置DMA1的通道1
/*DMA和ADC使能*/
DMA_Cmd(DMA1_Channel1, ENABLE); //DMA1的通道1使能
ADC_DMACmd(ADC1, ENABLE); //ADC1触发DMA1的信号使能
ADC_Cmd(ADC1, ENABLE); //ADC1使能
/*ADC校准*/
ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
/*ADC触发*/
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发ADC开始工作,由于ADC处于连续转换模式,故触发一次后ADC就可以一直连续不断地工作
}
void guiyi(void)
{
R = (uint16_t)(((uint32_t)(AD_Value[3] - ADC_min) * 100) / (ADC_max - ADC_min));
R_M = (uint16_t)(((uint32_t)(AD_Value[2] - ADC_min) * 100) / (ADC_max - ADC_min));
L_M = (uint16_t)(((uint32_t)(AD_Value[1] - ADC_min) * 100) / (ADC_max - ADC_min));
L = (uint16_t)(((uint32_t)(AD_Value[0] - ADC_min) * 100) / (ADC_max - ADC_min));
if (R > 100) R = 100;
if (R_M > 100) R_M = 100;
if (L_M > 100) L_M = 100;
if (L > 100) L = 100;
}
void adcwork(void)
{
fenzi = 60 *( (L - R) + 3 * (L_M - R_M));
fenmu = (L + R) + 3*(L_M + R_M) + 1;
adcerror = (int16_t)(fenzi / fenmu);
if (adcerror > 30)
{
adcerror = 30;
}
else if (adcerror < -30)
{
adcerror = -30;
}
}
这个是我的adc。c文件#include "stm32f10x.h" // Device header
#include "timer.h"
int Encoder_Left=0;
int Encoder_Right=0;
/**************************************************************************
Function: Read encoder count per unit time
Input : TIMX:Timer
Output : none
函数功能 :单位时间读取编码器计数
返 回 值 :速度值
**************************************************************************/
int Read_Encoder_L (void)
{
int Encoder_TIM_L;
Encoder_TIM_L=TIM2->CNT; //读取计数
if(Encoder_TIM_L>0xefff)Encoder_TIM_L=Encoder_TIM_L-0xffff; //转化计数值为有方向的值,大于0正转,小于0反转。
//TIM4->CNT范围为0-0xffff,初值为0。
TIM2->CNT=0; //读取完后计数清零
return Encoder_TIM_L; //返回值
}
int Read_Encoder_R (void)
{
int Encoder_TIM_R;
Encoder_TIM_R=TIM4->CNT; //读取计数
if(Encoder_TIM_R>0xefff)Encoder_TIM_R=Encoder_TIM_R-0xffff; //转化计数值为有方向的值,大于0正转,小于0反转。
//TIM4->CNT范围为0-0xffff,初值为0。
TIM4->CNT=0; //读取完后计数清零
return Encoder_TIM_R; //返回值
}
void Encoder_Get(void)
{
Encoder_Left=Read_Encoder_L(); //读取左轮编码器的值,前进为正,后退为负
Encoder_Right=Read_Encoder_R(); //读取右轮编码器的值,前进为正,后退为负
}
这个是我的encorder。c文件为什么我的stm32f103c8t6为核心板的电磁循迹智能小车不能循迹,只会乱跑
最新发布