欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SmartFusion从FPGA到ARM(三)——SysTick延时函数和GPIO位带操作

程序员文章站 2022-07-01 20:39:38
...


系列教程:SmartFusion从FPGA到ARM系列教程

1.SysTick定时器简介

SysTick定时器是存在于ARM Cortex-M内核的一个滴答定时器,只要是ARM Cortex-M0/M3/M4/M7内核的MCU都包含这个定时器。

它是一个24位的递减定时器,当计数到 0 时,将从RELOAD 寄存器中自动重装载定时初值,开始新一轮计数。

使用内核的SysTick定时器来实现延时,可以不占用系统定时器,由于和MCU外设无关,所以代码的移植,在不同厂家的Cortex-M内核MCU之间,可以很方便的实现。

Microsemi SmartFusion系列的FPGA芯片,内部就有一个ARM Cortex-M3内核的MCU,可以利用这个定时器,实现一个精确延时毫秒和微妙的函数。

2.精确延时函数的实现

在core_cm3.h文件中,有这样一个SysTick_Config函数:

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL); /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL); /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL; /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;/* Enable SysTick IRQ and SysTick Timer */
  return (0UL);   /* Function successful */
}

通过后面的注释也可以看出,这是对SysTick定时器进行初始化,配置初始计数值,使能中断,使能定时器等。对应的中断函数为:

void SysTick_Handler(void)
{

这个默认是空的,需要我们自己来实现。

如果SysTick初始化为:

SysTick_Config(SystemCoreClock / 1000);     //定时1ms

即SysTick定时器每1ms中断一次,如果我们定义全局变量,然后在中断函数中,让此变量递减,而在延时函数中,一直判断此变量是否减到了0,那么这样就实现了一个延时毫秒的函数。同理改变定时器的计数值为:

SysTick_Config(SystemCoreClock / 1000000);  //定时1us

那么就实现了每1us中断一次,所以延时微秒和延时毫秒函数的实现:

uint32_t fac_us=0;							//us延时倍乘数			   
uint32_t fac_ms=0;							//ms延时倍乘数,在ucos下,代表每个节拍的ms数
	
void delay_init(void)
{
    SystemCoreClockUpdate();        
}

void SysTick_Handler(void)
{
    if(fac_us) fac_us--;
    if(fac_ms) fac_ms--;
}

void delay_us(uint32_t nus)
{	
    SysTick_Config(SystemCoreClock / 1000000);  //定时1us
    fac_us = nus;
    while(fac_us != 0);	 
}

void delay_ms(uint32_t nms)
{	 		  	  
    SysTick_Config(SystemCoreClock / 1000);     //定时1ms
    fac_ms = nms;
    while(fac_ms != 0);	 	  	    
} 

在使用延时函数之前,只需要进行系统时钟的更新即可。

3.GPIO位带操作

从MSS_GPIO库函数中,可找到对单个GPIO进行控制的实现:

void MSS_GPIO_set_output
(
    mss_gpio_id_t       port_id,
    uint8_t             value
)
{
    uint32_t gpio_idx = (uint32_t)port_id;
    
    ASSERT( gpio_idx < NB_OF_GPIO );
    
    if ( gpio_idx < NB_OF_GPIO )
    {
        GPIO_BITBAND->GPIO_OUT[gpio_idx] = (uint32_t)value;
    }
}

可以看到,最终是可以简化为GPIO_BITBAND->GPIO_OUT[gpio_idx] = value;

a2fxxxm3.h头文件中有一个结构体的定义:

typedef struct
{
    __IO uint32_t GPIO_0_CFG;
   ............
    __IO uint32_t GPIO_31_CFG;
    __IO uint32_t GPIO_IRQ;
    __I  uint32_t GPIO_IN;
    __IO uint32_t GPIO_OUT;
} GPIO_TypeDef;

typedef struct
{
    __IO uint32_t GPIO_0_CFG[32];
	............
    __IO uint32_t GPIO_31_CFG[32];
    __IO uint32_t GPIO_IRQ[32];
    __I  uint32_t GPIO_IN[32];
    __IO uint32_t GPIO_OUT[32];
} GPIO_BitBand_TypeDef;

也就是说,最终的GPIO控制和读取,操作的其实是结构体成员GPIO_IN[32]和GPIO_OUT[32]这两个数组。

可以用一个带参数的宏来实现:

/* GPIO输出 */
#define MSS_IO_OUT(n) GPIO_BITBAND->GPIO_OUT[n]
/* 读取输入 */
#define MSS_IO_IN(n)  GPIO_BITBAND->GPIO_IN[n]

/* 示例 */
MSS_IO_OUT(0) = 0;	//GPIO_0输出0
MSS_IO_OUT(3) = 1;	//GPIO_3输入1

uint8_t sw1_in = MSS_IO_IN(2);//读取GPIO_2输入
uint8_t sw2_in = MSS_IO_IN(3);//读取GPIO_3输入

4.实际使用

所以GPIO的控制和延时函数的实际调用:

#include "main.h"

int main()
{
    delay_init();       /* 更新系统时钟 */
    MSS_WD_disable();
    
    MSS_GPIO_init();
    /* GPIO_0 & GPIO_1 配置成输出模式 */
    MSS_GPIO_config(MSS_GPIO_0, MSS_GPIO_OUTPUT_MODE);
    MSS_GPIO_config(MSS_GPIO_1, MSS_GPIO_OUTPUT_MODE);
    
    /* GPIO_2配置成输入模式*/
    MSS_GPIO_config(MSS_GPIO_2, MSS_GPIO_INPUT_MODE);

    while(1)
    {
        /* GPIO_0闪烁 */
        MSS_IO_OUT(0) = 0;
        delay_ms(500);
        MSS_IO_OUT(0) = 1;
        delay_ms(500);  
            
        /* 读取GPIO_2输入 */
        if(MSS_IO_IN(2) == 0)
            MSS_IO_OUT(1) = 0;
        else 
            MSS_IO_OUT(1) = 1;
    }
}

delay.c文件的内容:

#include "delay.h"

uint32_t fac_us = 0;
uint32_t fac_ms = 0;

void delay_init(void)
{
    SystemCoreClockUpdate();
}

void SysTick_Handler(void)
{
    if(fac_us) fac_us--;
    if(fac_ms) fac_ms--;
}

void delay_us(uint32_t nus)
{
    SysTick_Config(SystemCoreClock / 1000000);
    fac_us = nus;
    while(fac_us != 0);
}

void delay_ms(uint32_t nms)
{
    SysTick_Config(SystemCoreClock / 1000);
    fac_ms = nms;
    while(fac_ms != 0);
}

delay.h文件的内容:

#ifndef __DELAY_H__
#define __DELAY_H__
		   
#include "a2fxxxm3.h"  

extern uint32_t fac_us;
extern uint32_t fac_ms;

void delay_init(void);
void delay_ms(uint32_t nms);
void delay_us(uint32_t nus);

#endif

sys.h文件的内容:

#ifndef __SYS_H__
#define __SYS_H__

#include "a2fxxxm3.h"

/* GPIO输出 */
#define MSS_IO_OUT(n) GPIO_BITBAND->GPIO_OUT[n]

/* 读取输入 */
#define MSS_IO_IN(n)  GPIO_BITBAND->GPIO_IN[n]

#endif