stm32标准库LF-RFID读卡模块(基于新大陆的低频卡读取,t5557协议下的ic读取)

1.先讲下它的接线吧

 下面V3.3接VCC,引脚中的GND1和GND2中的任何一个接GNDj就行了,让后clk和data随便接个复用功能空出来的引脚就行,它这个是用模拟iic来实现这个功能的,所以引脚可以随便接下,我用的板子时stm32fzet6,clk就直接接PE4,data直接接PE5了。

 2.再来讲下它这个卡吧

外表长这样的低频卡

           

 内部存储如下所示

  它这个卡有两页,分别是1页和0页,它的1页设计是只可以读,不可以写。每个数据块都有32bit(1bit为8位)的存储空间,1页有两个数据块,但对第1页的数据块读取完32bit后,它会进入第1页的另一数据块读取32bit,然后再读32bit后,它就会回到原来的数据块重新读取。下图为第1页各数据块中存放的内容,block1为数据块1,block2为数据块2.

 我们再来看下它这个第0页的,它的所有块都是可读可写的,但它的第0块数据块是用来存储设定的指令的,所以一般情况下的都是只可读的情况。同时它的指令设计为32位(即4bit),但我不知道是它手册少给了还是什么,等大伙看后面代码控制这32位指令位是注意看下就知道了,很奇怪的。

 我们再来看下第0页的其他几块数据块,1到6块都是存数据的,只有第7块有所区别,在我们选择正常读和正常写时,第7块就是存放数据的,但当我们选择保护写和保护读时(即读写需要输入相应密码),第7块的前32位(4bit)即为密码。

 3.如何进行读写的

 p就表示你想选择那一页,总共有2页,当然第1页只能读,L表示锁定位,只有为0的时候才能正常读写,直接访问中的1P*后面那个0也是指锁定位,password就是密码,即32位(4bit),数据data也是32位,addr指地址,因为有7块数据块,刚好可以用3位表示,即addr用来选择数据块,如果你选择直接访问的话,你其实可以读32bit(远远于32位),可你写只能写4位就很奇怪,估计应该可以写更多的,建议大家去看下手册,也不知道后面这个能不能不看,要看的话,直接搜索Ic-T5557数据手册应该搜的出来。

  非接触方式的读/写数据传输 - 豆丁网 (docin.com)

 4.如何然读卡器接受你发送的读写指令

 你发的这些指令读卡器是不能直接识别的,这就需要我们最开始说的模拟iic输入,但它这个是用时间长短来模拟的,就很神奇,所以感觉又和iic模拟不一样,iic是有一根时钟线,还有根数据线的,它这个再写入数据时用clk拉高的时间来确定时写入0还是1,它在读取时就用data的电平跳变来确定读取到的是0还是1,即曼彻斯特编码(Manchester)来读取。下图是如何控制写入的时间来区分0与1

 5.讲了这么多,终于可以讲代码了

首先需要将引脚初始化,读取第一页的内容不管有没有密码都是直接读的,我的printf函数是串口打印,你直接用自己的就行,要串口usart.h函数可以去我那篇lory里面找下。

lf15k.c

#include "stm32f10x.h"
#include <string.h>
#include "systick.h"//这个是用于延时读卡延时的,它这个也许会和一般的delay冲突,所以等下我把delay也放过来
#include "lf125k.h"
#include "usart1.h"

/* Types -----------------------*/
/* Constants -------------------*/
/* Define ----------------------*/
#define REM GPIO_read_lf125k_data()
#define CLK(n) GPIO_set_lf125k_clk(n)
#define	M_OK 0
#define LF_DBR_BASE 
#define LF_1us 72ul
#define LF_720us (720000-LF_1us*720)
#define LF_650us (720000-LF_1us*650)
#define LF_Star (720000-LF_1us*5000)//5ms
#define LF_T557 (720000-LF_1us*350)//350us
#define LF_380us (720000-LF_1us*380)
#define LF_350us (720000-LF_1us*384)//8*48
//#define LF_350us (720000-LF_1us*350)
#define LF_250us (720000-LF_1us*250)
#define LF_200us (720000-LF_1us*200)
#define LF_100us (720000-LF_1us*128)//8*18
//#define LF_100us (720000-LF_1us*100)
#define LF_5ms (720000-LF_1us*5000)//5ms

//低频
#define GPIO_LF125K_DATA GPIOE
#define GPIO_LF125K_DATA_PIN GPIO_Pin_5
#define GPIO_LF125K_CLK GPIOE
#define GPIO_LF125K_CLK_PIN GPIO_Pin_4

typedef enum {    //第一页块的地址,0x00就是第0块,以此类推
	LF125_PAGE0_BANK0=0x00,
	LF125_PAGE0_BANK1=0x01,
	LF125_PAGE0_BANK2=0x02,
	LF125_PAGE0_BANK3=0x03,
	LF125_PAGE0_BANK4=0x04,
	LF125_PAGE0_BANK5=0x05,
	LF125_PAGE0_BANK6=0x06,
	LF125_PAGE0_BANK7=0x07,
}LF125_PAGE0_BANKx;
/* Variables -------------------*/
/* Functions prototypes --------*/
/* Functions -------------------*/
void GPIO_LF125K_configuration(void)//GPIO引脚的初始化
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_LF125K_CLK_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIO_LF125K_CLK, &GPIO_InitStructure);
	GPIO_InitStructure.GPIO_Pin = GPIO_LF125K_DATA_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIO_LF125K_DATA, &GPIO_InitStructure);
}
/*
 *@brief	.
 *@param	.
 *@retval	.
*/	
void GPIO_set_lf125k_clk (uint8_t status)//改变clk的值
{
	if (status)
		GPIO_WriteBit(GPIO_LF125K_CLK, GPIO_LF125K_CLK_PIN,Bit_SET);
	else
		GPIO_WriteBit(GPIO_LF125K_CLK, GPIO_LF125K_CLK_PIN,Bit_RESET);
}
/*
 *@brief	.
 *@param	.
 *@retval	.
*/	
uint8_t GPIO_read_lf125k_data (void)//读取data的值
{
	return GPIO_ReadInputDataBit(GPIO_LF125K_DATA, GPIO_LF125K_DATA_PIN);
}

uint8_t volue;//volue 为曼侧斯特码译码时的临时运算缓存
uint8_t tap;//tap 为曼侧斯特码发射时的临时运算缓存
uint8_t data_tap[6];//等待发射的数据
//打开天线(这个叫法怪怪的)
void LF125K_open_RF(void)
{
	CLK(1);
}
//关闭天线
void LF125K_close_RF(void)//
{
	CLK(0);
}
void LF125K_init(void)//初始化引脚和打开clk
{
	GPIO_LF125K_configuration();
	LF125K_open_RF();
//	DBG_B_INFO("LF125K初始化成功");
}
/*
 *@brief	Manchester process.曼侧斯特
 *@param	.
 *@retval	0:success
 		1:failse.
*/	
int8_t REM_Processing(uint8_t *buf)//读取8位数据,后面i值可以根据自己想要进行更改,但注意buf大小的改变
{
	uint32_t i,j,delay;
	long tus;
	for(i=0;i<50;i++){//find the first cync head 在规定时间内获得data的一个下降沿
		volue=0;
		delay = 7200;
		while(REM == 0 && delay)//wait high level
			delay --;
		if (delay == 0) {
			return 1;//timeout
		}
		SYSTICK_ClearSystickVal();
		while(REM == 1){
			if(SYSTICK_GetVal() > LF_720us){
				
			}else{
				i = 50;
				break;
			}
		}
   	}
	delay = 7200;
	while(REM == 1 && delay)//wait low level //通过两个while获得data的一个上升沿
		delay --;
	if (delay == 0) {
		return 1;
	}
	delay = 72000;
	while(REM == 0 && delay)//wait high level
		delay --;
	if (delay == 0) {
		return 1;
	}
	SYSTICK_ClearSystickVal();
	tus = LF_650us;
	for(i=0;i<8;i++)//开始读取数据
	{
		for(j=0;j<8;j++)
		{
			while(SYSTICK_GetVal() > tus);
			if(REM==1)//一个下降沿为一个1
			{
				delay = 7200;
				while(REM==1 && delay){
					delay --;
				}
				if(delay == 0)
					return 1;
				volue>>=1;
				volue|=0x80;
				tus = LF_T557;
				SYSTICK_ClearSystickVal();
			}
			else
			{
				delay = 7200;
				while(REM==0 && delay){//一个上升沿为一个0
					delay --;
				}
				if(delay == 0)
					return 1;
				volue>>=1;
				volue|=0x00;
				tus = LF_T557;
				SYSTICK_ClearSystickVal();
			}
		}
		buf[i]=volue;
		volue=0x00;
	}
	return 0;
}
void Data_Processing(void)//用于clk数据的写入 250us延时表示间隔 350us的clk拉高表示写入1 100us的clk拉高表示写入0
{
	if(tap==0x01){
		SYSTICK_ClearSystickVal();
		while(SYSTICK_GetVal()>LF_350us);
		CLK(0);
	 }else{
		SYSTICK_ClearSystickVal();
		while(SYSTICK_GetVal()>LF_100us);
		CLK(0);
	 }
	SYSTICK_ClearSystickVal();
	while(SYSTICK_GetVal()>LF_250us);//拉低250us表示间隔
	CLK(1);//再重新拉高用于写入
}
void write_od(uint8_t voled)//1P*与00的写入(即写入与读取的前两位)
{
	SYSTICK_ClearSystickVal();
	while(SYSTICK_GetVal()>LF_Star);
	CLK(0);//
	SYSTICK_ClearSystickVal();
	while(SYSTICK_GetVal()>LF_350us);
	CLK(1);
	tap=voled;
	tap&=0x10;
	if(tap==0x10){ //两个data_processing ,写入两位
		tap=0x01;
		Data_Processing();//
	}else{
		tap=0x00;
		Data_Processing();//
	}
	tap=voled;
	tap&=0x01;
	Data_Processing();//	
}
void  write_lock(uint8_t voled)//写入锁定位
{
	tap=voled;
	tap&=0x01;
	Data_Processing();
}
void write_data() //写入4bit数据 即32位
{
	uint8_t i,j,voled_1;
	for(j=0;j<4;j++)
	{
		voled_1=data_tap[j];
		for(i=0;i<8;i++)
		{ 
			tap=voled_1;
			tap&=0x01;
			Data_Processing();//
			voled_1>>=1;
		}
	}
}
void write_add(uint8_t voled) //写入页的块地址
{
	tap=voled;
	tap&=0x04;
	if(tap==0x04) //三个data_processing ,写入三位
	{
		tap=0x01;
		Data_Processing();//
	}
	else 
	{
		tap=0x00;
		Data_Processing();//
	}
	tap=voled;
	tap&=0x02;
	if(tap==0x02)
	{
		tap=0x01;
		Data_Processing();//
	}
	else 
	{
		tap=0x00;
		Data_Processing();//
	}
	tap=voled;
	tap&=0x01;
	Data_Processing();//	
}
int8_t LF125K_read_1_page(uint8_t* buf) //读取第一页,正常读,直接读的第1页第1块的内容,即自带的id
{
	uint8_t buf1[8];
	memset(buf, 0, sizeof(buf1));
	write_od(0x11);
	SYSTICK_Delay10ms(1);
	if(REM_Processing(buf))
	return 1;
	SYSTICK_Delay10ms(5);
	write_od(0x11);//读取两次防止出错
	SYSTICK_Delay10ms(1);
	if(REM_Processing(buf1))
		return 1;
	if (memcmp(buf1,buf,8) != 0){
		return 1;
	}
	return 0;
}
uint8_t jj=0;
void LF125K_demo(void)
{
	int ret;
  uint8_t buf[8];
	//uint8_t pwd[4]={0x80,0x82,0x0,0x0};//写密码是用的,设置的密码
	memset(buf, 0, sizeof(buf));
	printf("低频卡测试\r\n");
	ret=LF125K_read_1_page(buf);//读取第一页第1块的内容
	//ret=LF125K_read_0_page_protect(0x01,buf,pwd);//用密码读取第0页第6块的内容
	//ret=LF125K_read_0_page(0x06,buf);//0x06表示块的地址,可以更改
	if(ret == 0)
	{
	  printf("低频卡获取成功!");
		for(jj=0;jj<1;jj++){
			printf("id card:%X %X %X %X %X %X %X %X\r\n", buf[0+jj*8], buf[1+jj*8], buf[2+jj*8], buf[3+jj*8], buf[4+jj*8], buf[5+jj*8], buf[6+jj*8], buf[7+jj*8]);
		}
	}
	else
	{
	 printf("低频卡获取失败!");
	}
}

int8_t LF125K_read_0_page(uint8_t adr,uint8_t* buf) //无密码的读
{
	uint8_t  buf1[8];
	adr &= 0x7;
	write_od(0x10);
	write_lock(0x00);
	write_add(adr);
	SYSTICK_Delay10ms(2);
	REM_Processing(buf1);
	SYSTICK_Delay10ms(2);
	write_od(0x10);
	write_lock(0x00);
	write_add(adr);
	SYSTICK_Delay10ms(2);
	REM_Processing(buf);
	if(memcmp(buf1,buf,8)!=0)	
   		return M_OK+1;
	return M_OK;
}
int8_t LF125K_write_0_page(unsigned char adr,unsigned char *buf) //无密码的写
{
	uint8_t  buf1[10];
	uint8_t lock=0;
	if((adr&0x80) == 0x80)//防止超出块大小
		lock = 1;
	adr &=0x07;
	if(adr==0x00)//块0 配置区 防止误操作
		return M_OK+1;
	
	write_od(0x10); 
	write_lock(lock);//写1固化 
	data_tap[0]=buf[0];
	data_tap[1]=buf[1];
	data_tap[2]=buf[2];
	data_tap[3]=buf[3];
	write_data();
	write_add(adr);
	SYSTICK_Delay10ms(2);
	write_od(0x10);
	write_lock(0x00);
	write_add(adr);
	SYSTICK_Delay10ms(2);
	REM_Processing(buf1);
	if(memcmp(buf1,buf,4)!=0)	//重新读出,防止写入错误
   		return M_OK+1;
	return M_OK;
}


/*
 *@brief	带密码写块数据.
 *@param	.
 *@retval	.
*/
void LF125K_write_0_page_protect(uint8_t adr,uint8_t *buf,uint8_t *pwd)
{
	uint8_t lock=0;
	if ((adr&0x80) == 0x80) {
		lock = 1;
	}
	write_od(0x10);
	data_tap[0]=pwd[0];
	data_tap[1]=pwd[1];
	data_tap[2]=pwd[2];
	data_tap[3]=pwd[3];
	write_data();//
	write_lock(lock);
	
	data_tap[0]=buf[0];
	data_tap[1]=buf[1];
	data_tap[2]=buf[2];
	data_tap[3]=buf[3];
	write_data();
	write_add(adr);
	SYSTICK_Delay10ms(1);
}


/*
 *@brief	带密码读块数据.
 *@param	.
 *@retval	.
*/	
int8_t LF125K_read_0_page_protect(uint8_t adr,uint8_t *buf,uint8_t *pwd)
{
	write_od(0x10);
	data_tap[0]=pwd[0];
	data_tap[1]=pwd[1];
	data_tap[2]=pwd[2];
	data_tap[3]=pwd[3];
	write_data();
	write_lock(0x00);
	write_add(adr);
	SYSTICK_Delay10ms(1);
	if(REM_Processing(buf))
		return 1;	
	return 0;
}

/*
 *@brief	无密码初始化,初始一次就行了
 *@param	.
 *@retval	0:success
 		1:false.
*/	
void LF125K_card_init(void)//对第0页的块0内容进行设置,可是你去比较这值你会发现很多对不上的
{
	write_od(0x10);
	write_lock(0x00);
	data_tap[0]=0x00;
	data_tap[1]=0x28;//0x28;
	data_tap[2]=0x01;
	data_tap[3]=0x17;
	write_data();
	write_add(0x00);
}
/*
 *@brief	加密卡片.
 *@param	.
 *@retval	.
*/	
void LF125K_set_password (void)//对第0页的块0内容进行设置,可是你去比较这值你会发现很多对不上的
{
	write_od(0x10);
	write_lock(0x00);
	data_tap[0]=0x00;
	data_tap[1]=0x28;
	data_tap[2]=0x41;
	data_tap[3]=0x1f;
	write_data();
	write_add(0x00);
}
/*
 *@brief	清除卡片密码.
 *@param	.
 *@retval	.
*/	
int8_t LF125K_clear_pwd (uint8_t *pwd)
{
	uint8_t buf[10];
	write_od(0x10);
	data_tap[0]=pwd[0];
	data_tap[1]=pwd[1];
	data_tap[2]=pwd[2];
	data_tap[3]=pwd[3];
	write_data();
	write_lock(0x00);
	data_tap[0]=0x00;
	data_tap[1]=0x28;
	data_tap[2]=0x01;
	data_tap[3]=0x17;
	write_data();
	write_add(0);
	SYSTICK_Delay10ms(1);
	if(REM_Processing(buf))
		return 1;	
	return 0;
}

 lf15k.h

#ifndef __LF125K_H
#define __LF125K_H
/* Includes --------------------*/
#include "stm32f10x.h"

/* Types -----------------------*/
/* Constants -------------------*/
/* Define ----------------------*/
/* Variables -------------------*/
/* Functions prototypes --------*/
extern void LF125K_init(void);
extern void LF125K_demo(void);
void LF125K_set_password (void);

//adr(0~7), buf(return 4 bytes)
extern int8_t LF125K_read_0_page(uint8_t adr,uint8_t *buf);

//adr(0~7), buf(4 bytes)
extern int8_t LF125K_write_0_page(uint8_t adr,uint8_t *buf);

//return 8 bytes
extern int8_t LF125K_read_1_page(uint8_t *buf);

extern void LF125K_card_init(void);
extern int8_t LF125K_set_pwd (uint8_t *pwd);
extern int8_t LF125K_clear_pwd (uint8_t *pwd);
extern void LF125K_write_0_page_protect(uint8_t adr,uint8_t *buf,uint8_t *pwd);
extern int8_t LF125K_read_0_page_protect(uint8_t adr,uint8_t *buf,uint8_t *pwd);
#endif

systick.c

/*************************************************************************
#    FileName: bsp_systick.c
#      Author: Allen
#       Email: qiurenguo@gmail.com
#    HomePage: Allen
#       Brief: 
#  LastChange: 2014-05-09 11:21:53
*************************************************************************/
/* Includes ------------------------------------------------------------*/
#include <stdio.h>
#include "stm32f10x.h"
#include "systick.h"

/* Types ---------------------------------------------------------------*/
/* Constants -----------------------------------------------------------*/
/* Define --------------------------------------------------------------*/
/* Variables -----------------------------------------------------------*/
volatile long jiffies=0;
/* Functions prototypes ------------------------------------------------*/
/* Functions -----------------------------------------------------------*/
/*
@brief  systick.
@param  None.
@retval None.
*/

//#include "debug_printf.h"


void SYSTICK_NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	
	NVIC_InitStructure.NVIC_IRQChannel = (uint8_t)SysTick_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);	
}


void SYSTICK_Configuration (void)
{
	jiffies = 0;
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
	if (720000 > SysTick_LOAD_RELOAD_Msk)  while(1);/* Reload value impossible */
	SysTick->LOAD  = (720000 & SysTick_LOAD_RELOAD_Msk) - 1;/* set reload register */
	//NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);/* set Priority for Cortex-M0 System Interrupts */
	SysTick->VAL   = 0;/* 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 */
// 	if(SysTick_Config(720000) == 1){//10ms
// 		printf("\r\nsystick fail");
// 		while(1);
// 	}	
}

void SYSTICK_init(void)
{
	//systick NVIC init
	SYSTICK_NVIC_Configuration();

	//systick init
	SYSTICK_Configuration();
}

void SYSTICK_IntDisable(void)
{
	SysTick->CTRL &=(~SysTick_CTRL_TICKINT_Msk);
}
void SYSTICK_IntEnable(void)
{
	SysTick->CTRL |=(SysTick_CTRL_TICKINT_Msk);
}
void SYSTICK_ClearSystickVal (void)
{
	SysTick->VAL = 0;
}
long SYSTICK_GetVal(void)
{
	return SysTick->VAL;
}
void SYSTICK_Delay10ms(long i)
{
	long jif;
	SysTick->VAL = 0;
	jif = i+jiffies;
	while(jif>jiffies);
}

long SYSTICK_get_time(void)
{
	return jiffies;
}


/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */

void SysTick_Handler(void)
{
	//10ms one time

	if(++jiffies>3000000)
		jiffies=0;
}



/*********************************END OF FILE****************************/


systick.h 

*************************************************************************/
/* Define to prevent recursive inclusion -------------------------------*/
#ifndef SYSTICK_H
#define SYSTICK_H

/* Includes ------------------------------------------------------------*/
/* Exported types ------------------------------------------------------*/
/* Exported constants --------------------------------------------------*/
/* Exported define -----------------------------------------------------*/
/* Exported variables --------------------------------------------------*/
extern volatile long jiffies;
/* Exported functions prototypes ---------------------------------------*/

void SYSTICK_init(void);
extern void SYSTICK_Configuration (void);
extern void SYSTICK_Delay10ms(long i);
extern void SYSTICK_ClearSystickVal (void);
extern long SYSTICK_GetVal(void);
extern long SYSTICK_get_time(void);


//extern void delay_ms(int ms);
//extern void delay_us(int us);

#endif 
/*********************************END OF FILE****************************/


 main.c

 若果你只读第1页的内容,你就不需要改了,如果你要都第0页的内容,    //LF125K_card_init();//初始化值需要一次,一次后它会自动保存 把这个函数注释取消,主要这个只要一次就行,但你放着也没事,还有就是void LF125K_demo(void)函数中把读第1页的给注释掉,第0页的读取注释取消。带密码读的,就将main和void LF125K_demo(void)函数中的pwd数组注释取消,同时需要密码初始化一次,都数据选择密码读取,先消除密码读取也就只要消除一次就行,读取函数改下就行。需要特别注意的是,你用密码读前先将第0页第7块的内容读取下,否则它是与第0页第7块内容进行比较的,你读取和清楚密码都是需要密码的,你若不小心忘记了密码,那就啥都干不了了。你想改密码也是直接修改第0页第7块前32位就行了。

#include "stm32f10x.h"
#include "lf125k.h"
#include "delay.h"
#include "systick.h"
#include "usart1.h"

int main(void)
{
	//uint8_t pwd[8]={0x80,0x82,0x0,0,0,0,0,0};//设置密码用的
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级
  uart_init(115200);
	delay_init();
	SYSTICK_init();
	LF125K_init();
	//LF125K_set_password();
	//LF125K_card_init();//初始化值需要一次,一次后它会自动保存
	//LF125K_clear_pwd (pwd);//设置密码后进行密码的清除
	while(1)
	{
		LF125K_demo();
		delay_ms(1000);
	}
}

 Delay.c

/******************************************************************************
* @ File name --> delay.c
* @ Author    --> By@ Sam Chan
* @ Version   --> V1.0
* @ Date      --> 10 - 20 - 2012
* @ Brief     --> 系统延时相关的函数
*
* @               详细说明请参考《Cortex-M3权威指南(中文)》第133 ~ 134页 第8章 SysTick定时器介绍
*
* @ Copyright (C) 20**
* @ All rights reserved
*******************************************************************************
*
*                                  File Update
* @ Version   --> V1.0.1
* @ Author    --> By@ Sam Chan
* @ Date      --> 02 - 26 - 2014
* @ Revise    --> 增加另外一种延时计算方法
*
* @ Version   --> V1.0.2
* @ Author    --> By@ Sam Chan
* @ Date      --> 05 - 10 - 2014
* @ Revise    --> 增加对C++环境支持
*
* @ Version   --> V1.1
* @ Author    --> By@ Sam Chan
* @ Date      --> 05 - 24 - 2014
* @ Revise    --> 修改在跑ucos时初始化嘀嗒定时器、延时us和ms函数
*
******************************************************************************/

/******************************************************************************

* @ SysTick定时器 相关控制寄存器说明

@ 1、SysTick控制及状态寄存器(地址:0xE000_E010)复位值为0

	bit16 COUNTFLAG(R)  -> 如果在上次读取本寄存器后,SysTick已经数到了0,则该位为1。如果读取该位,该位将自动清零
	bit2  CLKSOURCE(R/W) -> 0=外部时钟源(STCLK)。1=内核时钟(FCLK)
	bit1  TICKINT(R/W)   -> 1=SysTick倒数到0时产生SysTick异常请求,0=数到0时无动作 
	bit0  ENABLE(R/W)    -> SysTick定时器的使能位

@ 2、SysTick重装载数值寄存器(地址:0xE000_E014)复位值为0

	[23:0] RELOAD(R/W) -> 当倒数至零时,将被重装载的值

@ 3、SysTick当前数值寄存器(地址:0xE000_E018) 复位值为0

	[23:0] CURRENT(R/Wc) -> 读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick 控制及状态寄存器中的COUNTFLAG标志

@ 4、SysTick校准数值寄存器(地址:0xE000_E01C)复位值: bit31未知。bit30未知。[23:0]为0

	bit32 NOREF(R)    -> 1=没有外部参考时钟(STCLK不可用)。0=外部参考时钟可用
	bit30 SKEW(R)     -> 1=校准值不是准确的10ms。0=校准值是准确的10ms
	[23:0] TENMS(R/W) -> 10ms的时间内倒计数的格数。芯片设计者应该通过Cortex‐M3的输入信号提供该数值。若该值读回零,则表示无法使用校准功能
	
******************************************************************************/ 

#include "delay.h"

/******************************************************************************
                   使用嵌入式操作系统时初始化心跳函数等
******************************************************************************/

#if _SYSTEM_SUPPORT_ROTS	//定义了则支持实时嵌入式操作系统
	#include "includes.h"	//增加操作系统需要头文件

/******************************************************************************
* Function Name --> SysTick定时器心跳初始化
* Description   --> 主要是初始化SysTick寄存器 
* Input         --> none
* Output        --> none
* Reaturn       --> none 
******************************************************************************/
void OS_Heart_Init(void)
{
	delay_init();
}
/******************************************************************************
* Function Name --> SysTick定时器中断服务函数
* Description   --> 在此编写了,则stm32f10x_it.c中就不需要编写,否则stm32f10x_it.c中就要编写
* Input         --> none
* Output        --> none
* Reaturn       --> none 
******************************************************************************/
void SysTick_Handler(void)
{
	/* 编写与SysTick定时器中断操作相关的API函数调用 */

	OSIntEnter();  //ucos进入中断
	OSTimeTick();  //调用ucos的时钟服务函数
	OSIntExit();  //ucos退出中断
} 

#endif  /* end _SYSTEM_SUPPORT_ROTS */

/******************************************************************************
                          结束嵌入式操作系统心跳设置
******************************************************************************/



//=========================================================
#if _USER_SysTick==1	//定义了则使用SysTick定时器做延时函数计数
//=========================================================

/******************************************************************************
                               定义计算变量
******************************************************************************/

static uint8_t  fac_us=0;	//us延时倍乘数
static uint16_t fac_ms=0;	//ms延时倍乘数

/******************************************************************************
* Function Name --> 初始化延时函数
* Description   --> 主要是初始化SysTick寄存器 
* Input         --> none
* Output        --> none
* Reaturn       --> none 
******************************************************************************/
void delay_init(void)
{
#if _SYSTEM_SUPPORT_ROTS  //运行在ucos上
	uint32_t RELOAD=0;	//当计数器倒数到0时的重装值,有效位:0 ~ 23
#endif

	/* 根据SysTick定时器的时钟分频来确定重装值 */
	/* 8分频时除以8000‘000,1分频时除以1000’000 */
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);  //选择外部时钟 HCLK / 8

	fac_us = SystemCoreClock / 8000000;  //系统时钟的 1/8

#if _SYSTEM_SUPPORT_ROTS  //运行在ucos上
	
	RELOAD = SystemCoreClock / 8000000;	//每秒钟的计数次数,单位Hz
	RELOAD *= 1000000 / OS_TICKS_PER_SEC;	//根据操作系统的心跳时长来计算溢出时间,单位:KHz
	                                    //RELOAD为24位计数器,最大值为:16777216 

	fac_ms = 1000 / OS_TICKS_PER_SEC;

	SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;	//开启SysTick定时器中断请求
	SysTick->LOAD = RELOAD;	//溢出计数值,每1/TICKINT_CNT秒中断一次
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;      //开始倒数

#else	    
	fac_ms = (uint16_t)fac_us*1000;	//ms需要的SysTick时钟数
#endif  /* end _SYSTEM_SUPPORT_ROTS */
}
/******************************************************************************
* Function Name --> 延时n个us
* Description   --> none
* Input         --> nus:要延时的us数
* Output        --> none
* Reaturn       --> none 
******************************************************************************/	    								   
void delay_us(uint32_t nus)
{		
	uint32_t temp=0;

#if _SYSTEM_SUPPORT_ROTS  //运行在ucos上

	uint32_t VAL_Prev=0;  //开始计时之前的值
	uint32_t VAL_Now=0;   //当前计时值
	uint32_t VAL_cnt=0;   //计数
	uint32_t Reload=SysTick->LOAD;  //获取到LOAD的值

	temp = nus*fac_us;  //得到延时的节拍数
	VAL_Prev = SysTick->VAL;  //保存当前的计数值

	while(1)
	{
		VAL_Now = SysTick->VAL;  //读取数值
		if(VAL_Now != VAL_Prev)
		{
			if(VAL_Now < VAL_Prev)  VAL_cnt += VAL_Prev-VAL_Now;  //因为SysTick是一个递减的定时器
			else                      VAL_cnt += Reload - VAL_Now + VAL_Prev;

			VAL_Prev = VAL_Now;  //刷新
			if(VAL_cnt >= temp)  break;  //超过/等于需要的延时值了,则退出循环
		}
	};

#else
		    	 
	SysTick->LOAD = nus*fac_us; //时间加载	  		 
	SysTick->VAL = 0x00;        //清空计数器
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;      //开始倒数 	 
	do
	{
		temp = SysTick->CTRL;
	}while(temp&0x01&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL = 0x00;	//清空计数器	

#endif  /* end _SYSTEM_SUPPORT_ROTS */
}
/******************************************************************************
* Function Name --> 延时n个ms
* Description   --> SysTick->LOAD为24位寄存器,所以,最大延时为:
*                   nms <= 0xffffff*8*1000/SYSCLK
*                   SYSCLK单位为Hz,nms单位为ms
*                   注意nms的范围 0 ~ 1864(72M情况下)
* Input         --> nms:要延时的ms数
* Output        --> none
* Reaturn       --> none 
******************************************************************************/	
void delay_ms(uint16_t nms)
{
#if _SYSTEM_SUPPORT_ROTS  //使用ucos了

	if(OSRunning == 1)  //ucos已经在跑了
	{
		if(nms > fac_ms)  //延时大于ucos基数
		{
			OSTimeDly(nms/fac_ms);  //采用ucos延时
		}
		nms %= fac_ms;  //ucos无法提供小于节拍的延时了
	}
	delay_us((uint32_t)(nms*1000));  //采用普通的延时

#else

	uint32_t temp;
			   
	SysTick->LOAD = (uint32_t)nms*fac_ms;	//时间加载(SysTick->LOAD为24bit)
	SysTick->VAL = 0x00;           //清空计数器
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;      //开始倒数  
	do
	{
		temp = SysTick->CTRL;
	}while(temp&0x01&&!(temp&(1<<16)));	//等待时间到达   
	SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;	//关闭计数器
	SysTick->VAL = 0x00;	//清空计数器	 

#endif  /* end _SYSTEM_SUPPORT_ROTS */
}


//=========================================================
#else	//使用另外资源进行延时计数
//=========================================================


/******************************************************************************
* Function Name --> 初始化延时函数
* Description   --> 主要Cortex-M3内核对系统时钟计数单元
*                   详细见《Cortex-M3权威指南(中文)》第216页 a)  时钟周期(CYCCNT) 的内容
*                   周立功《CM3计数参考手册》第28、29页、第110、125页
* Input         --> SYSCLK:系统工作最高的频率。单位MHz
* Output        --> none
* Reaturn       --> none 
******************************************************************************/
void delay_init(void)
{
	DEM_CTRL |= 1<<24;	//该位必须为1,使能跟踪和调试模块的使用。详细见:周立功《CM3计数参考手册》第115页介绍
						//在没有使用跟踪时,该位使能对功率使用的控制。它能够由应用程序或调试器使能,供ITM使用
						//在DWT能够使用之前,调试异常和监控控制寄存器的TRCENA(bit24)位必须置位

	DWT_CTRL |= 1<<0;	//使能DWT_CYCCNT计数器。
						//如果不使能,则计数器不执行计数操作,因此不会产生PC采样或CYCCNTENA事件。
						//在正常使用时,CYCCNT计数器应由调试器初始化为0。
}
/******************************************************************************
* Function Name --> 延时n个us
* Description   --> none
* Input         --> nus:要延时的us数
* Output        --> none
* Reaturn       --> none 
******************************************************************************/	
void delay_us(uint32_t nus)
{
	uint32_t savecount,endcnt,CPU_cnt;

	savecount = DWT_CYCCNT;	//保存计数器当前数值
	CPU_cnt = nus*(SystemCoreClock/(1000*1000));	//计算达到所需延时值的CPU时钟数。即多少个系统时钟计数
											//得到更精确延时时间,减去前面代码运行的时间即可

	endcnt = savecount + CPU_cnt;	//计算所需延时时间DWT_CYCCNT的计数值,在溢出时返回到0
	
	if(endcnt > savecount)	//所需延时值大于当前计数值
	{
		while(DWT_CYCCNT < endcnt);	//循环等待所需要的延时时间的CPU时钟计数值
	}
	else	//小于当前计数值
	{
		while(DWT_CYCCNT > endcnt);	//等待计数器溢出翻转
		while(DWT_CYCCNT < endcnt);	//等待所需延时时间到达
	}
}

void sdelay(uint32_t s)
{
	uint8_t i;
	while(s--)
	{
		i=1;
		while(i--);
	}
}

//=========================================================
#endif
//=========================================================





 delay.h

/******************************************************************************
* @ File name --> delay.h
* @ Author    --> By@ Sam Chan
* @ Version   --> V1.0
* @ Date      --> 10 - 20 - 2012
* @ Brief     --> 系统延时相关的函数
*
* @               详细说明请参考《Cortex-M3权威指南(中文)》第133 ~ 134页 第8章 SysTick定时器介绍
*
* @ Copyright (C) 20**
* @ All rights reserved
*******************************************************************************
*
*                                  File Update
* @ Version   --> V1.0.1
* @ Author    --> By@ Sam Chan
* @ Date      --> 02 - 26 - 2014
* @ Revise    --> 增加另外一种延时计算方法
*
* @ Version   --> V1.0.2
* @ Author    --> By@ Sam Chan
* @ Date      --> 05 - 10 - 2014
* @ Revise    --> 增加对C++环境支持
*
* @ Version   --> V1.1
* @ Author    --> By@ Sam Chan
* @ Date      --> 05 - 24 - 2014
* @ Revise    --> 修改在跑ucos时初始化嘀嗒定时器、延时us和ms函数
*
******************************************************************************/

#ifndef _delay_h_
#define _delay_h_

/*===========================================================================*/
#ifdef __cplusplus  /* C++支持 */
	extern "C"{
#endif
/*===========================================================================*/

/******************************************************************************
                               外部函数头文件                        
******************************************************************************/

#include "sys.h"

/******************************************************************************
                           延时函数计数来源定义
******************************************************************************/

#if _SYSTEM_SUPPORT_ROTS==1  //运行在ucos上
	#define _USER_SysTick             1  //1:使用SysTick定时器做延时函数计数
#else

#define _USER_SysTick					0	/* 定义是否使用SysTick定时器做延时计数函数 */
											//0:使用其他方式
											//1:使用SysTick定时器做延时函数计数

#endif
/******************************************************************************
                           定义其他延时计数资源
******************************************************************************/

#if !_USER_SysTick	//使用其他方式做延时函数计数

#define DWT_CTRL				*(volatile uint32_t*)0xe0001000	//DWT控制寄存器
#define DWT_CYCCNT				*(volatile uint32_t*)0xe0001004	//DWT当前PC采样周期计数寄存器
																//详细见:周立功《CM3计数参考手册》第129页介绍
#define DWT_CPICNT				*(volatile uint32_t*)0xe0001008	//DWT当前CPI计数寄存器
#define DEM_CTRL				*(volatile uint32_t*)0xe000edfc	//调试异常和监控控制寄存器

#endif

/******************************************************************************
                                 外部功能函数
******************************************************************************/

#if _USER_SysTick==1	//定义了则使用SysTick定时器做延时函数计数

void delay_init(void);	//初始化延时函数
void delay_us(uint32_t nus);	//延时n个us
void delay_ms(uint16_t nms);	//延时n个ms

#else	//否则使用其他方式

void delay_init(void);//void delay_init(uint8_t SYSCLK);	//初始化延时函数
void delay_us(uint32_t nus);  //延时n个us

//延时ms级定义,延时范围:1 ~ 65535ms。延时最大值可变,不爆机uint32_t/1000范围即可
#define delay_ms(nms)			delay_us((uint16_t)nms*1000)

#endif	//end _USER_SysTick

#if _SYSTEM_SUPPORT_ROTS==1	//定义了则支持实时嵌入式操作系统

void OS_Heart_Init(void);	//SysTick定时器心跳初始化

#endif

void sdelay(uint32_t s);

/*===========================================================================*/
#ifdef __cplusplus  /* C++支持 */
	}
#endif
/*===========================================================================*/


#endif  /* end delay.h */





  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值