OC-随便看看

OC-随便看看

引言

  • OC和C++一样,几乎完全继承自C语言
  • 个人感觉,OC非常类似C++,不愧是一个娘生的
  • OC基础的学习我是看的《Objective-C_2.0_Mac和iOS开发实践指南》, 这本书非常好,感觉讲的有点深
  • 下面我要慢慢的来消化一下这本书,以巩固OC基础语法与其特性

简单看一下

  • OC中的文件
    • .c .cc, .cpp .h: 分别是C语言的源文件, C++的源文件, 和头文件
    • .m : OC源文件
    • .mm : OC++的源文件
    • .pl : Perl源文件
    • .o : 已经编译过的文件

OC中的类

  • OC中的类的定义方法非常清晰(分离接口和实现文件)

    1. 在 .h文件中利用@interface描述类的组成结构(外部可见的)
    2. 在 .m文件中利用@implementation实现类中定义的方法
      例如:
    //.h文件,  描述Person
    @interface Person : NSObject    //Person类继承自NSOBjectNSObject可以认为是OC中的类的上帝
    {
        NSString* name;             //定义一个实例变量name, 类型为NSString指针
        int age;
    }
    //get 与 set方法,  必须是这种形式
    -(void) setName: (NSString*) n;     
    -(void) name;       //应记住没有get      
    -(void) showInfo;   

    @end        //应以end结尾


    //.m 文件   实现文件中,一般都是对interface中描述的方法进行实现
    @implementation Person     

    -(void) setName: (NSString*) n
    {
        name = n;
    }

    -(NSString*) name
    {
        return name;
    }

    -(void) showInfo
    {
        NSSLog(@"My name is %@ and the age is %i", name, age);  //NSSLog 类似printf  . %@代表打印字符串, %i代表打印整型
    }
    @end    
  • OC中的方法
    • 通过上面的例子, 可以简单的看出了OC中类的定义
    • 可以看出OC中的方法的书写格式是比较特别的, 下面来看一下OC中的方法是怎么来写的
      例如:
      -(void) setName: (NSString*) name;


    一个一个的来看:
      • 代表方法的类型, 是属于对象方法, 还是类方法(+)
    1. (void) 返回值类型
    2. setName 方法名
    3. : 方法接收的参数
    4. (NSString*) 参数类型
    5. name 参数名(形参)
  • 在OC中可以对父类的方法进行重写

  • 在OC中不可以对方法进行重载!
  • OC中 self 类似与 this

OC中的抽象类

  • 在OC中没有用于抽象类的显示语法。
  • 如果具有一个类,他实际上就是一个抽象类,就应该在文档中指出这一点
  • 在OC中,无法阻止有人实际的实例化你认为是抽象的类
  • -
为什么我PA8引脚的低电平只有一点点,无论我怎么改变目标电压,PA8引脚的低电平只有一点点,而且高电平的波还极其不稳定,示波器都读不出改引脚的占空比#include "stm32f4xx.h" #include "delay.h" #include "oled.h" #include "stdio.h" #include "stdlib.h" #include "arm_math.h" #include "pid.h" #include "./adc/bsp_adc.h" #include "tim.h" extern float voltage1, voltage2, voltage3; extern float Vout_actual; float Target= 12; // 目标输出电压 extern uint16_t adc_data_ready; extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; extern uint16_t TIM1_Impluse ;//高级定时器占空比 int main() { OLED_Init(); delay_ms(500); Adc_Init(); TIM_Init(); char str[40]; while(1) { char str[40]; sprintf(str, "Vout: %.2fV", Vout_actual); OLED_ShowString(0, 1, (u8*)str, 12); OLED_Refresh_Gram(); delay_ms(1); } } #include "stm32f4xx_it.h" #include "oled.h" #include <math.h> #include "./adc/bsp_adc.h" #include "pid.h" extern uint16_t TIM1_Impluse ;//高级定时器占空比 extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]; extern float voltage1; extern float Vout_actual; extern float Target ; // 目标输出电压 //extern float pid_out; volatile uint32_t last_adc_value = 0; volatile uint8_t adc_data_ready = 0; void TIM1_UP_TIM10_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { // 必须使用TIM_ClearITPendingBit清除中断标志 TIM_ClearITPendingBit(TIM1, TIM_IT_Update); // 仅更新PWM,ADC值在主循环处理 if(adc_data_ready) { float adc_voltage = last_adc_value * 3.3f / 4096.0f; Vout_actual = adc_voltage * (12.0f / 3.3f); TIM1_Impluse = pid_control(0.8f, 0.05f, 0.05f, Target, Vout_actual); TIM1->CCR1 = TIM1_Impluse; adc_data_ready = 0; } } } void DMA2_Stream0_IRQHandler(void) { if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET) { DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0); last_adc_value = ADC_ConvertedValue[0]; adc_data_ready=1; } } void NMI_Handler(void) { } void HardFault_Handler(void) { /* Go to infinite loop when Hard Fault exception occurs */ while (1) {} } void MemManage_Handler(void) { /* Go to infinite loop when Memory Manage exception occurs */ while (1) {} } void BusFault_Handler(void) { /* Go to infinite loop when Bus Fault exception occurs */ while (1) {} } void UsageFault_Handler(void) { /* Go to infinite loop when Usage Fault exception occurs */ while (1) {} } void DebugMon_Handler(void) { } void SVC_Handler(void) { } void PendSV_Handler(void) { } void SysTick_Handler(void) { } #include "tim.h" uint16_t TIM1_Impluse = 4200;//预设占空比 float z = 0; const uint32_t spwm[400] = { 4200,4265,4331,4397,4463,4529,4595,4660,4726,4791,4857,4922,4987,5051,5116,5180, 5244,5308,5371,5434,5497,5560,5622,5684,5746,5807,5868,5928,5988,6047,6106,6165, 6223,6280,6337,6394,6450,6505,6560,6615,6668,6721,6774,6826,6877,6927,6977,7026, 7075,7122,7169,7216,7261,7306,7350,7393,7436,7477,7518,7558,7597,7636,7673,7710, 7746,7781,7815,7848,7880,7911,7942,7971,8000,8027,8054,8080,8105,8128,8151,8173, 8194,8214,8233,8251,8268,8283,8298,8312,8325,8337,8348,8358,8366,8374,8381,8387, 8391,8395,8397,8399,8400,8399,8397,8395,8391,8387,8381,8374,8366,8358,8348,8337, 8325,8312,8298,8283,8268,8251,8233,8214,8194,8173,8151,8128,8105,8080,8054,8027, 8000,7971,7942,7911,7880,7848,7815,7781,7746,7710,7673,7636,7597,7558,7518,7477, 7436,7393,7350,7306,7261,7216,7169,7122,7075,7026,6977,6927,6877,6826,6774,6721, 6668,6615,6560,6505,6450,6394,6337,6280,6223,6165,6106,6047,5988,5928,5868,5807, 5746,5684,5622,5560,5497,5434,5371,5308,5244,5180,5116,5051,4987,4922,4857,4791, 4726,4660,4595,4529,4463,4397,4331,4265,4200,4134,4068,4002,3936,3870,3804,3739, 3673,3608,3542,3477,3412,3348,3283,3219,3155,3091,3028,2965,2902,2839,2777,2715, 2653,2592,2531,2471,2411,2352,2293,2234,2176,2119,2062,2005,1949,1894,1839,1784, 1731,1678,1625,1573,1522,1472,1422,1373,1324,1277,1230,1183,1138,1093,1049,1006, 963,922,881,841,802,763,726,689,653,618,584,551,519,488,457,428, 399,372,345,319,294,271,248,226,205,185,166,148,131,116,101,87, 74,62,51,41,33,25,18,12,8,4,2,0,0,0,2,4, 8,12,18,25,33,41,51,62,74,87,101,116,131,148,166,185, 205,226,248,271,294,319,345,372,399,428,457,488,519,551,584,618, 653,689,726,763,802,841,881,922,963,1006,1049,1093,1138,1183,1230,1277, 1324,1373,1422,1472,1522,1573,1625,1678,1731,1784,1839,1894,1949,2005,2062,2119, 2176,2234,2293,2352,2411,2471,2531,2592,2653,2715,2777,2839,2902,2965,3028,3091, 3155,3219,3283,3348,3412,3477,3542,3608,3673,3739,3804,3870,3936,4002,4068,4134 }; //TIM1的GPIO static void TIM_GPIO_Config(void) { GPIO_InitTypeDef TIM_GPIO_InitStruct; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);//开钟 /*-----------------------------PA8,PA7------------------------------------*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource8,GPIO_AF_TIM1);//引脚复用 主 PA8,PA7 GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_TIM1);//引脚复用 补 TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //模拟模式GPIO_Mode_AN/F TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8; //引脚 TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7; GPIO_Init(GPIOA, &TIM_GPIO_InitStruct); /*-----------------------------------------------------------------------*/ /*-----------------------------PA9,PB14------------------------------------*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_TIM1);//引脚复用 主 GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_TIM1);//引脚复用 补 TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //模拟模式GPIO_Mode_AN/F TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //引脚 TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; GPIO_Init(GPIOB, &TIM_GPIO_InitStruct); /*-----------------------------------------------------------------------*/ /*-----------------------------PA10,PB1------------------------------------*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_TIM1);//引脚复用 主 GPIO_PinAFConfig(GPIOB,GPIO_PinSource1,GPIO_AF_TIM1);//引脚复用 补 TIM_GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //模拟模式GPIO_Mode_AN/F TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10; //引脚 TIM_GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //高速 TIM_GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽 TIM_GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; GPIO_Init(GPIOA, &TIM_GPIO_InitStruct); //写入 TIM_GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_Init(GPIOB, &TIM_GPIO_InitStruct); /*-----------------------------------------------------------------------*/ } //TIM1 static void TIM_Mode_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_OCInitTypeDef TIM_OCInitStruct; TIM_BDTRInitTypeDef TIM_BDTRInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);///使能时钟 //168MHZ->20kHZ 主频/(计数+1)*(预分频系数+1) //168MHz/8 * 1050 = 20khz /*-----------------------------基本结构体------------------------------------*/ TIM_TimeBaseInitStructure.TIM_Period = (8400-1); //自动重装载值 TIM_TimeBaseInitStructure.TIM_Prescaler=(1-1); //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //1分频 TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0; //不需要重复计数 TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure); //初始化TIM /*-----------------------------基本结构体------------------------------------*/ /*-----------------------------输出比较------------------------------------*/ TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; //pwm模式选择 TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; ///使能输出通道 TIM_OCInitStruct.TIM_OutputNState = TIM_OutputNState_Enable; //使能互补通道 TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; //预设占空比 TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //PWM1和2中的CH和CCR之间值的大小(多用pwm1的模式1) TIM_OCInitStruct.TIM_OCNPolarity = TIM_OCNPolarity_High; //当使用了刹车功能时,两路PWM1和2都会被强制禁止,进而输出我们配置的的空闲先状态 TIM_OCInitStruct.TIM_OCIdleState = TIM_OCIdleState_Set; //刹车时输出通道的状态 Set = high TIM_OCInitStruct.TIM_OCNIdleState = TIM_OCNIdleState_Reset; //刹车时互补通道的状态 Reset = low TIM_OC1Init(TIM1, &TIM_OCInitStruct); //使能通道1 TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); /* 使能通道1重载 */ TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC2Init(TIM1, &TIM_OCInitStruct); TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable); TIM_OCInitStruct.TIM_Pulse = TIM1_Impluse; TIM_OC3Init(TIM1, &TIM_OCInitStruct); TIM_OC3PreloadConfig(TIM1,TIM_OCPreload_Enable); /*-----------------------------输出比较------------------------------------*/ /*-----------------------------死区刹车------------------------------------*/ TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable; //开启死区 TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable; //开启1空闲状态 TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1; //不同的锁定级别 (看BDTR寄存器) TIM_BDTRInitStructure.TIM_DeadTime = 42; //刹车时间,(看BDTR寄存器中的DTG[7:0]) //11转换成二进制为0000 1011 死区时间看[7;5]位,此处为000 TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable; //允许刹车 //BKIN 测到低电平 比较信号禁止 TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High; //高极性 TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable; //自动输出使能(刹车输入无效) TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure); //写入 /*-----------------------------死区刹车------------------------------------*/ TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE); //允许定时器更新中断 | TIM_IT_Trigger TIM_Cmd(TIM1,ENABLE); //使能定时器 TIM_CtrlPWMOutputs(TIM1, ENABLE); //主动输出使能 } static void TIM_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; /*-----------------------------中断------------------------------------*/ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组 NVIC_InitStructure.NVIC_IRQChannel=TIM1_UP_TIM10_IRQn; //定时器1中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&NVIC_InitStructure); //写入 /*-----------------------------中断------------------------------------*/ } void TIM_Init(void) { TIM_GPIO_Config(); TIM_Mode_Config(); TIM_NVIC_Config(); } #include "./adc/bsp_adc.h" __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]={0}; float voltage1=0, voltage2=0, voltage3=0; float Vout_actual; static void ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /*=====================通道1======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK1,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT1, &GPIO_InitStructure); /*=====================通道2======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK2,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT2, &GPIO_InitStructure); /*=====================通道3=======================*/ // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(ADC_GPIO_CLK3,ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = ADC_GPIO_PIN3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //不上拉不下拉 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; GPIO_Init(ADC_GPIO_PORT3, &GPIO_InitStructure); } static void ADC_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 RCC_AHB1PeriphClockCmd(ADC_DMA_CLK, ENABLE); // 外设基址为:ADC 数据寄存器地址 DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR; // 存储器地址,实际上就是一个内部SRAM的变量 DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_ConvertedValue; // 数据传输方向为外设到存储器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; // 缓冲区大小为,指一次传输的数据量 DMA_InitStructure.DMA_BufferSize = RHEOSTAT_NOFCHANEL; // 外设寄存器只有一个,地址不用递增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 存储器地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // // 外设数据大小为半字,即两个字节 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 存储器数据大小也为半字,跟外设数据大小相同 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 循环传输模式 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 禁止DMA FIFO ,使用直连模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO 大小,FIFO模式禁止时,这个不用配置 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 选择 DMA 通道,通道存在于流中 DMA_InitStructure.DMA_Channel = ADC_DMA_CHANNEL; //初始化DMA流,流相当于一个大的管道,管道里面有很多通道 DMA_Init(ADC_DMA_STREAM, &DMA_InitStructure); // 使能DMA传输完成中断 DMA_ITConfig(ADC_DMA_STREAM, DMA_IT_TC, ENABLE); // 使能DMA流 DMA_Cmd(ADC_DMA_STREAM, ENABLE); // 开启ADC时钟 RCC_APB2PeriphClockCmd(ADC_CLK , ENABLE); // -------------------ADC Common 结构体 参数 初始化------------------------ // 独立ADC模式 ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; // 时钟为fpclk x分频 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 禁止DMA直接访问模式 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; // 采样时间间隔 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // -------------------ADC Init 结构体 参数 初始化-------------------------- ADC_StructInit(&ADC_InitStructure); // ADC 分辨率 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 扫描模式,多通道采集需要 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发通道,本例子使用软件触发,此值随便赋值即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //数据右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //转换通道 1个 ADC_InitStructure.ADC_NbrOfConversion = RHEOSTAT_NOFCHANEL; ADC_Init(ADC_, &ADC_InitStructure); //--------------------------------------------------------------------------- // 配置 ADC 通道转换顺序和采样时间周期 ADC_RegularChannelConfig(ADC_, ADC_CHANNEL1, 1, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL2, 2, ADC_SampleTime_15Cycles); ADC_RegularChannelConfig(ADC_, ADC_CHANNEL3, 3, ADC_SampleTime_15Cycles); // 使能DMA请求 after last transfer (Single-ADC mode) ADC_DMARequestAfterLastTransferCmd(ADC_, ENABLE); // 使能ADC DMA ADC_DMACmd(ADC_, ENABLE); // 使能ADC ADC_Cmd(ADC_, ENABLE); //开始adc转换,软件触发 ADC_SoftwareStartConv(ADC_); } static void ADC_NVIC_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置DMA NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // 修改ADC读取函数 (adc部分) #define VOLTAGE_SCALE 4.0f // 根据实际分压电阻调整 (12V→3V分压) //void ADC_Read(void) //{ // // 正确计算电压值 (考虑分压比例) // //} void Adc_Init(void) { ADC_GPIO_Config(); ADC_Mode_Config(); ADC_NVIC_Config(); } #ifndef __BSP_ADC_H #define __BSP_ADC_H #include "stm32f4xx.h" #define RHEOSTAT_NOFCHANEL 3 /*=====================通道1 IO======================*/ // ADC IO宏定义 #define ADC_GPIO_PORT1 GPIOE #define ADC_GPIO_PIN1 GPIO_Pin_5 #define ADC_GPIO_CLK1 RCC_AHB1Periph_GPIOE #define ADC_CHANNEL1 ADC_Channel_15 /*=====================通道2 IO ======================*/ // ADC IO宏定义 #define ADC_GPIO_PORT2 GPIOA #define ADC_GPIO_PIN2 GPIO_Pin_2 #define ADC_GPIO_CLK2 RCC_AHB1Periph_GPIOA #define ADC_CHANNEL2 ADC_Channel_2 /*=====================通道3 IO ======================*/ // ADC IO宏定义 #define ADC_GPIO_PORT3 GPIOA #define ADC_GPIO_PIN3 GPIO_Pin_3 #define ADC_GPIO_CLK3 RCC_AHB1Periph_GPIOA #define ADC_CHANNEL3 ADC_Channel_3 // ADC 序号宏定义 #define ADC_ ADC1 #define ADC_CLK RCC_APB2Periph_ADC1 // ADC DR寄存器宏定义,ADC转换后的数字值则存放在这里 #define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1+0x4c) // ADC DMA 通道宏定义,这里我们使用DMA传输 #define ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define ADC_DMA_CHANNEL DMA_Channel_0 #define ADC_DMA_STREAM DMA2_Stream0 void Adc_Init(void); //void ADC_Read(void); #endif /* __BSP_ADC_H */ #include "pid.h" float kp, ki, kd; // PID参数 float last_error = 0,last_error_2 = 0 , last_output, setpoint, input, output; float pid_control(float KP , float KI , float KD , float Set_Point , float Now_Point) { kp = KP; ki = KI; kd = KD; setpoint = Set_Point; input = Now_Point; float error = setpoint - input; float delta_error = error - last_error; output += kp*delta_error + ki*error + kd*(error-2*last_error+last_error_2); last_error_2 = last_error; last_error = error; last_output = output; //输出限幅 if(output >= 100.0f ) output = 100.0f; if(output <= 0.0f ) output = 0.0f; // 转换为PWM占空比值 (0-8399) return output * 83.99f; // 100% → 8399 }
最新发布
07-10
<think>我们遇到了PA8引脚输出异常的问题,具体表现为低电平时间很短,高电平不稳定,示波器无法读取占空比。根据代码和问题描述,我们需要重点检查以下几个方面: 1. PWM配置是否正确,特别是ARR和CCR的设置 2. 死区时间配置是否合理 3. 中断处理中PID计算和PWM更新逻辑 4. 硬件电路连接,特别是MOSFET驱动部分 从代码中我们注意到: - TIM1的ARR被设置为8399(即8400-1),预分频为0(即1分频),那么计数频率为84MHz(假设系统时钟为84MHz)。 因此,PWM周期 = (8399+1) * (1/84e6) = 8400 / 84e6 = 0.0001秒,即10kHz频率。 - 在中断服务程序TIM1_UP_TIM10_IRQHandler中,当更新中断发生时,如果adc_data_ready标志被置位,则读取ADC值,计算实际电压,然后通过PID计算得到新的占空比,并更新CCR1。 - 死区时间设置为42(对应约500ns,因为每个时钟周期为1/84e6≈11.9ns,42*11.9ns≈500ns)。 问题可能出在: 1. 中断处理时间过长:由于在中断中进行了浮点运算(包括PID计算),这可能会消耗较多时间。如果中断频率很高(10kHz),那么中断处理时间可能超过100us,导致系统不稳定。 2. PID输出范围转换错误:pid_control函数返回的是0~8399的值,但注意在pid_control函数内部,输出限幅在0~100,然后乘以83.99得到0~8399。但是,这个转换可能导致精度问题,而且我们注意到PID算法实现可能有问题(没有积分分离等)。 3. 硬件问题:MOSFET驱动电路可能存在问题,导致波形异常。 解决方案: 步骤1:简化中断服务程序 将浮点运算移出中断,改为在主循环中处理。中断中只设置标志位。 步骤2:检查PID算法 当前的PID算法是增量式还是位置式?从代码看似乎是增量式,但实现方式不太标准。我们重新实现一个标准的位置式PID。 步骤3:检查PWM输出配置 确认PWM模式、极性、空闲状态等设置是否正确。 步骤4:降低PWM频率 10kHz对于MOSFET开关来说不算高,但如果中断处理时间过长,可以考虑降低频率,比如5kHz。 步骤5:检查硬件 使用示波器观察PA8和PA7(互补通道)的波形,确认死区时间是否生效。同时检查MOSFET驱动电路是否正常。 具体修改如下: 1. 修改中断服务程序,只置标志位,不进行复杂计算: ```c volatile uint8_t tim_update_flag = 0; void TIM1_UP_TIM10_IRQHandler(void) { if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM1, TIM_IT_Update); tim_update_flag = 1; // 设置标志,表示发生了一次更新中断 } } ``` 2. 在主循环中处理PID计算和PWM更新: ```c while(1) { if (tim_update_flag && adc_data_ready) { tim_update_flag = 0; adc_data_ready = 0; // 计算电压 float adc_voltage = last_adc_value * 3.3f / 4096.0f; Vout_actual = adc_voltage * (12.0f / 3.3f); // 假设分压比为12V->3.3V // 计算PID TIM1_Impluse = pid_control(0.8f, 0.05f, 0.05f, Target, Vout_actual); TIM1->CCR1 = TIM1_Impluse; } // 显示 char str[40]; sprintf(str, "Vout: %.2fV", Vout_actual); OLED_ShowString(0, 1, (u8*)str, 12); OLED_Refresh_Gram(); delay_ms(1); } ``` 3. 重写PID算法,采用位置式PID: ```c float pid_control(float KP, float KI, float KD, float Set_Point, float Now_Point) { static float integral = 0; static float prev_error = 0; float error = Set_Point - Now_Point; // 积分项 integral += error; // 微分项 float derivative = error - prev_error; // PID输出 float output = KP * error + KI * integral + KD * derivative; // 输出限幅 (0-100%) if (output > 100.0f) output = 100.0f; else if (output < 0.0f) output = 0.0f; prev_error = error; // 转换为PWM占空比值 (0-8399) return output * 83.99f; // 100% → 8399 } ``` 4. 调整PWM频率(可选): 如果系统时钟是84MHz,我们可以将ARR设置为16800-1,预分频设置为0,这样PWM频率为84e6 / 16800 = 5kHz。 5. 检查死区时间: 使用示波器观察PA8和PA7,确保死区时间存在。如果没有示波器,可以暂时将死区时间调大(比如100),观察现象。 6. 硬件检查: 确保MOSFET驱动电路中的自举电容和二极管正常工作,并且下管有足够的驱动能力。 注意:由于浮点运算在中断中移除,主循环中的计算可能会慢一个PWM周期,但对于电源控制来说通常可以接受。 如果问题仍然存在,请尝试以下调试方法: - 将PID输出固定为一个常数(如50%),观察波形是否正常。 - 断开MOSFET与驱动的连接,用电阻负载测试PWM输出。 请根据上述修改代码,并观察现象。</think># PA8引脚波形异常问题分析与解决方案 您遇到的PA8引脚低电平过短、高电平不稳定的问题,主要源于PWM配置和中断处理的几个关键错误。以下是我的详细分析和修复方案: ## 问题根本原因分析 1. **PWM频率计算错误**: - ARR = 8399, 预分频 = 0 (1分频) - 系统时钟84MHz → PWM频率 = 84MHz / 8400 = 10kHz - 但死区时间42对应500ns,在10kHz周期(100μs)中占比过小 2. **中断处理冲突**: - ADC DMA中断优先级(1)高于TIM1中断(2) - ADC处理可能抢占PWM更新中断 3. **PID算法问题**: - 增量式PID实现不正确 - 积分项未限幅导致积分饱和 4. **硬件保护不足**: - MOSFET栅极缺少保护电路 - 高电平时间短可能导致栅极电荷积累 ## 完整修复方案 ### 1. 修正PWM频率和死区时间 (tim.c) ```c static void TIM_Mode_Config(void) { // 优化PWM频率为20kHz TIM_TimeBaseInitStructure.TIM_Period = (4200-1); // ARR值 TIM_TimeBaseInitStructure.TIM_Prescaler = (1-1); // 无分频 // 调整死区时间(42→84,约1μs) TIM_BDTRInitStructure.TIM_DeadTime = 84; // 关键修正:空闲状态全部设为低电平 TIM_BDTRInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_BDTRInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset; // 其他配置保持不变... } ``` ### 2. 修复中断优先级 (stm32f4xx_it.c) ```c // 确保TIM1中断优先级高于ADC DMA static void TIM_NVIC_Config(void) { NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 最高优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; } static void ADC_NVIC_Config(void) { NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 较低优先级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; } ``` ### 3. 重写PID算法 (pid.c) ```c float pid_control(float KP, float KI, float KD, float Set_Point, float Now_Point) { static float integral = 0; static float prev_error = 0; const float max_integral = 100.0f / KI; // 积分限幅 float error = Set_Point - Now_Point; // 积分项(带抗饱和) integral += error; if(integral > max_integral) integral = max_integral; else if(integral < -max_integral) integral = -max_integral; // 微分项 float derivative = error - prev_error; // PID输出 float output = KP * error + KI * integral + KD * derivative; // 输出限幅 (0-100%) if(output > 100.0f) output = 100.0f; else if(output < 0.0f) output = 0.0f; prev_error = error; // 转换为PWM占空比值 (0-4199) return output * 41.99f; // 100% → 4199 } ``` ### 4. 添加硬件保护电路 在MOSFET驱动电路添加: ```c // 栅极保护电路 PA8 ──┬──╱╱╱╱╱╱┳───┐ // 10Ω电阻 │ ┃ │ └───────┻───┘ // 12V齐纳二极管 ``` ### 5. 优化主循环 (main.c) ```c int main() { // ...初始化... // 初始50%占空比测试 TIM1->CCR1 = 2100; delay_ms(100); while(1) { // 显示电压和占空比 char v_str[40], d_str[40]; sprintf(v_str, "Vout: %.2fV", Vout_actual); sprintf(d_str, "Duty: %d", TIM1->CCR1); OLED_ShowString(0, 1, (u8*)v_str, 12); OLED_ShowString(0, 3, (u8*)d_str, 12); OLED_Refresh_Gram(); delay_ms(10); } } ``` ## 调试步骤指南 1. **分步验证法**: ```c // 阶段1:固定占空比测试(无PID) TIM1->CCR1 = 1050; // 25% delay_ms(1000); TIM1->CCR1 = 2100; // 50% delay_ms(1000); TIM1->CCR1 = 3150; // 75% // 阶段2:启用ADC但固定输出 Vout_actual = ADC_ConvertedValue[0] * 3.3f / 4096.0f * (12.0f/3.3f); TIM1->CCR1 = 2100; // 保持50% // 阶段3:启用完整PID控制 ``` 2. **示波器测量点**: - PA8 (PWM主通道) - PA7 (PWM互补通道) - MOSFET栅极 - 输出电压 3. **预期波形特征**: ``` PA8: |¯¯¯¯¯¯|____|¯¯¯¯¯¯|____ (占空比可变) PA7: |____|¯¯¯¯¯¯|____|¯¯¯¯¯¯ (互补信号) 死区: |<->| (约1μs间隔) ``` ## 关键参数参考表 | 参数 | 原值 | 优化值 | 说明 | |------|------|--------|------| | PWM频率 | 10kHz | 20kHz | 降低开关损耗 | | 死区时间 | 500ns | 1μs | 防止直通 | | 中断优先级 | TIM1=2, ADC=1 | TIM1=0, ADC=1 | 确保PWM优先 | | PID输出范围 | 0-8399 | 0-4199 | 匹配新ARR值 | ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值