使用工具:keil5.38a,调试器是使用的自制的DAP调试器;
一:GPIO口操作
首先看一下官方SDK里面的驱动文件
这里要注意一下啊,对于初学者或者是对汇编了解不是很多的同学来说,启动文件里面的驱动文件不要修改,否则程序可能会导致意想不到问题,我不小心修改了栈大小,导致程序在进入主函数之后,就直接进入了硬件错误。
基本上所有的开发板上面第一个功能介绍是如何创建工程,第二个就是简单介绍IO口的操作,而最直接的就是利用LED灯作为显示,这样能清晰的看到IO口的高低电平。
查看原理图:
整个电路板只有两个0603封装的LED灯,一个是用来指示电源电路是否正常,另外一个是故障指示灯(PC15),当IO口输出高电平时,指示灯熄灭,输出低电平时,指示灯亮起。
首先要初始化使用的IO口:
#define LEDn 2 #define LED2_PIN GPIO_PIN_15 #define LED2_GPIO_PORT GPIOC #define LED2_GPIO_CLK RCM_AHB_PERIPH_GPIOC /*GPIO口初始化函数*/ void GPIO_Init(void) { GPIO_Config_T gpioConfig; /*注意要使能PCIO口的时钟,要不然io口是不会工作的。*/ RCM_EnableAHBPeriphClock(LED2_GPIO_CLK | LED3_GPIO_CLK); //使能IO口的时钟 /* LED2 GPIO configuration */ gpioConfig.pin = LED2_PIN; //选择相对应的引脚1 gpioConfig.mode = GPIO_MODE_OUT; //选择引脚的输出模式 gpioConfig.outtype = GPIO_OUT_TYPE_PP; //选择引脚的输出模式 gpioConfig.speed = GPIO_SPEED_50MHz; //选择引脚的输出速度 gpioConfig.pupd = GPIO_PUPD_NO; GPIO_Config(LED2_GPIO_PORT, &gpioConfig); //初始化引脚 /* LED3 GPIO configuration */ gpioConfig.pin = LED3_PIN; //初始化使用的引脚2 GPIO_Config(LED3_GPIO_PORT, &gpioConfig); //初始化引脚 } /*系统延时函数*/ void APM_MINI_DelayMs(__IO uint32_t nms) { uint32_t temp; SysTick->LOAD = (uint32_t)nms * cntMs; SysTick->VAL = 0x00; SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; do { temp = SysTick->CTRL; } while ((temp & 0x01) && !(temp & (1 << 16))); SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; SysTick->VAL = 0x00; }
这里为了显示状态的改变时用了500ms的延时,当然本身这种延时函数是没有问题的,但是以后在工作中不要用类似的延时,很影响程序的运行。试验效果将在下面的视频中展示。
二:外部中断(EINT)简单调试使用
中断/事件分为内部中断/事件、外部中断/事件。在该手册中,外部中断指从 I/O引脚输入信号引起的中断/事件,在中断向量表中指 EINTx;其它中断指内部中断/事件。事件可分为硬件事件、软件事件。硬件事件是通过外部/内核硬件信号产生事件,软件事件是通过指令产生事件。中断需经过中断处理函数实现需要处理的工作;事件不需要经过中断处理函数,可硬件触发预先设置的工作。外部事件例如可通过事件是 GPIO 输出脉冲,内部事件例如通过一个 TMR 的更新事件触发另一个 TMR 工作。这里我们用板载的按键K1(PC14)和LED灯(PC15)两个IO口调试这个功能。
/*中断处理函数*/ void EINT_Init(void) { GPIO_Config_T gpioConfig; EINT_Config_T eintConfig; /* Enable the BUTTON Clock */ RCM_EnableAHBPeriphClock(KEY1_BUTTON_GPIO_CLK); //使能按键1的使用IO口的时钟线 RCM_EnableAHBPeriphClock(KEY2_BUTTON_GPIO_CLK); //使能按键2的使用IO口的时钟线 RCM_EnableAPB2PeriphClock(RCM_AHB_PERIPH_GPIOC); //使能LED灯的使用IO口的时钟线 /* Configure Button pin as input floating */ gpioConfig.mode = GPIO_MODE_IN; //设置中断输入口为输入模式 gpioConfig.pupd = GPIO_PUPD_PU; gpioConfig.pin = KEY1_BUTTON_PIN; //设置中断输入口1的端口号 GPIO_Config(KEY1_BUTTON_GPIO_PORT, &gpioConfig); //初始化按键1的IO 口 gpioConfig.pin = KEY2_BUTTON_PIN; //设置中断输入口2的端口号 GPIO_Config(KEY2_BUTTON_GPIO_PORT, &gpioConfig); //初始化按键1的IO 口 /* Configure GPIO pin used as EINT Line */ SYSCFG_EINTLine(SYSCFG_PORT_GPIOC, SYSCFG_PIN_14); //配置IO口的中断模式 SYSCFG_EINTLine(SYSCFG_PORT_GPIOA, SYSCFG_PIN_0); //配置IO口的中断模式 /* Configure Button exit line */ eintConfig.line = KEY1_BUTTON_EXTI_LINE; //配置IO口的中断线 eintConfig.lineCmd = ENABLE; //使能IO口的中断线 eintConfig.mode = EINT_MODE_INTERRUPT; //配置中断为输入模式 eintConfig.trigger = EINT_TRIGGER_FALLING; //配置中断为下降沿捕获 EINT_Config(&eintConfig); //初始化中断口1 eintConfig.line = KEY2_BUTTON_EXTI_LINE; EINT_Config(&eintConfig); //初始化中断口2 /* Configure NVIC_IRQRequest */ NVIC_EnableIRQRequest(EINT4_15_IRQn, 0x0F); //配置中断的优先级为0x0F }
三:定时器3的PWM输出功能调试
APM32的定时器资源是比较丰富的,由于板子上面只有将PA7引脚引出,查看手册PA7引脚是可以复用定时器3的复用1功能;资料如下图所示:
void APM_MINI_TMR1_PWMOutPut_Init(void) { TMR_TimeBase_T timeBaseConfig; TMR_OCConfig_T occonfig; GPIO_Config_T gpioconfig; /* Enable Clock*/ RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA); /* Connect TMR1 to CH1 */ RCM_EnableAPB1PeriphClock(RCM_APB2_PERIPH_SYSCFG); RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3); /* Connect TMR1 to CH1 */ GPIO_ConfigPinAF(GPIOA, GPIO_PIN_SOURCE_7, GPIO_AF_PIN1); gpioconfig.mode = GPIO_MODE_AF; gpioconfig.outtype = GPIO_OUT_TYPE_PP; gpioconfig.pin = GPIO_PIN_7; gpioconfig.pupd = GPIO_PUPD_NO; gpioconfig.speed = GPIO_SPEED_50MHz; GPIO_Config(GPIOA, &gpioconfig); /* Set clockDivision = 1 */ timeBaseConfig.clockDivision = TMR_CKD_DIV1; /* Up-counter */ timeBaseConfig.counterMode = TMR_COUNTER_MODE_UP; /* Set divider = 47 .So TMR1 clock freq ~= 48/(47+1) = 1MHZ */ timeBaseConfig.div = 47 ; /* Set counter = 1000 */ timeBaseConfig.period = 500-1; /* Repetition counter = 0x0 */ timeBaseConfig.repetitionCounter = 0; TMR_ConfigTimeBase(TMR3, &timeBaseConfig); /* PWM1 mode */ occonfig.OC_Mode = TMR_OC_MODE_PWM2; /* Idle State is reset */ occonfig.OC_Idlestate = TMR_OCIDLESTATE_RESET; /* NIdle State is reset */ occonfig.OC_NIdlestate = TMR_OCNIDLESTATE_RESET; /* Enable CH1N ouput */ occonfig.OC_OutputNState = TMR_OUTPUT_NSTATE_DISABLE; /* Enable CH1 ouput */ occonfig.OC_OutputState = TMR_OUTPUT_STATE_ENABLE; /* CH1 polarity is high */ occonfig.OC_Polarity = TMR_OC_POLARITY_HIGH; /* CH1N polarity is high */ occonfig.OC_NPolarity = TMR_OC_NPOLARITY_HIGH; /* Set compare value */ occonfig.Pulse = 250-1; TMR_OC2Config(TMR3, &occonfig); /* Enable PWM output */ TMR_EnablePWMOutputs(TMR3); /* Enable TMR1 */ TMR_Enable(TMR3); }
上述代码是根据例程进行修改。需要修改引脚配置,定时器3的时钟和PWM的输出函数,当所有配置完成后,最后再使能定时器3,实际效果如下图:输出脉冲计算公式:48MHZ/48 =1M;1M/500 = 2000 = 2K;占空比修改occonfig.Pulse该项参数;
具体定时时间很好计算,主频是120mhz,然后arr好prc是199和999,所以每次触发的时间就是1ms,之后是定时器的回调函数。
四:ADC调试过程:APM32内部ADC介绍:12 位精度的 ADC,共 19 个通道, 16 个外部通道和 3 个内部通道, 各通道 A/D转换模式有单次、连续和断续, ADC 转换结果可以左对齐或右对齐存储在 16 位数据寄存器中。
void ADC_Init(void) { ADC_Config_T adcConfig; GPIO_Config_T gpioConfig; /* RCM Enable*/ /* RCM Enable*/ RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_GPIOA); RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC); RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG); /* GPIO Configuration */ gpioConfig.pin = GPIO_PIN_7; gpioConfig.mode = GPIO_MODE_AN; gpioConfig.pupd = GPIO_PUPD_PU; GPIO_Config(GPIOA, &gpioConfig); /* ADC Configuration */ ADC_Reset(); ADC_ConfigStructInit(&adcConfig); /* Set resolution*/ adcConfig.resolution = ADC_RESOLUTION_12B; /* Set dataAlign*/ adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT; /* Set scanDir*/ adcConfig.scanDir = ADC_SCAN_DIR_UPWARD; /* Set convMode continous*/ adcConfig.convMode = ADC_CONVERSION_CONTINUOUS; /* Set extTrigConv*/ adcConfig.extTrigConv1 = ADC_EXT_TRIG_CONV_TRG7; /* Set TrigEdge*/ adcConfig.extTrigEdge1 = ADC_EXT_TRIG_EDGE_NONE; ADC_Config(&adcConfig); ADC_ConfigChannel(ADC_CHANNEL_7, ADC_SAMPLE_TIME_239_5); /* Enable Interrupt*/ ADC_EnableInterrupt(ADC_INT_CS); NVIC_EnableIRQRequest(ADC_COMP_IRQn, 2); /* Enable VREFINT*/ ADC_EnableVrefint(); /* Calibration*/ ADC_ReadCalibrationFactor(); /* Enable ADC*/ ADC_Enable(); } /*! * @brief ADC interrupt callback * * @param None * * @retval None * * @note This function need to put into ADC_COMP_IRQHandler in apm32f035_int.c */ void ADC_Isr(void) { uint32_t adcData = 0; float voltage = 0.0; if (ADC_ReadIntFlag(ADC_INT_FLAG_CS) == SET) { ADC_ClearIntFlag(ADC_INT_FLAG_CS); /* Read ADC Conversion value */ adcData = ADC_ReadConversionValue(); voltage = ((float)adcData / 4095) * 3300; /* output to serial port */ printf("Vref voltage : %.3f mV\r\n", voltage); APM_MINI_LEDToggle(LED2); APM_MINI_DelayMs(500); } }
初始化ADC,并配置PA7为输入模式,更改中断号就可以正常调试;但是内部的ADC调试的时候会存在误差的,我在调试的时候发现,内部AD到不了满值,