?
目录
1. 设置GPIO:
?2. printf重定向
3. Log信息格式
4.?个性化输出
5. CubeMX+HAL打开串口中断
?6.?打开外部中断
7.?时钟树基本操作
8.?通用定时器配置
9. CubeMX配置定时器
(1)将定时器的时钟设为72M
?(2)选择内部时钟
?(3)配置定时器
?(4)开启中断
?定时器中断回调函数:(平滑滤波)
10.?CubeMX配置pwm
11.?CubeMX配置spwm
12.?CubeMX配置ADC
(1)轮询
(2)DMA方式
13. IIC和SPI
STM32系列视频(CubeMX+MDK5+HAL库+库函数一站式学习)_哔哩哔哩_bilibili
1. 设置GPIO:
(1)将PA8设置为输出,推挽上拉,初始状态为高电平:
?(2)使用pin?configuration里边的User Label,可以生成对应的宏,提高代码的复用性,并且方便移植。
?在对应的工程代码里就会有如下所示的宏定义:
?2. printf重定向
int fputc(int ch, FILE *f)
{
uint8_t temp[1] = {ch};
HAL_UART_Transmit(&huart1, temp, 1, 2);
return ch;
}
3. Log信息格式
参考目前主流嵌入式、安卓等输出方式:
[日志级别] 文件名 : 日志信息
//例: [info] main.c : init ok!
//例: [debug] adc.c : adc_getvalue -> 3.3v
(1)可以使用条件编译的方式来决定是否打印Log信息:
#define Log 1 //不想打印时改为 0 即可
#if Log
printf("[info]main.c:init!\r\n");
#endif
(2)可变参数宏
#define USER_MAIN_DEBUG
#ifdef USER_MAIN_DEBUG
#define user_main_printf(format, ...) printf( format "\r\n",##__VA_ARGS__)
#define user_main_info(format, ...) printf("【main】info:" format "\r\n",##__VA_ARGS__)
#define user_main_debug(format, ...) printf("【main】debug:" format "\r\n",##__VA_ARGS__)
#define user_main_error(format, ...) printf("【main】error:" format "\r\n",##__VA_ARGS__)
#else
#define user_main_printf(format, ...)
#define user_main_info(format, ...)
#define user_main_debug(format, ...)
#define user_main_error(format, ...)
#endif
当我需要打印串口信息时,define一个USER_MAIN_DEBUG,在我不需要时就将其注释。
4.?个性化输出
可以借助这个网站来设计字符:http://patorjk.com/software/taag/
printf(".__ .__ .__ .__ .___");
printf("| |__ ____ | | | | ______ _ _____________| | __| _/");
printf("| | \\_/ __ \\| | | | / _ \\ \\/ \\/ / _ _ __ \\ | / __ | ");
printf("| Y \\ ___/| |_| |_( <_> ) ( <_> ) | \\/ |__/ /_/ | ");
printf("|___| /\\___ >____/____/\\____/ \\/\\_/ \\____/|__| |____/\\____ |");
printf(" \\/ \\/ \\/");
5. CubeMX+HAL打开串口中断
?
?6.?打开外部中断
另外,EXTI外部中断的GPIO配置如下:默认下拉,上升沿触发。
然后编写外部中断的回调函数即可。
eg:测量pwm的频率。
1s的时间内,上升沿的次数就是频率。也就是说,1s内进入外部中断的次数就是频率。每进一次中断,pwm_value加1,1s后它的值就是频率。
当有上升沿时,就进入外部中断将pwm_value的值加1。
int pwm_value = 0;
int main()
{
pwm_value = 0;
HAL_Delay(1000); # 延时1s后得到的pwm_value就是频率值
printf("[\tmain]info:pwm_value = %d\r\n", pwm_value);
}
# 外部中断的回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == PWM_Pin) # 判断触发的引脚是否是定义的引脚
{
pwm_value++;
}
}
7.?时钟树基本操作
(1)使能外部时钟源
一般情况下,生成的工程默认用的是内部时钟,所以先进行外部时钟的使能。
?(2)然后将HCLK时钟频率调到最大,例如f103最大是72M,而f406最大是84M。
?(3)最后,针对不同的外设进行按需分频。
8.?通用定时器配置
eg:特定时间翻转LED。
# 溢出时间
Tout = (arr+1)*(psc+1)/Tclk
频率:Tclk/(psc+1) MHz
void Timer3_Init(uint16_t arr,uint16_t psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_Trigger,ENABLE); //定时器中断使能
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //先占优先级1级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx外设
}
void TIM3_IRQHandler(void) //TIM3中断
{
static uint8_t cnt = 0; //静态变量
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
/*中断操作*/
cnt++;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
}
if(cnt == 10)
{
LED = ~LED;
cnt = 0;
}
}
9. CubeMX配置定时器
(1)将定时器的时钟设为72M
?(2)选择内部时钟
?(3)配置定时器
主要配置:定时时间,以及是否重装定时器。
定时频率 =?定时器时钟/(预分频 + 1)/(计数值 + 1)Hz。
定时时间 = 1/定时频率? s。
?(4)开启中断
基本定时器:
高级定时器:
?定时器中断回调函数:(平滑滤波)
eg:平滑滤波 -- 让采样值在另一个“线程”一直滤波,而在需要它的时候,直接取它的值即可。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == htim1.Instance)
{
pwm_sum += pwm_value * 10; //pwm_sum累加
pwm_sum -= pwm_avg; //pwm_sum减去上次的平均值
pwm_avg = pwm_sum * 1.0 / 5; //更新pwm的平均值
pwm_value_final = pwm_avg; //pwm_value_final的值即为当前pwm的频率
pwm_value = 0; //将pwm_value清空,重新计数
}
}
10.?CubeMX配置pwm
(1)使能PWM通道
?这里,将TIM2的Channel1设置为PWM输出通道。
(PWM Generation CHx 正向、PWM Generation CHxN 反向、 PWM Generation CHx CHxN 一对互补 pwm 输出)
(2)配置频率及占空比
频率 =?定时器时钟 / (Prescaler + 1) / (Cound Period + 1) Hz
占空比 = Pulse(对比值) / (Cound Period 计数值) %
?(3)下属代码实现了pwm占空比逐渐增大至1,然后变为0的一个过程。
int main(void)
{
int pwm = 0;
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM1_Init();
# 使能tim1的通道1
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
while (1)
{
HAL_Delay(5);
# 修改tim1的通道1的pwm比较值为pwm++,即修改占空比
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pwm++);
pwm = (pwm > 999) ? 0:pwm;
}
}
11.?CubeMX配置spwm
SPWM就是在PWM的基础上,让PWM的占空比做正弦变化。
STM32系列视频(CubeMX+MDK5+HAL库+库函数一站式学习)_哔哩哔哩_bilibili
略。
12.?CubeMX配置ADC
通常是12位,精度为3.3/4096?v。
读取ADC的方式:轮询、DMA。
(1)轮询
首先配置ADC1的CH0,并且使能连续采集。
while(1)
{
HAL_ADC_Start(&hadc1);//启动ADC装换
HAL_ADC_PollForConversion(&hadc1, 50);//等待转换完成, 第二个参数表示超时时间,单位ms.
if(HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC))
{
AD_Value = HAL_ADC_GetValue(&hadc1);//读取ADC转换数据, 数据为12位
printf("[\tmain]info:v=%.1fmv\r\n",AD_Value*3300.0/4096);//打印日志
}
}
(2)DMA方式
①?初始化两个ADC通道
?②?配置
使能连续转换模式(Continuous Conversion Mode);
ADC规则组选择转换通道数为2(Number Of Conversion);
配置Rank的输入通道,分别转换channel 0和channel 1。
③?添加DMA
设置为连续传输模式,数据长度为字。
?④?示例代码
数组偶数下标的数据为通道0采集数据,数组奇数下标的数据为通道1采集数据。
程序中将数组偶数下标数据加起来求平均值,实现均值滤波的功能,再将数据转换为电压值,即为PA0管脚的电压值。另一个通道同理。
uint32_t ADC_Value[100]; //数据缓存数组
uint8_t i;
uint32_t ad1, ad2; //ad1存储PA0的电压值;ad2存储PA1的电压值
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM1_Init();
MX_DMA_Init();
MX_ADC1_Init();
HAL_TIM_Base_Start_IT(&htim1);
# 以DMA方式开启ADC转换。第二个参数是数据存储起始地址,第三个参数是DMA传输数据的长度。
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)&ADC_Value, 100);
while (1)
{
HAL_Delay(500);
for(i = 0,ad1 =0,ad2=0; i < 100;)
{
ad1 += ADC_Value[i++];
ad2 += ADC_Value[i++];
}
ad1 /= 50;
ad2 /= 50;
printf("\r\n********ADC-DMA-Example********\r\n");
printf("[\tmain]info:AD1_value=%1.3fV\r\n", ad1*3.3f/4096);
printf("[\tmain]info:AD2_value=%1.3fV\r\n", ad2*3.3f/4096);
}
13. IIC和SPI
|