实战_SysTick异常
本节课程使用CPU自带的SysTick定时器,让它产生周期性的中断,用来操作LED。
1.1 SysTick操作
Cortex-M处理器内部集成了一个小型的、名为SysTick的定时器。可以使用它来为操作系统提供系统时钟,也可以把它当做一般的定时器。
之所以在处理器内增加这样的定时器,是为了提高软件的可以移植性。
它是一个24位的定时器,向下计数。
在时钟源的驱动下,计数值到达0时,可以触发异常。然后我们就可以定义异常处理函数,每当触发异常时,就可以去执行函数中的操作。
它的框图如下:
在本程序中,只需要设置这几个寄存器即可:
我们通过定义一个结构体来操作这些寄存器
typedef struct
{
volatile unsigned int CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
volatile unsigned int LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
volatile unsigned int VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
volatile unsigned int CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
#define SysTick_BASE (0xE000E000 + 0x0010UL)
#define SYSTICK_FRE (8000000)
1.SysTick->CTRL
时钟源选择:
时钟源有两个,所以还要选择时钟源,这时我们需要操作如下几位
- bit2 设置为1,选择时钟来源为内部处理器的时钟;
- bit1 设置为1,使能异常
- bit0 设置为0,使能定时器
/* 3.选择时钟源,使能systick,使能异常 */
// 分别设置bit2,bit1,bit0
SysTickInit->CTRL = (1<<2) | (1<<1) | (1<<0);
2.SysTick->VAL
设置计数器VAL的值
已知基地址为:(0xE000E000 + 0x0010UL) ;我们通过定义一个宏来操作这个地址#define SysTick_BASE (0xE000E000 + 0x0010UL)
计数值count的计算:
1s钟产生1次异常
晶振8MHz,计时器count值为 val = 1/T ;
T = 1/8MHz
1s钟计数器的填充值应该为:val = 1/ 1/8M = 8000000
我们也通过定义一个宏来操作计数值:#define SYSTICK_FRE (8000000)
SysTickInit->VAL = SYSTICK_FRE;
3.SysTick->LOAD
LOAD自动重装寄存器也需要设置对应的重装值
/* 2.设置LOAD寄存器,重新加载值 */
SysTickInit->LOAD = SYSTICK_FRE;
1.2 清除SysTick异常
最后,当我们设置完上述寄存器后,还需要清除这个异常转态,让程序跳过异常,继续向下执行
清除systick的挂起状态
我们也通过宏定义来设置bit25
/* 清除异常状态 ;设置SCB中的ICSR为1*/
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
1.3 通过systick异常来控制LED闪烁
代码实现:
- start.s
PRESERVE8
THUMB
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
;IMPORT HardFault_Handler
IMPORT SysTick_Handler
__Vectors DCD 0
DCD 0x08000009; //Reset_Handler ; Reset Handler
DCD 0 ; NMI Handler
DCD 0 ; Hard Fault Handler
DCD 0 ; MPU Fault Handler
DCD 0 ; Bus Fault Handler
DCD 0 ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; SVCall Handler
DCD 0 ; Debug Monitor Handler
DCD 0 ; Reserved
DCD 0 ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
AREA |.text|, CODE, READONLY
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT mymain
IMPORT SystemInit
IMPORT uart_init
IMPORT SysTickInit
IMPORT LedInit
LDR SP, =(0x20000000+0x10000)
BL SystemInit
BL uart_init
BL SysTickInit
BL LedInit
;BL mymain
LDR R0, =mymain
BLX R0
ENDP
END
- init.c
void SystemInit(void)
{
extern int * Image$$ER_IROM1$$Base;
extern int * Image$$ER_IROM1$$Length;
extern int * Load$$ER_IROM1$$Base;
extern int * Image$$RW_IRAM1$$Base;
extern int * Image$$RW_IRAM1$$Length;
extern int * Load$$RW_IRAM1$$Base;
extern int * Image$$RW_IRAM1$$ZI$$Base;
extern int * Image$$RW_IRAM1$$ZI$$Length;
/* text relocate */
if (&Image$$ER_IROM1$$Base != &Load$$ER_IROM1$$Base)
memcpy(&Image$$ER_IROM1$$Base, &Load$$ER_IROM1$$Base, &Image$$ER_IROM1$$Length);
/* data relocate */
if (&Image$$RW_IRAM1$$Base != &Load$$RW_IRAM1$$Base)
memcpy(&Image$$RW_IRAM1$$Base, &Load$$RW_IRAM1$$Base, &Image$$RW_IRAM1$$Length);
/* bss clear */
memset(&Image$$RW_IRAM1$$ZI$$Base, 0, &Image$$RW_IRAM1$$ZI$$Length);
}
- uart.c
#include "uart.h"
typedef unsigned int uint32_t;
typedef struct
{
volatile uint32_t SR; /*!< USART Status register, Address offset: 0x00 */
volatile uint32_t DR; /*!< USART Data register, Address offset: 0x04 */
volatile uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */
volatile uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */
volatile uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */
volatile uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */
volatile uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */
} USART_TypeDef;
void uart_init(void)
{
USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;
volatile unsigned int *pReg;
/* 使能GPIOA/USART1模块 */
/* RCC_APB2ENR */
pReg = (volatile unsigned int *)(0x40021000 + 0x18);
*pReg |= (1<<2) | (1<<14);
/* 配置引脚功能: PA9(USART1_TX), PA10(USART1_RX)
* GPIOA_CRH = 0x40010800 + 0x04
*/
pReg = (volatile unsigned int *)(0x40010800 + 0x04);
/* PA9(USART1_TX) */
*pReg &= ~((3<<4) | (3<<6));
*pReg |= (1<<4) | (2<<6); /* Output mode, max speed 10 MHz; Alternate function output Push-pull */
/* PA10(USART1_RX) */
*pReg &= ~((3<<8) | (3<<10));
*pReg |= (0<<8) | (1<<10); /* Input mode (reset state); Floating input (reset state) */
/* 设置波特率
* 115200 = 8000000/16/USARTDIV
* USARTDIV = 4.34
* DIV_Mantissa = 4
* DIV_Fraction / 16 = 0.34
* DIV_Fraction = 16*0.34 = 5
* 真实波特率:
* DIV_Fraction / 16 = 5/16=0.3125
* USARTDIV = DIV_Mantissa + DIV_Fraction / 16 = 4.3125
* baudrate = 8000000/16/4.3125 = 115942
*/
#define DIV_Mantissa 4
#define DIV_Fraction 5
usart1->BRR = (DIV_Mantissa<<4) | (DIV_Fraction);
/* 设置数据格式: 8n1 */
usart1->CR1 = (1<<13) | (0<<12) | (0<<10) | (1<<3) | (1<<2);
usart1->CR2 &= ~(3<<12);
/* 使能USART1 */
}
int getchar(void)
{
USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;
while ((usart1->SR & (1<<5)) == 0);
return usart1->DR;
}
int putchar(char c)
{
USART_TypeDef *usart1 = (USART_TypeDef *)0x40013800;
while ((usart1->SR & (1<<7)) == 0);
usart1->DR = c;
return c;
}
-
exception.h
SCB结构体的定义,参照上篇文章的 exception.h 文件 -
systick.h
typedef struct
{
volatile unsigned int CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
volatile unsigned int LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
volatile unsigned int VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
volatile unsigned int CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
#define SysTick_BASE (0xE000E000 + 0x0010UL)
#define SYSTICK_FRE (8000000)
- systick.c
#include "string.h"
#include "exception.h"
#include "systick.h"
#include "led.h"
void SysTickInit(void)
{
SysTick_Type *SysTickInit = (SysTick_Type *)SysTick_BASE;
/* 1.设置周期:1s钟产生1次异常 晶振8MHz,计时器count值为 val = 1/T ;1/ 1/8M = 8000000*/
SysTickInit->VAL = SYSTICK_FRE;
/* 2.设置LOAD寄存器,重新加载值 */
SysTickInit->LOAD = SYSTICK_FRE;
/* 3.选择时钟源,使能systick,使能异常 */
// 设置bit2,bit1,bit0
SysTickInit->CTRL = (1<<2) | (1<<1) | (1<<0);
}
void SysTick_Handler(void)
{
puts("SysTick_Handler\n\r");
SCB_Type *SCB = (SCB_Type *)SCB_BASE_ADDR;
static int led_on = 0;
/* led */
if (led_on)
{
/* let led off */
LedCtrl(0);
}
else
{
/* let led on */
LedCtrl(1);
}
led_on = !led_on;
/* 清除异常状态 ;设置SCB中的ICSR为1*/
SCB->ICSR |= SCB_ICSR_PENDSTCLR_Msk;
}
- led.c
void LedInit(void)
{
unsigned int *pReg;
/* 使能GPIOB */
pReg = (unsigned int *)(0x40021000 + 0x18);
*pReg |= (1<<3);
/* 设置GPIOB0为输出引脚 */
pReg = (unsigned int *)(0x40010C00 + 0x00);
*pReg |= (1<<0);
}
void LedCtrl(int on)
{
unsigned int *pReg;
pReg = (unsigned int *)(0x40010C00 + 0x0C);
if (on)
{
/* 设置GPIOB0输出0 */
*pReg &= ~(1<<0);
}
else
{
/* 设置GPIOB0输出1 */
*pReg |= (1<<0);
}
}
- main.c 只是打印出一些信息,我们是在汇编文件中实现的led闪烁
#include "uart.h"
#include "string.h"
char g_Char = 'A';
const char g_Char2 = 'B';
int g_A[3] = {0, 0};
char g_B[9];
void delay(volatile int d)
{
while(d--);
}
int mymain()
{
char c;
void (*funcptr)(const char *s, unsigned int val);
static int s_C[16] = {0, 0};
funcptr = put_s_hex;
//uart_init();
//delay(1);
putchar('1');
putchar('0');
putchar('0');
putchar('a');
putchar('s');
putchar('k');
putchar('\n');
putchar('\r');
funcptr("test for text relocate ", 123);
put_s_hex("g_Char's addr = ", &g_Char);
put_s_hex("g_Char2's addr = ", &g_Char2);
put_s_hex("g_A[0]'s val = ", g_A[0]);
put_s_hex("g_B[0]'s val = ", g_B[0]);
put_s_hex("s_C[0]'s val = ", s_C[0]);
putchar(g_Char);
putchar(g_Char2);
while (1)
{
c = getchar();
putchar(c);
putchar(c+1);
}
return 0;
}
- 输出结果: