物联网|可变参数的使用技巧|不一样的点灯实验|访问外设的寄存器|操作寄存器实现点灯|硬件编程的基本流程-学习笔记(11)

可变参数的使用技巧

void fixed_args_func(int a, double b, char c)
{
        printf("a = 0x%p\n", &a);
        printf("b = 0x%p\n", &b);
        printf("c = 0x%p\n", &c);
if(&a==MCULowPower)
}
void var_args_func(const char * fmt, ...)
{
    ... ...
}
void var_args_func(const char * fmt, ...)
{
    char    *ap;

    ap = ((char*)&fmt) + sizeof(fmt);
    printf("%d\n", *(int*)ap);

    ap =  ap + sizeof(int);
    printf("%d\n", *(int*)ap);

    ap =  ap + sizeof(int);
    printf("%s\n", *((char**)ap));
}

在这里插入图片描述

第三阶段-初级实验

Lesson5:不一样的点灯实验—学习I/O的输出

1: 理论基础 CPU工作机制 如何对CPU编程
外设的的原理
2:代码实现以及分析
3:在板实验和调试
嵌入式开发必知会之三—
了解LED☆点灯的电路图分析区
STM32F407中GPIO的特性及操作方法
1.LED的功能,特点和用途
在这里插入图片描述

发光二极管
在这里插入图片描述

2.LED的工作原理

课后作业:请同学们课后了解电阻,电容和电感的功能,特点,用途及电路符号表示

☆点灯的电路图分析

1 一起看看点灯的电路图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Tip1:另一种点灯的电路

1 STM32F407中GPIO的特性
Tip1:如何访问外设的寄存器

2 STM32F407中操作GPIO的方法

  • 输出的配置

  • 高低电平的配告

  • 手册描述
    通用IO (GPIO)
    除非特别说明,否则本部分适用于整个STM32F4xx系列。

    • 7.1 GPIO简介
      每个通用IO端口包括4个32位配置寄存器(GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR和 GPIOx_PUPDR)、2个32位数据寄存器(GPIOx_IDR和
      GPIOx_ODR)、1个 32位置位/复位寄存器(GPIOx_BSRR)、1个32位锁定寄存器(GPIOx_LCKR)和2个32位复用功能选择寄存器(GPIOx_AFRH 和GPIOx_AFRL)。

    • 7.2 GPIO主要特性受控IO多达16个
      输出状态:推挽或开漏+上拉/下拉
      从输出数据寄存器(GPIOx_ODR)或外设(复用功能输出)输出数据可为每个IO选择不同的速度
      输入状态:浮空、上拉/下拉、模拟
      将数据输入到输入数据寄存器(GPIOx_IDR)或外设(复用功能输入)置位和复位寄存器(GPIOx_BSRR),对GPIOx_ODR具有按位写权限锁定机制(GPIOx_LCKR),可冻结IO配置
      模拟功能
      复用功能输入/输出选择寄存器(一个IO最多可具有16个复用功能>快速翻转,每次翻转最快只需要两个时钟周期
      引脚复用非常灵活,允许将IO引脚用作GPIO或多种外设功能中的一种

    • 7.3 GPIO功能描述
      根据数据手册中列出的每个lO端口的特性,可通过软件将通用IVO(GPIO)端口的各个端口位分别配置为多种模式;
      输入浮空
      输入上拉
      输入下拉模拟功能
      具有上拉或下拉功能的开漏输出具有上拉或下拉功能的推挽输出具有上拉或下拉功能的复用功能推挽具有上拉或下拉功能的复用功能开漏
      每个IO端口位均可自由编程,但IO端口寄存器必须按32位字、半字或字节进行访问。GPIOx_BSRR寄存器旨在实现对GPIO ODR寄存器进行原子读取/修改访问。这样便可确保在读取和修改访问之间发生中断请求也不会有问题。
      在这里插入图片描述

    • 8.3.10 Output配置
      在这里插入图片描述 在这里插入图片描述
      当lO口编程为输出时:。输出缓冲区被启用:
      在这里插入图片描述

    -漏极模式:输出寄存器中的“O”激活N-MOS,而“1”激活N-MOS
    在输出寄存器中离开Hi-Z端口(P-MOS从未激活)
    推挽模式:输出寄存器中的“O”激活N-MOS,而输出寄存器中的“1”激活P-MoS
    施密特触发器输入被激活
    弱上拉和下拉电阻是否激活取决于GPIOx_PUPDR寄存器中的值
    l/O引脚上的数据在每个AHB1时钟周期中被采样到输入数据寄存器中
    对输入数据寄存器的读访问获得I/O状态。
    对输出数据寄存器的区域访问获得最后写入的值。
    图29显示了IO端口位的输出配置。
    原文:
    8.3.10 Output configuration
    When the I/O port is programmed as output:
    • The output buffer is enabled:
    – Open drain mode: A “0” in the Output register activates the N-MOS whereas a “1”
    in the Output register leaves the port in Hi-Z (the P-MOS is never activated)
    – Push-pull mode: A “0” in the Output register activates the N-MOS whereas a “1” in
    the Output register activates the P-MOS
    • The Schmitt trigger input is activated
    • The weak pull-up and pull-down resistors are activated or not depending on the value
    in the GPIOx_PUPDR register
    • The data present on the I/O pin are sampled into the input data register every AHB1
    clock cycle
    • A read access to the input data register gets the I/O state
    • A read access to the output data register gets the last written value
    Figure 29 shows the output configuration of the I/O port bit.

Tip1:如何访问外设的寄存器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2 STM32F407中操作GPIO的方法

  • 输出的配置
  • 高低电平的配置

通过直接操作寄存器实现点灯实验

  • 实验的需求分析(实验目的)
    • 学会基本的硬件操作—I/O的输出高低电平
    • 操作学习利用系统时钟实现较为精确的延时
    • 学习规范的模块化编程
    • 学会使用CMSIS的支持库
  • 通过直接操作寄存器实现点灯代码
  • 代码下载及调试演示
    1程序流程分析
    • 如何利用系统时钟实现较为精确的延时
      9.5SysTick 定时器
      9.5.1 为什么要有SysTick 定时器
      Cortex-M处理器内集成了一个小型的名为SysTick(系统节拍)的定时器,它属于NVIC的一部分,且可以产生SysTick 异常(异常类型#15)。SysTick 为简单的向下计数的24位计数器,可以使用处理器时钟或外部参考时钟(通常是片上时钟源)。
      在现代操作系统中,需要一个周期性的中断来定期触发OS内核,如用于任务管理和上下文切换,处理器也可以在不同时间片内处理不同任务。处理器设计还需要确保运行在非特权等级的应用任务无法禁止该定时器,否则任务可能会禁止SysTick 定时器并锁定整个系统。
      之所以在处理器内增加一个定时器,是为了提高软件的可移植性。由于所有的Cortex-M处理器都具有相同的SysTick定时器,为一种Cortex-M3/M4微控制器实现的OS也能适用于其他的Cortex-M3/M4微控制器。
      若应用中不需要使用OS,SysTick定时器可用作简单的定时器外设﹐用以产生周期性中断、延时或时间测量。
      9.5.2SysTick定时器操作

      在这里插入图片描述
      如表9.6所示,SysTick定时器中存在4个寄存器。CMSIS-Core头文件中定义了一个名为SysTick的结构体,方便对这些寄存器的访问。
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

      SysTick内部包含一个24位向下计数器,如图9.15所示。它会根据处理器时钟或一个参考时钟信号(在ARM Cortex-M3或Cortex-M4 技术参考手册中也被称作STCLK)来减小计数。参考时钟信号取决于微控制器的实际设计,有些情况下﹐它可能会不存在。由于要检测上升沿,参考时钟至少得比处理器时钟慢两倍。
      在设置控制和状态寄存器的第О位使能该计数器后,当前值寄存器在每个处理器时钟周期或参考时钟的上升沿都会减小。若计数减至0,它会从重加载寄存器中加载数值并继续运行。
      另外一个寄存器为SysTick校准值寄存器。它为软件提供了校准信息。由于CMSIS-Core提供了一个名为SystemCoreClock 的软件变量(CMSIS 1.2 及之后版本可用,CMSIS 1.1或之前版本则使用变量SystemFrequency) ,因此它就未使用SysTick校准值寄存器。系统初始
      化函数SystemInit()函数设置了该变量,而且每次系统时钟配置改变时都要对其进行更新。这种软件手段比利用SysTick校准值寄存器的硬件方式更灵活。
      SysTick寄存器的细节如表9.7~表9.10所示。
      9.5.3使用 SysTick定时器
      若只想产生周期性的SysTick中断,最简单的方法就是使用CMSIS-Core函数 SysTick_Config :
      uint32_t SysTick_Conf ig(uint32_t ticks) ;
      该函数将SysTick 中断间隔设置为ticks,使能计数器使用处理器时钟,然后设置SysTick异常为最低优先级。
      例如,若要在30MHz的时钟频率下产生1kHz的SysTick异常,则可以使用:SysTick_Config(SystemCoreClock / 1000) ;
      变量SystemCoreClock应该存放正确的时钟频率数值,也就是30×10°。另外,只需使用:
      SysTick_Config( 30000) ;
      //30MHz / 1000 = 30000
      SysTick_Handler( void)的触发频率就变成了1kHz。
      若SysTick_Config 函数的输入参数不满足24位重加载数值寄存器(大于0xFFFFFF),SysTick_Config函数返回1,否则会返回0。
      许多情况下,可能会使用参考时钟或者不想使能SysTick 中断,那么就不要使用SysTick_Config 函数。此时需要直接操作 SysTick寄存器,推荐使用下面的流程:
      (1)将0写人 SysTick->CTRL禁止 SysTick定时器。这个操作是可选的。若重用了其他代码,则由于SysTick之前可能已经使能过了,因此本操作是推荐使用的。
      (2)将新的重加载值写入SysTick->LOAD,重加载值应该为周期数减1。
      (3)将任何数值写人SysTick当前值寄存器SysTick->VAL,该寄存器会被清零
      (4)写入 SysTick控制和状态寄存器SysTick->CTRL启动SysTick定时器。
      由于SysTick定时器向下计数到0,因此,若要设置SysTick周期为1000,则应该将重加载值(SysTick->LOAD)设置为999。
      若要在轮询模式中使用SysTick定时器,则可以利用SysTick 控制和状态寄存器(SysTick->CTRL)中的计数标志来确定定时器何时变为0。例如,可以将SysTick定时器设置为特定数值,然后等它变为0,并以此实现延时:
      SysTick-> CTRL = 0;
      /禁止SysTick
      SysTick - > LOAD = OxFF;
      //计数范围255~0 (256个周期)
      SysTick-> VAL = 0 ;
      //清除当前值和计数标志
      SysTick- > CTRL = 5;
      //使能SysTick定时器并使用处理器时钟 5的二进制是0101
      while ( (SysTick - >CTRL & Ox00010000) == 0);//等待计数标志置位
      SysTick - > CTRL= 0 ;
      /!禁止 SysTick
      若要将SysTick中断用作在一定时间后触发的单发操作,则可以将重加载值减小12个周期,以补偿中断等待时间。例如,要使SysTick定时器在300个时钟周期后执行:
      volatile int SysTickFired;
      1/全局软件标志,表示 SysTickAlarm已执行

      SysTick-> CTRL= 0 ;
      /禁止 SysTick
      SysTick -> LOAD = (300-12);
      //设置重加载值,由于异常等待减去12
      SysTick - > VAL=0;
      //将当前值清为0
      SysTickFired = 0;
      //将软件标志设为0
      SysTick - > CTRL= 0x7;
      1/使能SysTick,使能 SysTick异常且使用处理器时钟 0x7=0111
      while (SysTickFired == 0 );
      1/等待SysTick处理将软件标志置位
      在单发SysTick处理中﹐需要禁止SysTick,以防SysTick 异常再次产生。若由于所需的处理任务花费的时间太长而导致挂起状态再次置位﹐则可能还需要清除SysTick 的挂起状态:
      void SysTick_Handler(void)
      // SYSTICK异常处理
      {
      SysTick ->CTRL = Ox0 ;
      /1禁止SysTick
      …;
      //执行所需任务
      SCB->ICSR | = 1 <<25;
      //清除SYSTICK挂起位,防止再次挂起
      sysTickFired++ ;
      //更新软件标志,主程序据此可以知道SysTick
      定时任
      务已执行
      return;
      若同时产生了另一个异常,则 SysTick异常可能会延迟。
      SysTick 定时器可用于时间测量。例如,可以用下面的代码测量一个短函数的持续时间:
      unsigned int start_time,stop_time,cycle_count;
      SysTick - > CTRL= 0 ;
      /I/禁止SysTick
      SysTick - > LOAD =0xFFFFFFFF;
      //将重加载值设置为最大
      SysTick -> VAL= 0 ;
      //将当前值清为0
      SysTick ->CTRL= 0x5;
      /1使能SysTick,使用处理器时钟
      while(SysTick- > VAL != 0);
      //等待SysTick重加载
      start_time = SysTick - > VAL;
      //获取开始时间
      function();
      //执行要测量的函数
      stop_time =SysTick- > VAL;
      1/获取停止时间
      由于SysTick定时器向下计数, start_time的数值比 stop_time要大。可能还需要在时间
      测量的结尾检查一下count_flag。若count_tlag 置位时测则风的NE大了 A旧 t六敝还要能SysTick异常且在SysTick处理中计算SyslicK T效奋恤出的认效。nW川心欢u一考虑SysTick 异常。
      SysTick定时器还提供了一个校准值寄存器。若该信息存在,则SysTick->CALIB寄存
      器的最低24位表示要得到10ms SysTick间隔所需的重加载值。个过,计多似丘润’P开仅有这个信息,TENMS位域读出为0。CMSIS-Core方案则提供」一个表水频平信忌的队什受量,这种方式更加灵活且得到了多数微控制器供应商的支持。
      可以利用 SysTick校准值寄存器的第31位确定参考时钟是否存在。9.5.4其他考虑
      在使用SysTick定时器时需要考虑以下几点:
      . SysTick定时器中的寄存器只能在特权状态下访问。·参考时钟在一些微控制器设计中可能会不存在。
      ·若应用中存在嵌入式OS,SysTick定时器会被OS使用,因此就不能冉放应用在方使用了。
      ·当处理器在调试期间暂停时,SysTick定时器会停止计数。
      ·根据微控制器的实际设计,SysTick定时器可能会在某些休眠模式中停止计数。

/初始化延迟函数
//当使用uoo=的时候,此函数会初始化uco=的时钟节拍7/SYSTICK的时钟固定为AHB时钟的1/8
//sYSCLK:系统时钟频率
void delay_init (u8 sYSCLK)
{
SysTick->CTRL&=~(1<<2);//SYSTICR使用外部时钟源 只操作了第2位
fac_us=SYSCLK/8;//每个us需要的systick时钟数
fac_ms= (u16) fac_us*1000;//每个ns需要的systick时钟数
}
  • 如何初始化I/O

Tip1:硬件编程的基本流程

1、初始化硬件:总线始终初始化,申请区域存放寄存器,配置IO的输出形式,设置IOspeed,是否需要AF(第2功能)

2代码实现及分析

systick.h:

//摘自正点原子提供例程

#ifndef __SYSTICK_H__
#define __SYSTICK_H__

#include "stm32f4xx.h"

typedef uint32_t  u32;
typedef uint16_t u16;
typedef uint8_t  u8;
static u8  fac_us=0;							//us延时倍乘数
static u16 fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数


void delay_init(u8 SYSCLK);
void delay_us(u32 nus);
void delay_ms(u16 nms);

#endif

systick.c:

#include "systick.h"


//摘自正点原子提供例程
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init(u8 SYSCLK)
{
#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
	u32 reload;
#endif
 	SysTick->CTRL&=~(1<<2);					//SYSTICK使用外部时钟源
	fac_us=SYSCLK/8;						//不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS 						//如果需要支持OS.
	reload=SYSCLK/8;						//每秒钟的计数次数 单位为K
	reload*=1000000/delay_ostickspersec;	//根据delay_ostickspersec设定溢出时间
											//reload为24位寄存器,最大值:16777216,在72M下,约合1.86s左右
	fac_ms=1000/delay_ostickspersec;		//代表OS可以延时的最少单位
	SysTick->CTRL|=1<<1;   					//开启SYSTICK中断
	SysTick->LOAD=reload; 					//每1/delay_ostickspersec秒中断一次
	SysTick->CTRL|=1<<0;   					//开启SYSTICK
#else
	fac_ms=(u16)fac_us*1000;				//非OS下,代表每个ms需要的systick时钟数
#endif
}
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
	u32 temp;
	SysTick->LOAD=nus*fac_us; 				//时间加载
	SysTick->VAL=0x00;        				//清空计数器
	SysTick->CTRL=0x01 ;      				//开始倒数
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达
	SysTick->CTRL=0x00;      	 			//关闭计数器
	SysTick->VAL =0X00;       				//清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
	u32 temp;
	SysTick->LOAD=(u32)nms*fac_ms;			//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;           			//清空计数器
	SysTick->CTRL=0x01 ;          			//开始倒数
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));	//等待时间到达
	SysTick->CTRL=0x00;      	 			//关闭计数器
	SysTick->VAL =0X00;       				//清空计数器
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

打酱油的工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值