发此帖,一是工作总结,二是经验分享。
CONTENTS
已完成:
组装过程
没什么好多说的,比较简单,细心一点就好了。
电池及电机位置摆放:
视频地址:http://player.youku.com/player.php/sid/XMTI1NzM0MTQ0MA==/v.swf
FreeRTOS移植
开发环境: keil uVersion5 ,ST-link,USB转TTL。
去年玩四轴的时候用过FreeRTOS,觉得还算好用,今年继续用这个吧。RTOS,实时操作系统,有兴趣的可以去谷歌。以后的开发实验都在这个系统上进行了。
一、新建文件夹Balancer123,作为我们存放工程的目录,在里面新建lib,list,output,user四个文件夹,并新建一个空工程:
二、下载FreeRTOS压缩文件包,下载STM32库函数文件stsw-stm32054.zip(STM32F10x_StdPeriph_Lib_V3.5.0),并分别解压出FreeRTOSV8.2.1、STM32F10x_StdPeriph_Lib_V3.5.0两个文件夹。
三、将解压出来的外设库中STM32F10x_StdPeriph_Lib_V3.5.0\Libraries下两个文件夹拷至工程Lib目录,复制FreeRTOSV8.2.1\FreeRTOS\Source文件夹到工程下Lib文件夹,并重命名为FreeRTOS。
1复制\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template目录下stm32f10x_conf.h、stm32f10x_it.c、stm32f10x_it.h文件至\user\目录下。
2复制FreeRTOSV8.2.1\FreeRTOS\Demo\CORTEX_STM32F103_Keil目录下FreeRTOSConfig.h和main.c文件到工程文件夹的\user\目录下。
3复制Balancer123\lib\CMSIS\CM3\DeviceSupport\ST\STM32F10x目录下
stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h三个文件至\user\目录下。
4复制Balancer123\lib\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\arm目录下startup_stm32f10x_md.s文件到\user\目录下并改名为STM32F10x.s(该文件为系统的启动文件,适用于64K-128K Flash空间的芯片)。取消该STM32F10x.s文件只读属性,修改如下:
在 __heap_limit(位于50行) 段后添加
IMPORT xPortPendSVHandler
IMPORT xPortSysTickHandler
IMPORT vPortSVCHandler
__Vectors区段中断向量表将SVCall Handler、PendSV_Handler、SysTick Handler改由RTOS管理。直接注释掉旧的,添加新代码并保存。
;DCD SVC_Handler ; SVCall Handler
DCD vPortSVCHandler ; SVCall Handler
;DCD PendSV_Handler ; PendSV Handler
DCD xPortPendSVHandler ; PendSV Handler
;DCD SysTick_Handler ; SysTick Handler
DCD xPortSysTickHandler ; SysTick Handler
四、如下图进入工程项目管理菜单。
笔者简单分了四组,分别是user,lib,freertos,drivers。三组中分别添加文件,如下图:
1、User添加user目录下四个程序文件。
2、lib添加Balancer123\lib\STM32F10x_StdPeriph_Driver\src目录下所有c文件。
3、freertos添加\Balancer123\lib\FreeRTOS\目录下list.c,queue.c,task.c,timer.c四个文件及Balancer123\lib\FreeRTOS\portable\MemMang\目录下heap_2.c文件及Balancer123\lib\FreeRTOS\portable\RVDS\ARM_CM3\目录下port.c文件。最后一个目录下有个portmacro.h文件后面也会用得到。
4、Drivers文件夹留待以后存放自己编写的设备驱动,如电机,编码器,i2c设备,spi设备等。
添加完成后如下图所示:
五、配置属性:
选择时钟频率为72.0,选中右上角的“Use MicroLIB”复选框。
点击“Seletc Folder for Objects...”选择工程目录下创建的output文件夹,选中下面的“Create Hex File”复选框。
同上步一样选择listing目录为工程中的list文件夹。
定义“Preprocessor Symbols”,添加“USE_STDPERIPH_DRIVER,STM32F10X_MD”表示使用标准外设库并使用中等容量Flash的芯片。定义“IncludePaths”,添加需要用到的头文件的路径,如上所示就差不多了。记得添加Balancer123\lib\FreeRTOS\portable\RVDS\ARM_CM3目录到头文件目录,里面有portmacro.h文件需要用到。
笔者使用ST-link,设置右上角为“ST-Link Debugger”并点右侧“Settings”按钮配置,调出配置菜单。“Debug”页修改左侧菜单中的“ort:”改为“SW”,“Flash Download”页在“Download Functions”中选中“reset and Run”,这样程序下载到flash后自动运行。点击“Add”按钮添加Flash 编程方法,选128K的那项,即下载到芯片中的Flash中。如下图所示
六、编译程序
由于使用了其他工程的main.c文件,把用到的不必要的头文件,宏定义,其他功能或函数都删除。仅保留 static void prvSetupHardware( void )函数即可(该函数最后一行如果提示有错误可以删掉),该函数初始化对时钟等初始化。Main()函数删减到
int main( void )
{
prvSetupHardware();
/* Start the scheduler. */
vTaskStartScheduler();
/* Will only get here if there was not enough heap space to create the
idle task. */
return 0;
}
即可。编译一下,对可能出的错误按提示修改。如果没错误,一个空的工程建立成功了。
LED GPIO端口测试
LED端口定义如下:
驱动很简单,往PB8端口输出0后LED亮,输出1时LED灭,直接贴程序代码:
#include "led.h" #include "stm32f10x.h" #include <stdbool.h> /*FreeRtos includes*/ #include "FreeRTOS.h" #include "task.h" //Hardware configuration #define LED_GPIO_PERIF RCC_APB2Periph_GPIOB #define LED_GPIO_PORT GPIOB #define LED_GPIO_GREEN GPIO_Pin_8 #define LED_POL_GREEN LED_POL_NEG static bool isInit=false; void ledSet(bool value); //Initialize the green led pin as output void ledInit() { GPIO_InitTypeDef GPIO_InitStructure; if(isInit) return; // Enable GPIO RCC_APB2PeriphClockCmd(LED_GPIO_PERIF, ENABLE); //Initialize the LED pins as an output GPIO_InitStructure.GPIO_Pin = LED_GPIO_GREEN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); //Turn off the LED:s ledSet(0); isInit = true; } bool ledTest(void) { return isInit; } void ledSet(bool value) { if(value) GPIO_ResetBits(GPIOB, LED_GPIO_GREEN); else GPIO_SetBits(GPIOB, LED_GPIO_GREEN); } void vLEDTask( void *pvParameters ) { for( ;; ) { ledSet(true); vTaskDelay(500); ledSet(false); vTaskDelay(2000); } }
头文件很简单,就是对两个函数的声明。测试的时候只需在main函数中调用ledInit()函数并创建led任务即可以了。该任务功能是让led亮500ms,熄灭2000ms,循环执行。main函数如下:
int main( void ) { prvSetupHardware(); ledInit(); xTaskCreate(vLEDTask,"LED", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY+3, NULL ); /* Start the scheduler. */ vTaskStartScheduler(); /* Will only get here if there was not enough heap space to create the idle task. */ return 0; }
电机驱动实验
原理:通过STM32的定时器产生PWM信号,控制电机的转速。根据原理图知道,电机A接PB1,电机B接PB0。根据下表定义知道,用到了定时器TIM3的CH4和CH3。
直接贴程序,通过程序基本上就弄懂原理了。可以直接使用初始化的程序。
#include <stdbool.h> #include "stdio.h" /* ST includes */ #include "stm32f10x.h" #include "motors.h" //FreeRTOS includes #include "FreeRTOS.h" #include "task.h" // HW defines #define MOTORS_GPIO_TIM_PERIF RCC_APB1Periph_TIM3 #define MOTORS_GPIO_TIM_DBG DBGMCU_TIM3_STOP #define MOTORS_GPIO_TIM TIM3 #define MOTORS_GPIO_PERIF RCC_APB2Periph_GPIOB #define MOTORS_GPIO_PORT GPIOB #define MOTORS_GPIO_M1 GPIO_Pin_1 // TIM3_CH4 #define MOTORS_GPIO_M2 GPIO_Pin_0 // TIM3_CH3 #define MOTORS_TIM_PERIF RCC_APB1Periph_TIM3 #define M1_GPIO_IN1 GPIO_Pin_14 #define M1_GPIO_IN2 GPIO_Pin_15 #define M2_GPIO_IN1 GPIO_Pin_13 #define M2_GPIO_IN2 GPIO_Pin_12 const int MOTORS[] = { MOTOR_M1, MOTOR_M2}; static bool isInit = false; void TIM3_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; /* GPIOB clock enable */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); /*GPIOB Configuration: TIM3 channel 3 and 4 as alternate function push-pull */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = M1_GPIO_IN1 | M1_GPIO_IN2 | M2_GPIO_IN1 | M2_GPIO_IN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//stop jtag,enable swd } void TIM3_Mode_Config(void) { //Init structures TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //Timer configuration TIM_TimeBaseStructure.TIM_Period = MOTORS_PWM_PERIOD; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //PWM channels configuration (All identical!) TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //M1:TIM3_CH3N TIM_OC3Init(MOTORS_GPIO_TIM, &TIM_OCInitStructure); TIM_OC3PreloadConfig(MOTORS_GPIO_TIM, TIM_OCPreload_Enable); //M2:TIM3_CH4N TIM_OC4Init(MOTORS_GPIO_TIM, &TIM_OCInitStructure); TIM_OC4PreloadConfig(MOTORS_GPIO_TIM, TIM_OCPreload_Enable); //TIM_ARRPreloadConfig(TIM3,ENABLE); TIM_Cmd(TIM3,ENABLE); TIM_CtrlPWMOutputs(MOTORS_GPIO_TIM, ENABLE); DBGMCU_Config(MOTORS_GPIO_TIM_DBG, ENABLE); } void motor1SetDir(int ratio){ if(ratio < 0){ GPIO_ResetBits(GPIOB, M1_GPIO_IN1); GPIO_SetBits(GPIOB, M1_GPIO_IN2); return; } GPIO_ResetBits(GPIOB, M1_GPIO_IN2); GPIO_SetBits(GPIOB, M1_GPIO_IN1); return; } void motor2SetDir(int ratio){ if(ratio < 0){ GPIO_ResetBits(GPIOB, M1_GPIO_IN1); GPIO_SetBits(GPIOB, M1_GPIO_IN2); return; } GPIO_ResetBits(GPIOB, M2_GPIO_IN2); GPIO_SetBits(GPIOB, M2_GPIO_IN1); return; } /* Public functions */ //Initialization. Will set all motors ratio to 0% void motorsInit(void) { if (isInit) return; TIM3_GPIO_Config(); TIM3_Mode_Config(); isInit = true; } //-2047 <= ration <= 2047 void motorsSetRatio(int id, int ratio) { switch(id) { case MOTOR_M1: motor1SetDir(ratio); TIM_SetCompare4(TIM3, ratio); break; case MOTOR_M2: motor2SetDir(ratio); TIM_SetCompare3(TIM3, ratio); break; } } // FreeRTOS Task to test the Motors driver void motorsTestTask(void* params) { //Wait 3 seconds before starting the motors vTaskDelay(3000); motorsSetRatio(MOTOR_M1, 100); motorsSetRatio(MOTOR_M2, 100); while(1) { printf("This is in motorsTestTask\r\n"); vTaskDelay(1000); } }
编码器数据采集实验
电机驱动器将两组编码器输出信号分别接到PA0、PA1和PB6、PB7接口。如下图所示,用到TIM2 的CH1、CH2,TIM4的CH1、CH2。
直接贴代码,初始化的部分可以直接使用。不懂得找找百度谷歌,应该不太难。程序每半秒输出一次编码器的位置。头文件仅添加了两个public函数的声明。
#include <stdbool.h> #include "stdio.h" /* ST includes */ #include "stm32f10x.h" #include "encoder.h" //FreeRTOS includes #include "FreeRTOS.h" #include "task.h" #define ENCODER1_GPIO_PERIF RCC_APB2Periph_GPIOA #define ENCODER1_GPIO_PORT GPIOA #define ENCODER1_GPIO_A GPIO_Pin_0 #define ENCODER1_GPIO_B GPIO_Pin_1 #define ENCODER2_GPIO_PERIF RCC_APB2Periph_GPIOB #define ENCODER2_GPIO_PORT GPIOB #define ENCODER2_GPIO_A GPIO_Pin_6 #define ENCODER2_GPIO_B GPIO_Pin_7 #define ENCODER1_TIMER TIM2 #define ENCODER2_TIMER TIM4 void EncoderInit(void) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB2PeriphClockCmd(ENCODER1_GPIO_PERIF | ENCODER2_GPIO_PERIF,ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2 | RCC_APB1Periph_TIM4, ENABLE); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Pin = ENCODER1_GPIO_A | ENCODER1_GPIO_B; GPIO_Init(ENCODER1_GPIO_PORT,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = ENCODER2_GPIO_A | ENCODER2_GPIO_B; GPIO_Init(ENCODER2_GPIO_PORT,&GPIO_InitStructure); /* Timer configuration in Encoder mode */ TIM_DeInit(ENCODER1_TIMER); TIM_DeInit(ENCODER2_TIMER); TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseInit(ENCODER1_TIMER, &TIM_TimeBaseStructure); TIM_TimeBaseInit(ENCODER2_TIMER, &TIM_TimeBaseStructure); TIM_EncoderInterfaceConfig(ENCODER1_TIMER, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); TIM_EncoderInterfaceConfig(ENCODER2_TIMER, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising); //Reset counter ENCODER1_TIMER->CNT = 0; ENCODER2_TIMER->CNT = 0; TIM_Cmd(ENCODER1_TIMER, ENABLE); TIM_Cmd(ENCODER2_TIMER, ENABLE); } void vEncoderTestTask(void* params){ uint16_t counter1,counter2; while(1){ counter1 = TIM_GetCounter(ENCODER1_TIMER); counter2 = TIM_GetCounter(ENCODER2_TIMER); printf("Encoder counter %7d %7d\r\n",counter1,counter2); vTaskDelay(500); } }
ADC实验
该实验测量电池电压,是不可省略的一部分。由于ADC中有一项是测量STM32芯片内温度的,顺便求出温度。采用ADC1的DMA方式读取。DMA1选择通道1,如下图所示:
至于ADC1的通道选择,查下表:
原理图中通过分压后接PA4,因此选择ADC1的通道4,通道16,通道17:
直接贴代码,照着代码比较容易理解。一次采集三个数据,采集完16组数据并通过DMA传给数组后,产生一个中断。中断处理函数对数组中的值求均值作为ADC的结果。代码应该没问题,相关函数可以直接copy & paste使用。
#include "stm32f10x.h" #include "FreeRTOS.h" #include "task.h" #include "semphr.h" #include "adc.h" #include "uart1.h" #include "Nvicconf.h" #include "stdio.h" #define NVIC_ADC_PRI 12 // PORT A #define GPIO_VBAT GPIO_Pin_4 // CHANNELS #define CH_VBAT ADC_Channel_4 #define CH_VREF ADC_Channel_17 #define CH_TEMP ADC_Channel_16 #define ADC_DECIMATE_TO_BITS 12 #define ADC_MEAN_SIZE 16 #define ADC_RESOLUTION 12 #define ADC_DECIMATE_DIVEDEND (ADC_MEAN_SIZE) #define ADC_SAMPLING_FREQ 100 #define ADC_OVERSAMPLING_FREQ (ADC_SAMPLING_FREQ * ADC_MEAN_SIZE) #define ADC_TRIG_PRESCALE 1 #define ADC_TRIG_PRESCALE_FREQ (72000000 / (ADC_TRIG_PRESCALE + 1)) #define ADC_TRIG_PERIOD (ADC_TRIG_PRESCALE_FREQ / (ADC_OVERSAMPLING_FREQ)) #define ADC_INTERNAL_VREF 1.20 /******** Types ********/ /* typedef struct { uint16_t vbat; uint16_t vtemp; uint16_t vref; } AdcGroup; */ static bool isInit; volatile AdcGroup adcValues[ADC_MEAN_SIZE]; xQueueHandle adcQueue; static void adcDmaInit(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // DMA channel1 configuration DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adcValues; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 3*(ADC_MEAN_SIZE); 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_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); // Enable DMA channel1 // Enable the Transfer Complete Interrupt DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); DMA_Cmd(DMA1_Channel1, ENABLE); } /** * Decimates the adc samples after oversampling */ static void adcDecimate(AdcGroup* oversampled, AdcGroup* decimated) { uint32_t i; uint32_t sumVbat; uint32_t sumVtemp; uint32_t sumVref; AdcGroup *adcIterator; AdcGroup *adcOversampledGroup; // Compute sums and decimate adcIterator = oversampled; sumVbat = 0; sumVtemp = 0; sumVref = 0; for (i = 0; i < ADC_MEAN_SIZE; i++) { adcOversampledGroup = &((AdcGroup*)adcIterator)[i]; sumVbat += adcOversampledGroup->vbat; sumVtemp += adcOversampledGroup->vtemp; sumVref += adcOversampledGroup->vref; } // Decimate decimated->vbat = sumVbat / ADC_DECIMATE_DIVEDEND; decimated->vtemp = sumVtemp / ADC_DECIMATE_DIVEDEND; decimated->vref = sumVref / ADC_DECIMATE_DIVEDEND; } void adcInit(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; if(isInit) return; // Enable TIM2, GPIOA and ADC1 clock RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); GPIO_InitStructure.GPIO_Pin = GPIO_VBAT; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOB, &GPIO_InitStructure); // ADC1 configuration ADC_DeInit(ADC1); ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 3; ADC_Init(ADC1, &ADC_InitStructure); // ADC1 channel sequence ADC_RegularChannelConfig(ADC1, CH_VBAT, 1, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, CH_TEMP, 2, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, CH_VREF, 3, ADC_SampleTime_239Cycles5); // Enable ADC1 external trigger ADC_ExternalTrigConvCmd(ADC1, DISABLE); ADC_TempSensorVrefintCmd(ENABLE); adcDmaInit(); // Enable ADC1 DMA ADC_DMACmd(ADC1, ENABLE); // Enable ADC1 ADC_Cmd(ADC1, ENABLE); // Calibrate ADC1 ADC_ResetCalibration(ADC1); while(ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); // Enable the DMA1 channel1 Interrupt NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_ADC_PRI; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); adcQueue = xQueueCreate(1, sizeof(AdcGroup*)); printf("this is in adcInit\r\n"); isInit = true; } float adcConvertToVoltageFloat(uint16_t v, uint16_t vref) { return (v / (vref / ADC_INTERNAL_VREF)); } float adcConvertToTempFloat(uint16_t v, uint16_t vref) { return 25.0 + (1.43 - (v / (vref / ADC_INTERNAL_VREF)))*232.56; } void adcInterruptHandler(void) { portBASE_TYPE xHigherPriorityTaskWoken; AdcGroup* adcBuffer; if(DMA_GetITStatus(DMA1_IT_TC1)) { DMA_ClearITPendingBit(DMA1_IT_TC1); adcBuffer = (AdcGroup*)&adcValues[0]; xQueueSendFromISR(adcQueue, &adcBuffer, &xHigherPriorityTaskWoken); } } void vAdcTask(void *param) { AdcGroup* adcRawValues; AdcGroup adcValues; float vbat; float temp; vTaskDelay(1000); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while(1) { xQueueReceive(adcQueue, &adcRawValues, portMAX_DELAY); adcDecimate(adcRawValues, &adcValues); // 10% CPU vbat = adcConvertToVoltageFloat(adcValues.vbat,adcValues.vref); temp = adcConvertToTempFloat(adcValues.vtemp,adcValues.vref); printf("ADC %04x %04x %04x",adcValues.vbat,adcValues.vtemp,adcValues.vref); printf(" %.2f %.2f\r\n",vbat*7.73,temp); vTaskDelay(5000); } }
板载按键程序
板子上有个按键,连接到PB5,如图:
决定通过外部中断方式检测按键操作,如果按键被按下,PB5电平被拉低,产生一个下降沿。
因此,相关宏定义如下:
#define GPIO_PORT_KEY GPIOB
#define GPIO_KEY GPIO_Pin_5
#define KEY_GPIO_IRQ_SRC_PORT GPIO_PortSourceGPIOB
#define KEY_GPIO_IRQ_SRC GPIO_PinSource5
#define KEY_GPIO_IRQ_LINE EXTI_Line5
#define KEY_EXTI_IRQn EXTI9_5_IRQn
我们使用中断服务程序:EXTI9_5_IRQHandler。
程序比较简单,简单易懂。相关的初始化函数可以直接使用,keyTask函数每隔0.1秒输出一次值,该值由按键控制。
#include "key.h" #include "stdio.h" #include "FreeRTOS.h" #include "task.h" #include "uart1.h" #define NVIC_KEY_PRI 14 #define GPIO_PORT_KEY GPIOB #define GPIO_KEY GPIO_Pin_5 #define KEY_GPIO_IRQ_SRC_PORT GPIO_PortSourceGPIOB #define KEY_GPIO_IRQ_SRC GPIO_PinSource5 #define KEY_GPIO_IRQ_LINE EXTI_Line5 #define KEY_EXTI_IRQn EXTI9_5_IRQn static bool isInit; uint8_t key; void keyInit(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; key = 0x55; if(isInit) return; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); /* Configure PULL UP -------------------------*/ GPIO_InitStructure.GPIO_Pin = GPIO_KEY; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIO_PORT_KEY, &GPIO_InitStructure); GPIO_EXTILineConfig(KEY_GPIO_IRQ_SRC_PORT,KEY_GPIO_IRQ_SRC); EXTI_InitStructure.EXTI_Line = KEY_GPIO_IRQ_LINE; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); NVIC_InitStructure.NVIC_IRQChannel = KEY_EXTI_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = NVIC_KEY_PRI; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); isInit = true; } void keyInterruptHandler(void) { if(EXTI_GetITStatus(KEY_GPIO_IRQ_LINE) != RESET) { key = ~key; EXTI_ClearITPendingBit(KEY_GPIO_IRQ_LINE); } } void keyTask(void *param) { while(1) { vTaskDelay(100); printf("key1 : %x \r\n",key); } }
打赏帖 | |
---|---|
【Zephyr】MCXN947 Zephyr 开发入门适配shell被打赏20分 | |
【我要开发板】6.联合MATLAB记录数据被打赏50分 | |
【瑞萨RA2E1开发板】:使用ADC功能实现位移传感器采集方案被打赏20分 | |
【nRF7002DK】基于sht30的温湿度计被打赏20分 | |
【nRF7002DK】日志打印被打赏20分 | |
rtthread硬件加密-5hash加密分析被打赏10分 | |
【STM32F769】SD卡驱动及其调试经验分享被打赏32分 | |
【分享开发笔记,赚取电动螺丝刀】使用看门狗降低系统隐藏bug触发概率被打赏18分 | |
【STM32F769】调试SD驱动,由于其时钟配置不对引起的错误以及排查记录被打赏35分 | |
【分享开发笔记,赚取电动螺丝刀】MCUXpressoConfigTools配置外设时的异常解决被打赏24分 |