赞
踩
使用软件:STM32cubeMAx
开发板:野火挑战者开发板
通用输入输出接口
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);将引脚设置为读
void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);将引脚设置为写
void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);翻转引脚电平
程序实例:用按键控制LED亮灭
while (1)
{
/* USER CODE END WHILE */
if(HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_9)==GPIO_PIN_SET){
HAL_Delay(20);//消除延迟抖动
while(HAL_GPIO_ReadPin(GPIOI,GPIO_PIN_9)==GPIO_PIN_SET);
HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_6);
}
/* USER CODE BEGIN 3 */
}
启动流程分析
芯片上电->触发复位异常->跳转到中断向量表特定偏移位置->获取内容执行
分类:
同步通信:
一般情况下同步通信指的是通信双方根据同步信号进行通信的方式。比如通信双方有一个共同的时钟信号,通讯中通常双方会统一规定在时钟信号的上升沿或下降沿对数据线进行采样。(有时钟线)
异步通信:是指数据传输速度匹配依赖于通信双方有自己独立的系统时钟,大家约定好通信的速度。异步通信不需要同步信号,但是并不是说通信的过程不同布(无时钟线)。
串行通信:指的是同一时刻只能收或发一个bit位信息。因此只用1根信号线即可。
并行通信:指的是同一时刻可以收或发多个bit位的信息,因此需要多根信号线才行
单工:要么收,要么发,只能做接收设备或者发送设备。比如收音机
半双工:可以收,可以发,但是不能同时收发, 比如对讲机
全双工:可以在同一时刻既接收,又发送。 手机
2.串口通信协议介绍
控制寄存器——USART_CR
波特率寄存器——USART_BRR
状态寄存器——USART_SR
数据寄存器——USART_DR
控制状态寄存器:(中断,奇校验,发送接受使能,停止位)
波特率寄存器:控制波特率
状态寄存器:发送状态寄存器/读取数据寄存器是否为空,发送是否完成
数据寄存器:存放数据
寄存器:
void USART1_Putchar(uint8_t ch){
while(!USART1->SR & (1<<7));
USART1->DR=ch;
}
uint8_t USART1_Getchar(void){
while(!USART1->SR & (1<<5));
return USART1->DR;
}
HAL库:
printf重定义:
/* USER CODE BEGIN 0 */
int fputc(int ch,FILE* fp){
while(!(USART1->SR)& (1<<7));
USART1->DR=ch;
return ch;
}
/* USER CODE END 0 */
库函数使用:
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_UART_Transmit(&huart1,buf,32,100);
HAL_UART_Receive(&huart1,buf,32,100);
}
/* USER CODE END 3 */
处理器中断:即CPU在正常执行程序的过程中,遇到外部/内部的紧急事件需要处理,暂时中止当前程序的执行,转而去为处理紧急的事件,待处理完毕后再返回被打断的程序处继续往下执行
中断流程图:
{PC, xPSR, R0-R3, R12, LR}
STM32F4中断的体系结构
中断管理:
Cortex-M4 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有256 级的可编程中断优先级设置
当异常或中断发生时,处理器会把PC设置为一个特定地址,这一地址就称为异常向量。
中断向量表:存放中断函数的地址
中断和异常的区别:
中断是微处理器外部发送的,通过中断通道送入处理器内部,一般是硬件引起的,比如串口接收中断,而异常通常是微处理器内部发生的,大多是软件引起的,比如除法出错异常,特权调用异常等待。不管是中断还是异常,微处理器通常都有相应的中断/异常服务程序
嵌套中断:
中断优先级寄存器 NVIC_IPRx
IPR中的4 位,又分为抢占优先级和响应优先级
总结:
1.抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。同一时刻发生的中断,优先处理优先级较高的中断。
2.高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。
3.抢占优先级相同就看响应优先级,同样数值越小优先级越高。
4.如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。如果同时发生则优先处理编号较小的那个。
GPIO 口连接到 16 个外部中断 / 事件线
系统配置控制器 (SYSCFG):控制接入外部中断的gpio
外部中断流程图:
notes:uart每次中断过后都会中断清除使能寄存器,因此需要每次都要调用HAL_UART_Receive_IT,使能中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance==USART6){
printf("%c",(char)buf);
}
HAL_UART_Receive_IT(&huart6,&buf,1);//中断使能函数
}
1.时钟就是由振荡电路(RC LC和晶体振荡电路)构成
时钟形成过程:
2.振荡器
振荡器是用来产生重复电子讯号的电子元件。其构成的电路叫振荡电路,能将直流电转换为具有一定频率交流信号输出的电子电路或装置。
RC LC振荡器(电阻 电容构成):实现的成本比较低,电阻电容的精度问题所以RC振荡器的震荡频率会有误差,同时受到温度、湿度的影响
晶体振荡器:说震荡频率一般都比较稳定,同时精度也较高,成本较高
STM32时钟源:
HSI:高速内部时钟,RC振荡器,频率为16MHz;
HSE:高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~26MHz,我们开发板是25MHz
LSI:低速内部时钟,RC 振荡器,频率为 32kHz 左右。供独立看门狗和自动唤醒单元使用。
LSE:低速外部时钟,接频率为 32.768kHz 的石英晶体。这个主要是 RTC 的时钟源
时钟树:
主要关注重载数值寄存器和定时器(有相应的寄存器位控制,具体可以查看数据手册)
notes:重点是关注system clock的中断优先级,一般系统默认设置为0,0(最高级),如果想要在别的中断中调用delay函数,此中断优先级必须要比delay优先级低,否则会产生死锁。
__weak void HAL_Delay(uint32_t Delay)
{
uint32_t tickstart = HAL_GetTick();
uint32_t wait = Delay;
while((HAL_GetTick() - tickstart) < wait)
{
}
}
基本定时器:简单的定时计数功能
通用定时器:包含基本定时器所有功能,输入捕获、输出比较、pwm输出功能
高级定时器:包含通用定时器所有功能,拥有死区生成和互补输出,主要用于电机控制
定时器计数模式:
向上计数,向下计数,中央对齐
定时器时钟:
notes:要判断psc预分频的系数,决定输入到APB总线的时钟大小
定时器时基单元
时钟源(内部时钟),经过psc预分频器之后,驱动CNT计数,自动重装载计数器(ARR),当CNT的值达到这个值之后,就会产生事件或者中断。
中断计时公式: 1/(TIMxCLK/(PSC+1)) * (ARR+1)
HAL_TIM_Base_Start_IT(&htim2);//使能中断
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM2){
printf("time2 interupt\n");
}
}
/* USER CODE END 1 */
通用定时器引入通道概念(也就是拥有外部引脚输入信号),因此具备输入捕获和输出比较功能,高级定时器还额外拥有死区生成和互补输出的功能。
第一部分 时钟源:
拥有内部时钟输入(CK_INT,一般默认采用这种输入)
●外部时钟模式 1:外部输入引脚TIx(x=1,2,3,4)(用作定时器级联)
● 外部时钟模式 2:外部触发输入 ETR
● 外部触发输入 (ITRx):使用一个定时器作为另一定时器的预分频器,例如,可将定时器1 配置为定时器 2 的预分频器。
第二部分:控制器
高级控制定时器控制器部分包括触发控制器、从模式控制器以及编码器接口。触发控制器用来针对片内外设输出触发信号,比如为其它定时器提供时钟和触发 DAC/ADC 转换。编码器接口专门针对编码器计数而设计。从模式控制器可以控制计数器复位、启动、递增/递减、计数。
第三部分:时基单元
高级定时器额外拥有重复计数器,就是每次CNT计数器满足ARR寄存器的数值,会产生事件或中断,重复计数器可以将这个中断信号累加到一定值,才触发中断,也就是延长了中断的周期。
第四部分 输入捕获
外部通道接受到输入信号,经过滤波和边缘检测,触发一次中断,获取CCR寄存器的值,用来计算脉冲和周期。
第五部分 公共部分
捕获/比较寄存器(CCR),输入捕获和输出比较公用的寄存器部分,主要是存储存储触发中断读取cnt的数值
第六部分 输出比较
设置CCR寄存器的上限值来修改输出脉冲的宽度和周期。(pwm呼吸灯,舵机旋转)
互补输出和死区控制
/* USER CODE BEGIN 2 */ printf("this is time test\n"); HAL_TIM_Base_Start_IT(&htim2);//使能更新中断 HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);//使能捕获中断 /* USER CODE END 2 */ /******************************************************************************************/ /* USER CODE BEGIN 1 */ uint8_t rise_flag=0;//按键默认低电平,按下高电平 uint32_t capture_value=0; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM2){ if(rise_flag){//只有在按下按键之后更新中断才能计算更新周期 capture_value+=1000000; } } } void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){ if(htim->Instance==TIM2){ if(!rise_flag){//捕获到上升沿 rise_flag=1; HAL_Delay(20); if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_5)==GPIO_PIN_SET){ __HAL_TIM_DISABLE(htim); //关闭定时器 __HAL_TIM_SET_COUNTER(htim,0);//设置计数器值为0 TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //清除原来的设置 TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);//设置为下降沿捕获 __HAL_TIM_ENABLE(htim);//使能定时器 } }else{//下降沿,开始计算时间 rise_flag=0; //计数器的值+N*1000000+20000(延迟抖动) capture_value+=HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1)+20000;//读取计数器的值 printf("捕获时间为 %d秒,%d毫秒 %d微秒\n",capture_value/1000000,capture_value%1000000/1000,capture_value%1000); __HAL_TIM_DISABLE(htim); //关闭定时器 TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); //清除原来的设置 TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);//设置为下降沿捕获 __HAL_TIM_ENABLE(htim);//使能定时器 } } } /* USER CODE END 1 */
主要是设置通道为输出比较模式,让他输出pwm波形。我们可以调节他的占空比来控制其波形。
占空比:就是输出的PWM中,高电平保持的时间 与该PWM的时钟周期的时间之比
pwm模式:
PWM输出极性(可选择):
1、高电平有效
2、低电平有效
STM32cub3MAX配置
实验代码:
void set_compare(uint32_t value){ TIM5->CCR1=value; return; } HAL_TIM_PWM_Start(&htim5,TIM_CHANNEL_1);//使能PWM模式 /******************************************************************************/ /* USER CODE BEGIN WHILE */ while (1) { if(dir){ compare+=10; if(compare>=1000){ compare=1000-1; dir=0; } }else{ compare-=10; if(compare<=0){ compare=0; dir=1; } } set_compare(compare); HAL_Delay(20); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */
主要用于时钟计数,计数值到达设定数值之后,如果没有及时喂狗就会产生复位,重置系统。
独立看门狗:专用的内部低速时钟(LSI),计数为0 产生复位,对时间精度要求不高
窗口看门狗:总线APB1时钟,在规定的上下窗口中喂狗,对时间精度要求高
独立看门狗工作原理框图
关键字寄存器 (IWDG_KR)
预分频器寄存器 (IWDG_PR)
重载寄存器 (IWDG_RLR)
状态寄存器 (IWDG_SR)
主要函数:HAL_IWDG_Refresh
WWDG工作原理框图分析:
①窗口看门狗时钟
②计数器时钟
③计数器
④窗口值
使用时需要判断CFR寄存器W[0:6]的值(上窗口值),以及CR的T[0:6]的值
if(value<W[0:6] and value >T[0:6])可以refresh
ADC功能:是将外部的模拟信号转为为数字(电)信号,比如说:传感器的数据
外部的电压大小。将连续的模拟量的值转换为一定的比例电压的值。
ADC性能指标:量程、分辨率(精度,最高为12位),转换时间
总体框图:
1.输入电压范围:
设计原理图的时候一般把 V SSA 和 V REF- 接地,把 V REF+ 和 V DDA 接 3.3V,得到ADC 的输入电压范围为:0~3.3V.如果需要拓展范围的话,还需要设置一个电压转换电路
2.输入通道
0到15外部通道,还有额外的温度传感器,VREFIBT,VBT电源挂在
3、转换顺序
注入通道组:常规通道(16)
规则通道组:优先级较高(插入打断规则组,4个)
4、转换时间
Tconv = 采样时间 + 12个周期
5、数据寄存器
规则数据寄存器 ADC_DR,低16位存放数据,可以采用左对齐和右对齐(一般采用这种方式)方式
DMA:完成一个通道转换之后就会产生DMA中断
6、中断
转换结束中断
规则通道和注入通道的数据转换结束后,都可以产生中断
模拟看门狗中断
当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断
溢出中断
如果发生 DMA传输数据丢失,会置位 ADC 状态寄存器 ADC_SR的 OVR位,如果同时使能了溢出中断,那在转换结束后会产生一个溢出中断。
DMA 请求
规则和注入通道转换结束后,除了产生中断外,还可以产生 DMA请求,把转换好的数据直接存储在内存里面
7、触发源
软件触发(ADC控制存储器:ADC_CR)和外部事件触发
8、ADC工作模式
单次转换/连续转换,扫描(多通道)/非扫描,不连续采样模式(一般不使用)
配置:
while (1)
{
/*采用轮询方式*/
HAL_ADC_Start(&hadc1);//软件触发
if(HAL_ADC_PollForConversion(&hadc1, 100)==HAL_OK){//规则组通道采样完成
adc_value=HAL_ADC_GetValue(&hadc1);
printf("adc_value=%d\n",adc_value);
}
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
while (1) { /*采用中断方式*/ HAL_ADC_Start_IT(&hadc1); HAL_Delay(1000); } /*********************************************************************************/ extern uint32_t adc_value; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){ if(hadc->Instance==ADC1){ adc_value=HAL_ADC_GetValue(&hadc1); printf("adc_value=%d\n",adc_value); } } /* USER CODE END 1 */
while (1) { /*采用多通道方式*/ for(i=0;i<2;i++){ HAL_ADC_Start(&hadc1); if(HAL_ADC_PollForConversion(&hadc1,100)==HAL_OK){ adc_value=HAL_ADC_GetValue(&hadc1); printf("adc_channl %d value=%d\n",i+1,adc_value); } } HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }
概念:直接存储器访问,不需要通过cpu直接将数据发送指定位置,节省了cpu的资源。
1外设通道
2数据流仲裁
3数据FIFO
4存储器端口
5外设端口
1、外设通道
STM32具有两个DMA控制器,每个控制器具有8个数据流,每个数据流对应8个外设
2、数据流仲裁器
将数据流通道区分优先级,配置 DMA_SxCR寄存器 PL[1:0]位,可以设置为非常高、高、中和低四个级别
3、数据FIFO
数据缓存器,每个数据流都有一个4字节的数据缓存器。
DMA传输有直接模式(收到DMA请求就直接发送数据)和FIFO(数据达到FIFO的存储容量时发送数据)
4、5 存储器端口和外设端口
AHB存储端口(用于连接存储器),AHB外设端口(用于连接外设)
传输模式:存储器->外设,外设->存储器,存储器->存储器(DMA1除外)
传输的源,目的、长度:
源地址:存储器/外设地址
目的地址:外设/存储器地址
数据传输方向:存储器->外设,外设->存储器,存储器->存储器
写入的数据量:0~65536
源和目的地址的位宽(尽量保持一致,不一致需要使用FIFO模式)
增量设置:外设/存储器的指针是否自动递增
循环模式:根据外设模式来确定是否使用循环模式(如:adc使用了连续模式,则可以启用DMA循环模式,就不需要调用软件触发来使能中断)
单次传输和突发传输:
单次传输就正常传输;突发传输可以产生增量传输(比单次传输数据量大或相同),且数据不可分割,保持数据一致性具有很大作用
DMA中断:
配置:
案例:
DMA模式配置信息:
extern uint16_t buf[2]; void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){ if(hadc->Instance==ADC1){ printf("buf[0]=%d,buf[1]=%d",buf[0],buf[1]); } } /*******************************************************************************/ while (1) { HAL_ADC_Start_DMA(&hadc1,(uint32_t*)buf,2);//2表示两路数据,启动DMA中断请求 HAL_Delay(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。