发此帖,一是工作总结,二是经验分享。
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目录下四个程序文件。
4、Drivers文件夹留待以后存放自己编写的设备驱动,如电机,编码器,i2c设备,spi设备等。
添加完成后如下图所示:
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文件后面也会用得到。
五、配置属性:
选择时钟频率为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);
}
}
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);
}
}
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |