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

文章目录

        • 1.SysTick定时器简介
        • 2.精确延时函数的实现
        • 3.GPIO位带操作
        • 4.实际使用

系列教程: 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

你可能感兴趣的:(Microsemi,Microsemi,FPGA,Libero,ARM,MSS_GPIO)