STM32F030F4P+PWM复用+DMA+ADC基本配置

STM32F030系列单片机介绍

这里主要介绍的就是单品机是48M的,使用了,IO口控制,PWM复用,DMA,ADC等等。如何需要修改主频可以参考:标准库配置

PWM复用

111通过查找手册可以看出来PA6要使用TIM3—CH1来做PWM是要复用的

/*************************************************
Function: 		PWM_config
Description: 	tim3定时器通道一  PA6 输出PWM波
Input: 				 
Output: 			 
Return: 		 
*************************************************/
void PWM_config(uint16_t arr , uint16_t psc)
{
		
		GPIO_InitTypeDef GPIO_Motorinit;//配置GPIO结构体初始化
		TIM_TimeBaseInitTypeDef TIM_Motorinit;//配置定时器结构体初始化
		TIM_OCInitTypeDef TIMPWM_Motorinit;//配置舵机定时器结构体初始化
		NVIC_InitTypeDef         NVIC_InitStructure;


		//1.打开时钟
		RCC_APB2PeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);//配置GPIO时钟
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//配置定时器时钟
		GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_1); //GC6 复用为 TIM3	



		NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;//TIM2中断
		NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
		NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道被使能
		NVIC_Init(&NVIC_InitStructure);

		GPIO_Motorinit.GPIO_Pin =GPIO_Pin_6;	
		GPIO_Motorinit.GPIO_Mode = GPIO_Mode_AF;				
		GPIO_Motorinit.GPIO_OType = GPIO_OType_PP;	
		GPIO_Motorinit.GPIO_PuPd = GPIO_PuPd_UP; 
		GPIO_Motorinit.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA,&GPIO_Motorinit);//配置GPIO初始化函数




		//3.配置通用定时器结构体
		TIM_Motorinit.TIM_ClockDivision  =  TIM_CKD_DIV1;//设置时钟不分频
		TIM_Motorinit.TIM_CounterMode    =  TIM_CounterMode_Up;//设置计数方式为向上计数
		TIM_Motorinit.TIM_Period         =  arr-1;//设置在下一个更新事件装入活动的自动重装载值为199
		TIM_Motorinit.TIM_Prescaler      =  psc-1;//TIM时钟频率预分频值为7199

		TIM_TimeBaseInit(TIM3,&TIM_Motorinit);	//配置定时器初始化函数

		//4.配置定时器输出PWM结构体
		TIMPWM_Motorinit.TIM_OCMode      = TIM_OCMode_PWM1;//配置PWM定时器模式为1
		TIMPWM_Motorinit.TIM_OutputState = TIM_OutputState_Enable;//使能PWM比较输出
		TIMPWM_Motorinit.TIM_OCPolarity  = TIM_OCNPolarity_Low;//选择有效输出的极性

		TIM_OC1Init(TIM3,&TIMPWM_Motorinit);//配置初始化函数
		TIM_Cmd(TIM3,ENABLE);//使能PWM输出
		TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);//自动加载的预装载寄存器使能

	
}

1.主要注意的是配置的时候IO口输出模式是复用:
GPIO_Motorinit.GPIO_Mode = GPIO_Mode_AF;
不要不会有PWM波形的
2.主要**注意*的是在配置引脚复用时要选择号对应的复用线:
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_1);
查手册可知

DMA+ADC

在之前使用的过程中对ADC接触不多,使用的多数也是单路ADC。但需要使用两路或多路ADC的时候就需要DMA传输了。本人也不是很懂DMA,也是站在巨人的肩膀上而已。

#define ADC1_DR_Address                0x40012440
 
 
uint16_t RegularConvData_Tab[4];
 
 /**
  * @file   ADC_Configuration
  * @brief  完成ADC和DMA的初始化
  * @param  无
  * @retval 无
  */
	
	
void ADC_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	ADC_InitTypeDef ADC_InitStructure;
	DMA_InitTypeDef     DMA_InitStructure;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);


	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 , ENABLE);	

	RCC_ADCCLKConfig(RCC_HCLK_Div4);   //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M


	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 ;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
	GPIO_Init(GPIOA, &GPIO_InitStructure);   

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
	GPIO_Init(GPIOB, &GPIO_InitStructure); 

	ADC_StructInit(&ADC_InitStructure);//初始化ADC结构
	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//12位精度
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //规定模式装换工作在连续模式
	ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; 
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据对其为右对齐
	ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward; //ADC的扫描方向


	DMA_DeInit(DMA1_Channel1);	/* DMA1 Channel1 Config */
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设地址		
	//		DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;//外设地址
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RegularConvData_Tab;//内存地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为数据传输的来源
	DMA_InitStructure.DMA_BufferSize = 2;//采集2个通道
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器不变
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为16位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA_Priority设定DMA通道x的软件优先级
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);	  
	DMA_Cmd(DMA1_Channel1, ENABLE);/* DMA1 Channel1 enable */


	ADC_GetCalibrationFactor(ADC1);

	ADC_Init(ADC1, &ADC_InitStructure); 

	/* Convert the ADC1 Channel 0 with 55.5 Cycles as sampling time */ 
	ADC_ChannelConfig(ADC1, ADC_Channel_5 , ADC_SampleTime_55_5Cycles);//ADC_SampleTime_55_5Cycles);   
	ADC_ChannelConfig(ADC1, ADC_Channel_9 , ADC_SampleTime_55_5Cycles);//ADC_SampleTime_55_5Cycles);   

	/* ADC Calibration */
	ADC_GetCalibrationFactor(ADC1);


	ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); /* Enable ADC_DMA */	
	ADC_DMACmd(ADC1, ENABLE); 

	/* Enable ADCperipheral[PerIdx] */
	ADC_Cmd(ADC1, ENABLE);     

	while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN)); /* Wait the ADCEN falg */
	ADC_StartOfConversion(ADC1); /* ADC1 regular Software Start Conv */ 
}

主要解释一下这两行外设地址配置:
1.DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; //外设地址
2. #define ADC1_DR_Address 0x40012440
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;//外设地址
两种配置都能使用。推荐使用第一种。第二种可能需要查手册。
在解释一下我当初想不明白的数据在哪里:
uint16_t RegularConvData_Tab [4];
数据储存在定义的一维数组里面。不是像单路ADC那样调用库转换。后面转换使用在main里面介绍。

main函数介绍

__commom_data Commom_Data;


	
float ADC_V_Value= 0;
float ADC_I_Value= 0;

/**
  * @file   main
  * @brief  Main program.
  * @param  None
  * @retval None
  */
int main(void)
{
	
	static uint32_t timecnt = 0;
	static uint32_t toggle = 0;
	uint32_t i = 0;
	
	Commom_Data.PWM_Vlaue_led = 50;
		
	LED_GPIO_Config();//初始化LED
	
	TIM2_Config();

	ADC_Configuration();

	USART1_Config();

	PWM_config(100,480);
	

    while (1)
    {
		
			TIM_SetCompare1(TIM3,Commom_Data.PWM_Vlaue_led);
		
			ADC_V_Value= (3.3/4096) * RegularConvData_Tab[0]*13;
			ADC_I_Value= ((3.3/4096) * RegularConvData_Tab[1])/0.75;	

			key_Control();


		

					if(Get_ms() - timecnt > 250)
					{
						 timecnt = Get_ms();
						
						 toggle = ~toggle;
						
						 if(toggle)
							 GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);
						 else
							GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);	 		 
							 
					}
	
    }
}

主要解释的几个数据使用和转换:
1.TIM_SetCompare1(TIM3,Commom_Data.PWM_Vlaue_led);
2.ADC_V_Value= (3.3/4096) * RegularConvData_Tab[0]*13;
3.ADC_I_Value= ((3.3/4096) * RegularConvData_Tab[1])/0.75;

1.就是在循环里不停调用的PWM配置函数。
2.为什么使用3.3V进行转换要看硬件参考的电压是多少,我这边的硬件参考电压是3.3V的。
4096:2的12次方就是4096使用的也是12位的ADC
12位的ADC 最大的数字量是4096, 那么ADC输出值只能在0~4096之间.
注意:电压电流的乘和除的系数要根据外围硬件电路来进行使用。

写在最后 一种很好又简单理解的任务调度使用(基于定时器)

在介绍一种基于定时器的任务调度:


static uint32_t Tim_ms_i=0,Tim_s_i=0 ;

uint16_t Time2;

 /**
  * @file   TIM2_Config
  * @brief  调用函数库,初始化定时器2的配置
  * @param  无
  * @retval 无
  */
void TIM2_Config(void)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    NVIC_InitTypeDef         NVIC_InitStructure;
    //使能TIM2时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 
	
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);
    NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
	
    //定时器定时时间T计算公式:T=(TIM_Period+1)*(TIM_Prescaler+1)/TIMxCLK
    TIM_TimeBaseStructure.TIM_Period = (10-1);//自动重装载值10--定时时间(10*4800/48M)s 
    TIM_TimeBaseStructure.TIM_Prescaler =(2400-1);//预分频值,+1为分频系数 
    TIM_TimeBaseStructure.TIM_ClockDivision =0; 
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;	//TIM向上计数模式
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);	//使能TIM2中断源
    TIM_Cmd(TIM2, ENABLE);  	//使能TIMx外设						 	
}
/*************************************************
Function: 		uiGet_ms
Description: 	获取当前ms数值
Input: 				 
Output: 			 
Return: 		 
*************************************************/
uint32_t Get_ms(void)
{
	return Tim_ms_i;
}
/*************************************************
Function: 		uiGet_s
Description: 	获取当前s数值
Input: 				 
Output: 			 
Return: 		 
*************************************************/
uint32_t Get_s(void)
{
	return Tim_s_i;
}

 /**
  * @file   TIM2_IRQHandler
  * @brief  定时器2中断处理函数
  * @param  无
  * @retval 无
  */
void TIM2_IRQHandler(void)
{	
	
		static uint32_t count_i = 0;

    if ( TIM_GetITStatus(TIM2,TIM_IT_Update) != RESET ) //是否发生中断
    {	
        TIM_ClearITPendingBit(TIM2 , TIM_FLAG_Update);    //清除中断待处理位
        Time2--;
			
				Tim_ms_i++;
		if(count_i%1000==0)//读取s
			Tim_s_i++;
		
		
				/****end*****/
		count_i++;
		if(count_i>=60000)//公约数
			count_i=0;
			
			
    }		 
}

先配置好定时器,在使定时器1ms更新一次。 在通过这种的使用方式进行不同的时间使用不同的函数。

					if(Get_ms() - timecnt > 250)
					{
						 timecnt = Get_ms();
						
						 toggle = ~toggle;
						
						 if(toggle)
							 GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);
						 else
							GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);	 		 
							 
					}

在后续的使用中还是发现了问题,定时时间因为公约数(60000)和u16类型(65535)的限制。在使用超过数据限制的时间是会出现问题。解决发现也是有的在调用前及时的清零。也可以使用u32的类型进行扩大使用范围。

个人学习记录,有错误欢迎指正。

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
STM32F4可以通过TIM PWM DMA来控制WS2812灯带。 在使用STM32F4来控制WS2812灯带之前,我们需要了解一些基本的原理。WS2812灯带是一种基于Neopixel技术的RGB LED灯带,它具有灯珠之间串行通信的特点。通过发送一系列的0和1的数据信号,可以控制每个灯珠的颜色与亮度。 首先,我们需要配置STM32F4的GPIO引脚作为TIM输出模式,选择PWM模式,并配置DMA进行数据传输。接着,我们需要设置TIM的周期和预分频系数,以控制PWM信号的频率和占空比。根据WS2812的通信协议,每个数据位以50%占空比的PWM信号来表示,其中逻辑0和逻辑1的时间分别为400ns和800ns。 然后,我们通过DMA传输具有正确占空比的PWM数据到GPIO引脚,以控制WS2812灯带。我们可以使用定时器计数器的更新事件作为触发源,通过TIM的DMA请求信号来触发DMA传输。 我们可以通过编写相应的代码来配置STM32F4的定时器和DMA。首先,我们需要定义一个数据缓冲区,将要传输的PWM数据写入缓冲区中。然后,我们配置DMA的传输长度、传输方向和传输模式。接着,我们配置定时器的PWM模式、周期和预分频系数。最后,我们启动定时器和DMA,并等待传输完成。 以上就是利用STM32F4的TIM PWM DMA来控制WS2812灯带的简要介绍。通过正确配置定时器和DMA,我们可以实现高效、准确地控制WS2812灯带的颜色和亮度,从而实现丰富多彩的灯光效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值