【第10讲 新建工程模板-基于固件库】20170315
1,MDK中源文件显示为乱码,解决方法如下:
1)点击菜单栏Edit按钮,在其下拉选项中选择点击Configuration
2)在默认弹出的Editor对话框中,看到“General Editor Setting”版块,在Encoding下拉选项框中
选择合适的字符显示形式即可。
【第13-15讲 GPIO】20170321
0,GPIO的配置:
GPIO_Mode_AIN = 0x0, //模拟输入
GPIO_Mode_IN_FLOATING = 0x04, //浮空输入
GPIO_Mode_IPD = 0x28, //下拉输入
GPIO_Mode_IPU = 0x48, //上拉输入
GPIO_Mode_Out_OD = 0x14, //开漏输出
GPIO_Mode_Out_PP = 0x10, //通用推挽输出
GPIO_Mode_AF_OD = 0x1C, //复用开漏输出
GPIO_Mode_AF_PP = 0x18 //复用推挽输出
1,SYSTEM路径下sys.h文件中拥有资源:
GPIO位操作的封装接口宏定义
2,SYSTEM路径下delay.h文件中拥有资源:
常用延时函数的初始化、可调用函数
3,外设时钟使能函数:
FWLIB路径下stm32f10x_rcc.c
1)IO时钟使能函数void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
【第17讲 按键输入实验-GPIO做输入】20170321
1,读取IO口输入电平
1)调用库函数:
uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
2)直接读取IO口输入电平寄存器:
GPIOx->IDR
3)使用位带操作读取:
PXin(n) 读取GPIOX.n口电平
2,按键输入流程设置:
1)使能IO口时钟RCC_APB2PeriphClockCmd()
2)初始化IO模式
3)电平检测
【第19讲 STM32时钟系统精讲】20170322
1,时钟使能寄存器:
typedef struct
{
__IO uint32_t CR; //HSI,HSE,CSS,PLL等的使能和就绪标志位
__IO uint32_t CFGR; //PLL等的时钟源选择,分频系数设定
__IO uint32_t CIR; // 清除/使能 时钟就绪中断
__IO uint32_t APB2RSTR; //APB2线上外设复位寄存器
__IO uint32_t APB1RSTR; //APB1线上外设复位寄存器
__IO uint32_t AHBENR; //DMA,SDIO等时钟使能
__IO uint32_t APB2ENR; //APB2线上外设时钟使能
__IO uint32_t APB1ENR; //APB1线上外设时钟使能
__IO uint32_t BDCR; //备份域控制寄存器
__IO uint32_t CSR; //控制状态寄存器
} RCC_TypeDef;
2,static void SetSysClockTo72(void)配置流程:
使能外部高速时钟HSE(需要等待查看其是否准备好)->配置外设分屏系数HPRE(AHB)、
PPRE2(APB2)、PPRE1(APB1)->使能PLL(需要等待查看其是否准备好)->选择PLL作为系统时钟源(需要等待查看其是否准备好)
【第20讲 系统时钟初始化函数】20170322
系统时钟初始化函数:
SystemInit();
使用V3.5版本的库函数,该函数在系统启动之后会自动调用:
startup_stm32f10x_xx.s文件中:
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
【第21讲 Systick滴答定时器-延时函数讲解】20170322
1,Systick滴答定时器并不占用CPU
2,ms和us延时函数已经固化到system路径的delay.c中
SysTick_CLKSourceConfig() //Systick时钟源选择 misc.c文件中
SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK或者HCLK/8,并开启中断 //core_cm3.h/core_cm4.h文件中
Systick中断服务函数:
void SysTick_Handler(void);
【第23讲 端口复用和重映射】20170328
1,端口复用:
GPIO端口时钟使能。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
复用外设时钟使能。
比如你要将端口PA9,PA10复用为串口,所以要使能串口时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
端口模式配置。
GPIO_Init()函数。
2,端口重映射:
使能GPIO时钟(重映射后的IO);
使能功能外设时钟(例如串口1);
使能AFIO时钟。重映射必须使能AFIO时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
开启重映射。
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
【第24讲 NVIC中断优先级管理】20170328
1,中断优先级设置步骤:
1)系统运行后先设置中断优先级分组。调用函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //这个配置在程序中的前后位置比较随意
整个系统执行过程中,只设置一次中断分组。
2)针对每个中断,设置对应的抢占优先级和响应优先级:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
3)如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
2,抢占优先级 & 响应优先级区别:
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
3,细节:
typedef struct
{
uint8_t NVIC_IRQChannel; //设置中断通道
uint8_t NVIC_IRQChannelPreemptionPriority;//设置响应优先级
uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级
FunctionalState NVIC_IRQChannelCmd; //使能/使能
} NVIC_InitTypeDef;
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断【【【【【(中断通道NVIC_IRQChannel的值在文件Stm32f10x.h的最前面)】】】】】】
)
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
【第25讲 串行通信原理讲解-UART】20170329
1,STM32的串口通信接口:
UART:通用异步收发器
USART:通用同步异步收发器
2,常见的串行通信接口:
UART
(通用异步收发器)
TXD:发送端
RXD:接受端
GND:公共地
异步通信
全双工
单总线(1-wire)
DQ:发送/接受端
异步通信
半双工
SPI
SCK:同步时钟
MISO:主机输入,从机输出
MOSI:主机输出,从机输入
同步通信
全双工
I2C
SCL:同步时钟
SDA:数据输入/输出端
同步通信
半双工
【第26讲 STM32串口寄存器库函数配置方法+手把手教你写串口通信实例】20170329
1,串口操作相关库函数(省略入口参数):
void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
FlagStatus USART_GetFlagStatus();//获取状态标志位
void USART_ClearFlag();//清除状态标志位
ITStatus USART_GetITStatus();//获取中断状态标志位
void USART_ClearITPendingBit();//清除中断状态标志位
2,串口配置的一般步骤
1)串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
2)串口复位:USART_DeInit(); 这一步不是必须的
3)GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
4)串口参数初始化:USART_Init();
5)开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
NVIC_Init(); //**********************************(中断通道NVIC_IRQChannel的值在文件Stm32f10x.h的最前面)*********************
USART_ITConfig(); //使能串口相关中断(发送缓冲区空中断、接受缓冲区满中断)
6)使能串口:USART_Cmd();
7)编写中断处理函数:
void USARTx_IRQHandler(void); //USARTx的值可选为USART1、USART2、USART3、UART4、UART5【【【所有中断函数名均在startup_stm32f10x_hd.s文件里,字符External Interrupts所在的行下面】】】】
8)串口数据收发:
void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
9)串口传输状态获取:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
【第28讲 外部中断实验-EXTI】20170331
1,外部中断的一般配置步骤:
初始化IO口为输入。
GPIO_Init();
开启IO口复用时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
设置IO口与中断线的映射关系。
void GPIO_EXTILineConfig();
初始化线上中断,设置触发条件等。
EXTI_Init();
配置中断分组,并开启中断并且初始化NVIC,并使能中断。
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); //这个配置在程序中的前后位置比较随意,一般放在全局main中
NVIC_Init();
编写中断服务函数。
EXTIx_IRQHandler();
清除中断标志位
EXTI_ClearITPendingBit();
2,外部中断常用库函数:
①void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
//设置IO口与中断线的映射关系
exp: GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
//初始化中断线:触发方式等
③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
//判断中断线中断状态,是否发生
④void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
//清除中断线上的中断标志位
【第29讲 独立看门狗实验-IWDG】20170331 如果程序中对led灯没有延时处理,复位非常快,将不能看到效果
1,IWDG独立看门狗操作库函数:
void IWDG_WriteAccessCmd(uint16_t IWDG_WriteAccess);//取消写保护:0x5555使能
void IWDG_SetPrescaler(uint8_t IWDG_Prescaler);//设置预分频系数:写PR
void IWDG_SetReload(uint16_t Reload);//设置重装载值:写RLR
void IWDG_ReloadCounter(void);//喂狗:写0xAAAA到KR
void IWDG_Enable(void);//使能看门狗:写0xCCCC到KR
FlagStatus IWDG_GetFlagStatus(uint16_t IWDG_FLAG);//状态:重装载/预分频 更新
2,独立看门狗操作步骤:
① 取消寄存器写保护:
IWDG_WriteAccessCmd();
② 设置独立看门狗的预分频系数,确定时钟:
IWDG_SetPrescaler(); //设置预分频系数:写PR
③ 设置看门狗重装载值,确定溢出时间:
IWDG_SetReload(); //设置重装载值:写RLR
④ 应用程序喂狗:
IWDG_ReloadCounter();
⑤ 使能看门狗
IWDG_Enable();
溢出时间计算:
Tout=((4×2^PR) ×RLR) /40 (M3内核)
【第30讲 窗口看门狗-WWDG】20170406
1,窗口看门狗配置过程:
① 使能看门狗时钟:
RCC_APB1PeriphClockCmd();
② 设置分频系数:
void WWDG_SetPrescaler(uint32_t WWDG_Prescaler);
③ 设置上窗口值:
void WWDG_SetWindowValue(uint8_t WindowValue);
④ 开启提前唤醒中断并分组(可选):
void WWDG_EnableIT(void);
NVIC_Init();
⑤ 使能看门狗:
void WWDG_Enable(uint8_t Counter);//IS_WWDG_COUTTER(COUNTER) (((COUNTER)>= 0X40)&&((COUNTER)<= 0X7F))
⑥ 喂狗:
void WWDG_SetCounter(uint8_t Counter);//IS_WWDG_COUTTER(COUNTER) (((COUNTER)>= 0X40)&&((COUNTER)<= 0X7F))
⑦编写中断服务函数
WWDG_IRQHandler();
void WWDG_IRQHandler(void)
{
WWDG_SetCounter(Counter);
WWDG_ClearFlag();
...
}
【第32讲 定时器中断实验】20170410
1,定时器中断实现步骤:
① 能定时器时钟。
RCC_APB1PeriphClockCmd();
② 初始化定时器,配置ARR(自动重装载寄存器),PSC(预分频寄存器)。
TIM_TimeBaseInit();
③ 开启定时器中断,配置NVIC。
void TIM_ITConfig();
NVIC_Init();
④ 使能定时器。
TIM_Cmd();
⑥ 编写中断服务函数。
TIMx_IRQHandler() 【【TIMx并不合法,编译时并不能检查出来,这里可以选择TIM3_IRQHandler】】
{
if(TIM_GetITStatus(...)) // ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
...
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
}
2,Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk //ARR和PSC都是u16类型数据
如,Tclk为72MHz时,要设定溢出时间为500ms,则ARR取值为4999,PSC取值为7199
3,通用定时器包括TIM2、TIM3、TIM4?和?TIM5。
【第33讲 PWM输出实验】20170411 【【呼吸灯的亮度由亮变暗采用的就是PWM脉宽调制,点亮灯的电平占空比越大,灯越亮】】
1,PWM输出配置步骤:
① 使能定时器3和相关IO口时钟。
使能定时器3时钟:
RCC_APB1PeriphClockCmd();
使能GPIOB时钟:
RCC_APB2PeriphClockCmd();
② 初始化IO口为复用功能输出。函数:
GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
③ 这里我们是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,
所以需要开启AFIO时钟。同时设置重映射。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);
④ 初始化定时器:ARR,PSC等:
TIM_TimeBaseInit(); //只需要配置结构体TIM_TimeBaseInitTypeDef中的成员TIM_Period、TIM_Prescaler、TIM_ClockDivision、TIM_CounterMode
⑤ 初始化输出比较参数:
TIM_OC2Init(); //OC2是通道2;只需要初始化结构体TIM_OCInitTypeDef中的成员TIM_OCMode、TIM_OutputState(比较输出使能)、TIM_OCPolarity(输出极性)
⑥ 使能预装载寄存器:
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
⑦ 使能定时:
TIM_Cmd();
以上七步放到初始化中完成,下一条视情况而定:
⑧ 不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare2();
2:高级定时器TIM1和TIM8必须做如下操作,才能正常输出PWM:
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能,高级定时器必须开启这个
【第34讲 输入捕获实验】20170415 细节处参考 第33讲
1,输入捕获的一般配置步骤:
① 初始化定时器和通道对应IO的时钟。
② 初始化IO口,模式为输入:
GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入
③初始化定时器ARR,PSC
TIM_TimeBaseInit();
④初始化输入捕获通道
TIM_ICInit();
typedef struct
{
uint16_t TIM_Channel; //捕获通道1-4
uint16_t TIM_ICPolarity; //捕获极性
uint16_t TIM_ICSelection; //映射关系
uint16_t TIM_ICPrescaler; //分频系数
uint16_t TIM_ICFilter; //滤波器
} TIM_ICInitTypeDef;
⑤如果要开启捕获中断,
NVIC_Init();
TIM_ITConfig(); //允许某某中断,比如更新中断 ,允许CC1IE捕获中断
⑥使能定时器:
TIM_Cmd();
⑦编写中断服务函数:
TIMx_IRQHandler()
{
if(TIM_GetITStatus(...)) //获取相关中断的标志位状态
{
...
TIM_ClearFlag();//必须得在获取了标志位状态后再清除,否则前一条语句永远发现不了相关中断标志位已经成立过,清除中断标志位,或者使用函数TIM_ClearITPendingBit();
}
TIM_SetCounter(); //设置当前计数器的值
}
2,输入捕获通道初始化函数:
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
typedef struct
{
uint16_t TIM_Channel; //捕获通道1-4,选择要配置为输入捕获1还是输入捕获2、或3或4
uint16_t TIM_ICPolarity; //捕获极性,对应设置捕获/比较使能寄存器CCER的位段CCxP,选择设置捕获发生在ICx的上升沿或者下降沿
uint16_t TIM_ICSelection; //映射关系(默认将通道设置为输入捕获),对应设置CCMR寄存器的CCxS位段,IC1映射到TI1,就选择\
TIM_ICSelection_DirectTI;如果映射到TI2,选择TIM_ICSelection_IndirectTI
uint16_t TIM_ICPrescaler; //分频系数,对应设置捕获/比较模式寄存器CCMRx寄存器的位段ICxPSC,设置选择每检查到N个边沿才触发一次捕获
uint16_t TIM_ICFilter; //滤波器,对应设置捕获/比较模式寄存器CCMRx寄存器的位段ICxF,配置输入滤波-采样频率,一般赋值0x00;
} TIM_ICInitTypeDef;
3,通道极性设置独立函数:设置捕获极性或者输出极性
void TIM_OCxPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
4,获取通道捕获值:
uint32_t TIM_GetCapture1(TIM_TypeDef* TIMx);
【第35讲 电容触摸按键实验】20170418
1,TPAD触摸电容放电时间5ms足以放完电容所有电荷。
2,电容充电到某个临界值时,可以被输入捕获以边沿方式捕捉到。
【第36讲 OLED显示实验】20170912
1,大量显示扫描算法可以研究研究
【第41讲 RTC实时时钟_备份区域BKP 原理讲解-M3】
① 使能PWR和BKP时钟: RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
② 使能后备寄存器访问: PWR_BackupAccessCmd(ENABLE);
③ 配置RTC时钟源,使能RTC时钟:
RCC_RTCCLKConfig(); //参数设置,如 RCC_RTCCLKSource_LSE
RCC_RTCCLKCmd(ENABLE);
如果使用LSE,要打开LSE:RCC_LSEConfig(RCC_LSE_ON);
④ 设置RTC预分频系数:RTC_SetPrescaler();
⑤ 设置时间:RTC_SetCounter(); //即设置RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)
⑥开启相关中断(如果需要):RTC_ITConfig();
⑦编写中断服务函数:RTC_IRQHandler();
⑧部分操作要等待写操作完成和同步。
RTC_WaitForLastTask();//等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
初始化示例代码:
u8 RTC_Init(void)
{
//检查是不是第一次配置时钟
u8 temp=0;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); //使能PWR和BKP外设时钟
PWR_BackupAccessCmd(ENABLE); //使能后备寄存器访问
//【【RTC模块的部分区域属于硬件的后备区域,即当VDD电源被切断,他们仍然由VBAT维持供电,
//【【备份数据寄存器(BKP_DRx)里的数据不会丢失,RTC寄存器中除开中断相关的的寄存器(RTC_CRH、RTC_CRL)会丢失被复位,
//【【RTC计数器寄存器 (RTC_CNTH / RTC_CNTL)是不会丢失的
if (BKP_ReadBackupRegister(BKP_DR1) != 0x5050) //从指定的后备寄存器中读出数据:读出了与写入的指定数据不相乎,则说明是第一次配置RTC时钟
{ //BKP->DR1用于保存是否第一次配置的设置
BKP_DeInit(); //复位备份区域
RCC_LSEConfig(RCC_LSE_ON); //设置外部低速晶振(LSE),使用外设低速晶振
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET&&temp<250) //检查指定的RCC标志位设置与否,等待低速晶振就绪
{
temp++;
delay_ms(10);
}
if(temp>=250)return 1;//初始化时钟失败,晶振有问题
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); //设置RTC时钟(RTCCLK),选择LSE作为RTC时钟
RCC_RTCCLKCmd(ENABLE); //使能RTC时钟
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_WaitForSynchro(); //等待RTC寄存器同步
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_EnterConfigMode();/// 允许配置
RTC_SetPrescaler(32767); //设置RTC预分频的值
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
RTC_Set(2015,1,14,17,42,55); //设置时间,该函数调用了RTC_SetCounter();
RTC_ExitConfigMode(); //退出配置模式
BKP_WriteBackupRegister(BKP_DR1, 0X5050); //向指定的后备寄存器中写入用户程序数据
}
else//系统继续计时
{
RTC_WaitForSynchro(); //等待最近一次对RTC寄存器的写操作完成
RTC_ITConfig(RTC_IT_SEC, ENABLE); //使能RTC秒中断
RTC_WaitForLastTask(); //等待最近一次对RTC寄存器的写操作完成
}
RTC_NVIC_Config();//RCT中断分组设置 ,自定义函数,配置秒中断的优先级
RTC_Get();//更新时间 ,自定义函数,读取当前时间
return 0; //ok
}
【第43讲 待机唤醒实验-低功耗-M3】
1:待机唤醒配置步骤
①使能电源时钟。
因为要配置电源控制寄存器,所以必须先使能电源时钟。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
②设置WK_UP引脚作为唤醒源。
设置PWR_CSR的EWUP位,使能WK_UP用于将CPU从待机模式唤醒。
PWR_WakeUpPinCmd(ENABLE); //使能唤醒管脚功能
③设置SLEEPDEEP位,设置PDDS位,执行WFI指令,进入待机模式。
void PWR_EnterSTANDBYMode(void);
2:从待机模式唤醒可以通过以下方式:
WKUP引脚的上升沿(需要使能唤醒PWR_WakeUpPinCmd())、 RTC闹钟事件的上升沿(需要配置闹钟RTC_SetAlarm())、 NRST引脚上外部复位、 IWDG复位
3:原子代码中,WKUP按键在待机模式下,具备唤醒功能,一旦按下即可唤醒,唤醒后,
源码配置其为复用功能,即既可以做GPIO,又能作为外部中断0输入脚
4:PA0在待机模式下可以作为WKUP唤醒,运行模式下,可以同时配置为TIM5_CH1的输入捕获脚和外部中断零EXTI_Line0的检测脚】】】】】】】】】】
【第44讲 ADC基本原理-M3】
1:
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
typedef struct
{
uint32_t ADC_Mode;//ADC模式:配置ADC_CR1寄存器的位[19:16] :DUALMODE[3:0]位
FunctionalState ADC_ScanConvMode; //是否使用扫描模式。ADC_CR1位8:SCAN位
FunctionalState ADC_ContinuousConvMode; //单次转换OR连续转换:ADC_CR2的位1:CONT
uint32_t ADC_ExternalTrigConv; //触发方式:ADC_CR2的位[19:17] :EXTSEL[2:0]
uint32_t ADC_DataAlign; //对齐方式:左对齐还是右对齐:ADC_CR2的位11:ALIGN
uint8_t ADC_NbrOfChannel;//规则通道序列长度:ADC_SQR1的位[23:20]: L[3:0]
}ADC_InitTypeDef;
范例代码配置为单通道AD转换:
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //不开启扫描 //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//单次转换模式 //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1;//顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);
2:范例--ADC1的通道1(PA1)进行单次转化
①开启PA口时钟和ADC1时钟,设置PA1为模拟输入。
GPIO_Init();
APB2PeriphClockCmd();
② 复位ADC1,同时设置ADC1分频因子。
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
ADC_DeInit(ADC1);
③ 初始化ADC1参数,设置ADC1的工作模式以及规则序列的相关信息。
void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct);
④ 使能ADC并校准。
ADC_Cmd(ADC1, ENABLE);
⑤ 配置规则通道参数:
ADC_RegularChannelConfig();
⑥开启软件转换:ADC_SoftwareStartConvCmd(ADC1, ENABLE);
⑦等待转换完成,读取ADC值。
ADC_GetConversionValue(ADC1);
2:ADC一旦转换结束,将产生一个DMA请求,这个请求将实现数据自动传输到用户指定的目的地址。
3:小节
1):pwm dac这个是干嘛的?
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能,高级定时器必须开启这个
2):DAC输出脚PA.4要配置为模拟输入???
-- DAC 本身是输出,但是为什么端口要设置为模拟输入模式呢?因为一但
使能 DACx 通道之后,相应的 GPIO 引脚(PA4 或者 PA5)会自动与 DAC 的模拟输出相连,设
置为输入,是为了避免额外的干扰。
3):
DAC在硬件触发下,产生DMA的请求,可以自动从用户指定的内存地址提取数据传输到DAC进行转换
【第48讲 DAC数模转换实验-M3】
1:配置-使能
GPIO_InitTypeDef GPIO_InitStructure;
DAC_InitTypeDef DAC_InitType;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE ); //使能PORTA通道时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE ); //使能DAC通道时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4) ;//PA.4 输出高
DAC_InitType.DAC_Trigger=DAC_Trigger_None; //不使用触发功能 TEN1=0
DAC_InitType.DAC_WaveGeneration=DAC_WaveGeneration_None;//不使用波形发生
DAC_InitType.DAC_LFSRUnmask_TriangleAmplitude=DAC_LFSRUnmask_Bit0;//屏蔽、幅值设置
DAC_InitType.DAC_OutputBuffer=DAC_OutputBuffer_Disable ; //DAC1输出缓存关闭 BOFF1=1
DAC_Init(DAC_Channel_1,&DAC_InitType); //初始化DAC通道1
DAC_Cmd(DAC_Channel_1, ENABLE); //使能DAC1
DAC_SetChannel1Data(DAC_Align_12b_R, 0); //12位右对齐数据格式设置DAC值
2:
DAC_SetChannel1Data();//设置DAC转换值
DAC_GetDataOutputValue(DAC_Channel_1);//读取前面设置DAC的值
3:如果配置中的DAC_InitType.DAC_Trigger设置了TRGO触发方式,则需配置定时器的触发控制器:
TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);//Update更新事件被选为触发输出TRGO(TRGO用于输出到其他定时器DAC/ADC)
【第50讲 DMA基本原理-M3】20181016
1,DMA的通道编号相当于中断的响应优先级,当两个DMA的软件设置的优先级相同时,则较低通道编号的DMA比较高
通道编号的DMA优先级更高,更优先响应。
2,在大容量产品和互联型产品中,DMA1控制器拥有高于DMA2控制器的优先级。
3,串口发送DMA配置:
① 使能DMA时钟
RCC_AHBPeriphClockCmd(); // RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
② 初始化DMA通道参数
DMA_Init(); //void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
//如果是别的外设,比如ADC\TIM\I2C\SPI等等时,固定对应的DMA通道号得查阅相关资料DMA章节,这里可以查阅
//STM32中文参考手册_V10.pdf DMA章节147页
typedef struct
{
uint32_t DMA_PeripheralBaseAddr; //外设基地址
uint32_t DMA_MemoryBaseAddr; //存储器基地址
uint32_t DMA_DIR; //数据传输方向
uint32_t DMA_BufferSize; //通道传输数据量 ----非循环模式下,通道传输数据量 也可以通过DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)设置
uint32_t DMA_PeripheralInc;//外设增量模式
uint32_t DMA_MemoryInc; //存储器增量模式
uint32_t DMA_PeripheralDataSize; //外设数据宽度
uint32_t DMA_MemoryDataSize; //存储器数据宽度
uint32_t DMA_Mode; //模式:是否循环
uint32_t DMA_Priority; //优先级
uint32_t DMA_M2M; //是否存储器到存储器方式
}DMA_InitTypeDef;
通道
优先级
数据传输方向
存储器/外设 数据宽度
存储器/外设 地址是否增量
循环模式
数据传输量
③使能串口DMA发送,串口DMA使能函数:
USART_DMACmd(); // //void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState)
④使能DMA1通道,启动传输。
DMA_Cmd(); //void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState)
⑤查询DMA传输状态标志
DMA_GetFlagStatus(); //FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)
//清除通道标志
DMA_ClearFlag(); //void DMA_ClearFlag(uint32_t DMAy_FLAG)
⑥获取/设置通道当前剩余数据量:
DMA_GetCurrDataCounter(); //uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx)
DMA_SetCurrDataCounter(); //void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber)
另外,可以配置DMA中断请求:
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
【心得】
//printf("\r\nDMA DATA:\r\n"); //printf使用的串口和开通的DMA某通道占用的串口是同一个串口,在DMA占用时,printf无法执行,会被卡主
【第57讲 STM32 CAN控制器原理与配置-M3】
1) 发送邮箱有三个,接收邮箱有两个(FIFO0和FIFO1),每个接收邮箱可以存储三个报文(即“三级深度”)。
2) 发送标志符,扩展符值的设置(CAN_TIxR==TxMessage.StdId/TxMessage.ExtId) 跟 过滤器组寄存器值(CAN_FiRx==CAN_FilterIdHigh/CAN_FilterIdLow/CAN_FilterMaskIdHigh/CAN_FilterMaskIdLow) 的配置关系详见<<STM32中文参考手册_V10.pdf>> 432页
3) 屏蔽位模式和列表模式的区别:
前者是过滤一组ID,后者是过滤列表中要求的唯一几个ID。
模式配置CAN_FilterMode:
CAN_FilterMode_IdMask CAN_FilterMode_IdList
位宽配置CAN_FilterScale: CAN_FilterScale_16bit 设置2个16位过滤组 设置4个16位过滤ID
CAN_FilterScale_32bit 设置1个32位过滤组 设置2个32位过滤ID
4) 范例配置:
//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:CAN_SJW_1tq~ CAN_SJW_4tq
//tbs2:时间段2的时间单元. 范围:CAN_BS2_1tq~CAN_BS2_8tq;
//tbs1:时间段1的时间单元. 范围:CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024; tq=(brp)*tpclk1
//波特率=Fpclk1/((tbs1+1+tbs2+1+1)*brp);
//mode:CAN_Mode_Normal,普通模式;CAN_Mode_LoopBack,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Mode_Init(CAN_SJW_1tq,CAN_BS2_8tq,CAN_BS1_9tq,4,CAN_Mode_LoopBack);
//则波特率为:36M/((8+9+1)*4)=500Kbps
//返回值:0,初始化OK;
// 其他,初始化失败;
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
//CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //邮箱发送优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= mode; //模式设置: mode:0,普通模式;1,回环模式;
//设置波特率
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2; //Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1
CAN_Init(CAN1, &CAN_InitStructure); //初始化CAN1
CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //屏蔽位模式
//CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdList; //列表模式
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位宽
//CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_16bit; //16位宽
//CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000; //32位ID
CAN_FilterInitStructure.CAN_FilterIdHigh=0x6020; //该十六进制数最末一位往往跟RTR,
//IDE,EXTID部分位的过滤选择有关,设置为0表示不关心这些位,不要求强制匹配
//CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow=0x1020;
//CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK 全0表示任何一位都不关心,都将认可 -ypl
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x6420;//32位MASK,当处于"屏蔽位模式"时,这十六位中被选中的位(被置1的位)即是需要关心的,
//则要求被过滤报文的标志符相关位跟CAN_FilterIdHigh和CAN_FilterIdLow设置的相关位值务必匹配相同
//CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000; //32位MASK 全0表示任何一位都不关心,都将认可
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x8420; //32位MASK
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;//激活过滤器0
CAN_FilterInit(&CAN_FilterInitStructure); //滤波器初始化
#if CAN_RX0_INT_ENABLE
CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE); //FIFO0消息挂号中断允许.
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0;
}
#if CAN_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
//TxMessage.StdId=0x12; // 标准标识符
//TxMessage.StdId=0x642>>1; //该值的设置一般要求不为奇数,这里由于右移一位,所以导致奇数偶数相等 -ypl
TxMessage.StdId=0x70A>>1; // 标准标识符,由于下一行设置的是标准帧,则只关心StdId的设置
//32位宽屏蔽位模式时,该标志符0x70A<<1可通过CAN_FilterIdHigh和CAN_FilterMaskIdHigh设置的过滤条件
// TxMessage.ExtId=0x12; // 设置扩展标示符 //由于下一行设置的是标准帧,不是扩展帧,故该行代码可以注释掉
TxMessage.IDE=CAN_Id_Standard; // 标准帧
TxMessage.RTR=CAN_RTR_Data; // 数据帧
TxMessage.DLC=len; // 要发送的数据长度
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i];
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束
if(i>=0XFFF)return 1;
return 0;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}
【第74讲 SD卡实验(SDIO)源码讲解_战舰_精英】
1,寄存器:
CID 128 卡标识号
RCA 16 相对卡地址(Relative card address):本地系统中卡的地址,动态变化,在主机初始化的时候确定
*SPI模式中没有
CSD 128 卡描述数据:卡操作条件相关的信息数据
SCR 64 SD配置寄存器:SD卡特定信息数据
OCR 32 操作条件寄存器
2,
stm32芯片内部外设SDIO适配器里关于控制单元、命令通道和数据通道使用
SDIO适配器一侧的时钟~~SDIOCLK是SDIO外设的输入时钟,而SDIO_CK是供应到SD卡的输出时钟,
两者关系满足 SDIO_CK频率 = SDIOCLK/[CLKDIV + 2]
Command Format 57
Responses 69
Card Registers 84
CRC7 54
OCR 85
01000000 00000000000000000000000000000000
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00