搜档网
当前位置:搜档网 › STM32F103驱动MFRC522

STM32F103驱动MFRC522

STM32F103驱动MFRC522
STM32F103驱动MFRC522

//MF522命令字

/////////////////////////////////////////////////////////////////////

#ifndef __rc522_H

#define __rc522_H

#include "sys.h"

#include

#include "delay.h"

#include

#define PCD_IDLE 0x00 //取消当前命令

#define PCD_AUTHENT 0x0E //验证密钥

#define PCD_RECEIVE 0x08 //接收数据

#define PCD_TRANSMIT 0x04 //发送数据

#define PCD_TRANSCEIVE 0x0C //发送并接收数据

#define PCD_RESETPHASE 0x0F //复位

#define PCD_CALCCRC 0x03 //CRC计算

/////////////////////////////////////////////////////////////////////

//Mifare_One卡片命令字

/////////////////////////////////////////////////////////////////////

#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态#define PICC_REQALL 0x52 //寻天线区内全部卡

#define PICC_ANTICOLL1 0x93 //防冲撞

#define PICC_ANTICOLL2 0x95 //防冲撞

#define PICC_AUTHENT1A 0x60 //验证A密钥

#define PICC_AUTHENT1B 0x61 //验证B密钥

#define PICC_READ 0x30 //读块

#define PICC_WRITE 0xA0 //写块

#define PICC_DECREMENT 0xC0 //扣款

#define PICC_INCREMENT 0xC1 //充值

#define PICC_RESTORE 0xC2 //调块数据到缓冲区

#define PICC_TRANSFER 0xB0 //保存缓冲区中数据

#define PICC_HALT 0x50 //休眠

/////////////////////////////////////////////////////////////////////

//MF522 FIFO长度定义

/////////////////////////////////////////////////////////////////////

#define DEF_FIFO_LENGTH 64 //FIFO size=64byte

#define MAXRLEN 18

/////////////////////////////////////////////////////////////////////

//MF522寄存器定义

// PAGE 0

#define RFU00 0x00 #define CommandReg 0x01 #define ComIEnReg 0x02 #define DivlEnReg 0x03 #define ComIrqReg 0x04 #define DivIrqReg 0x05

#define ErrorReg 0x06 #define Status1Reg 0x07 #define Status2Reg 0x08 #define FIFODataReg 0x09 #define FIFOLevelReg 0x0A #define WaterLevelReg 0x0B #define ControlReg 0x0C #define BitFramingReg 0x0D #define CollReg 0x0E #define RFU0F 0x0F // PAGE 1

#define RFU10 0x10 #define ModeReg 0x11 #define TxModeReg 0x12 #define RxModeReg 0x13 #define TxControlReg 0x14

#define TxAutoReg 0x15 #define TxSelReg 0x16 #define RxSelReg 0x17 #define RxThresholdReg 0x18 #define DemodReg 0x19 #define RFU1A 0x1A #define RFU1B 0x1B #define MifareReg 0x1C #define RFU1D 0x1D #define RFU1E 0x1E #define SerialSpeedReg 0x1F

// PAGE 2

#define RFU20 0x20 #define CRCResultRegM 0x21 #define CRCResultRegL 0x22 #define RFU23 0x23 #define ModWidthReg 0x24 #define RFU25 0x25 #define RFCfgReg 0x26 #define GsNReg 0x27

#define CWGsCfgReg 0x28

#define ModGsCfgReg 0x29

#define TModeReg 0x2A

#define TPrescalerReg 0x2B

#define TReloadRegH 0x2C

#define TReloadRegL 0x2D

#define TCounterValueRegH 0x2E

#define TCounterValueRegL 0x2F

// PAGE 3

#define RFU30 0x30

#define TestSel1Reg 0x31

#define TestSel2Reg 0x32

#define TestPinEnReg 0x33

#define TestPinValueReg 0x34

#define TestBusReg 0x35

#define AutoTestReg 0x36

#define VersionReg 0x37

#define AnalogTestReg 0x38

#define TestDAC1Reg 0x39

#define TestDAC2Reg 0x3A

#define TestADCReg 0x3B

#define RFU3C 0x3C

#define RFU3D 0x3D

#define RFU3E 0x3E

#define RFU3F 0x3F

///////////////////////////////////////////////////////////////////// //和MF522通讯时返回的错误代码

///////////////////////////////////////////////////////////////////// #define MI_OK 0

#define MI_NOTAGERR 1//(-1)

#define MI_ERR 2//(-2)

#define SHAQU1 0X01

#define KUAI4 0X04

#define KUAI7 0X07

#define REGCARD 0xa1

#define CONSUME 0xa2

#define READCARD 0xa3

#define ADDMONEY 0xa4

#define spi_cs PEout(4)

#define spi_ck PEout(3)

#define spi_mosi PEout(2)

#define spi_miso PEin(1)

#define spi_rst PEout(0)

#define SET_SPI_CS spi_cs=1

#define CLR_SPI_CS spi_cs=0

#define SET_SPI_CK spi_ck=1

#define CLR_SPI_CK spi_ck=0

#define SET_SPI_MOSI spi_mosi=1

#define CLR_SPI_MOSI spi_mosi=0

#define STU_SPI_MISO spi_miso

#define SET_RC522RST spi_rst=1

#define CLR_RC522RST spi_rst=0

extern unsigned char LastKeyA[6];//NO.2卡

extern unsigned char NewKeyA[6];//NO.2卡

extern unsigned char NewKey[16];

extern unsigned char RC522_Read_Data[16];

extern unsigned char RC522_Write_Data[16];

extern unsigned char MLastSelectedSnr[4];

extern unsigned char RevBuffer[30];

void RC522_GPIO_Configuration(void);

unsigned char SPIReadByte(void);

void SPIWriteByte(unsigned char SPIData);

char PcdRequest(unsigned char req_code,unsigned char *pTagType);

char PcdAnticoll(unsigned char *pSnr);

char PcdSelect(unsigned char *pSnr);

char PcdAuthState(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr);

char PcdRead(unsigned char addr,unsigned char *pData);

char PcdWrite(unsigned char addr,unsigned char *pData);

char PcdHalt(void);

void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData);

char PcdReset(void);

char M500PcdConfigISOType(unsigned char type);

unsigned char ReadRawRC(unsigned char Address);

void WriteRawRC(unsigned char Address, unsigned char value);

void SetBitMask(unsigned char reg,unsigned char mask); void ClearBitMask(unsigned char reg,unsigned char mask); char PcdComMF522(unsigned char Command,

unsigned char *pInData,

unsigned char InLenByte,

unsigned char *pOutData,

unsigned int *pOutLenBit);

void PcdAntennaOn(void);

void PcdAntennaOff(void);

void InitRc522(void);

#endif

#include "rc522.h"

//////////////////////////////////////////////////////////////////////////////////

//深圳海德电子有限公司

//公司网站:https://www.sodocs.net/doc/2618210368.html,/

//修改日期:2015/7/21

//版本:V1.0

//版权所有,盗版必究。

//程序员:李俊

//////////////////////////////////////////////////////////////////////////////////

unsigned char LastKeyA[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};//NO.2卡

unsigned char NewKeyA[6]={0x80,0x80,0x80,0x80,0x80,0x80};//NO.2卡

unsigned char NewKey[16]={0x80,0x80,0x80,0x80,0x80,0x80,\

0xff,0x07,0x80,0x69,\

0x80,0x80,0x80,0x80,0x80,0x80};

unsigned char RC522_Read_Data[16];

unsigned char RC522_Write_Data[16];

unsigned char MLastSelectedSnr[4];

unsigned char RevBuffer[30];

void RC522_GPIO_Configuration(void)

{

RCC->APB2ENR |= 1<<6; //使能GPIOE的时钟

GPIOE->CRL &= 0XFFF00000;

GPIOE->CRL |= 0X00022282;

GPIOE->ODR |= 1<<1;

}

//------------------------------------------

// SPI读取数据

//------------------------------------------

unsigned char SPIReadByte(void)

{

unsigned char SPICount; // Counter used to clock out the data

unsigned char SPIData;

SPIData = 0;

// CLR_SPI_CS;

for (SPICount = 0; SPICount < 8; SPICount++) // Prepare to clock in the data to be read

{

SPIData <<=1; // Rotate the

data

CLR_SPI_CK; // Raise the clock to clock the data out of the MAX7456

if(STU_SPI_MISO)

{

SPIData|=0x01;

}

// delay_us(10);

SET_SPI_CK;

// delay_us(10); // Drop the clock ready for the next bit

}

// SET_SPI_CS; // and loop back

return (SPIData); // Finally return the read data

}

//------------------------------------------

// SPI写入数据

//------------------------------------------

void SPIWriteByte(unsigned char SPIData)

{

unsigned char SPICount; // Counter used to clock out the data

// CLR_SPI_CS;

for (SPICount = 0; SPICount < 8; SPICount++)

{

if (SPIData & 0x80)

{

SET_SPI_MOSI;

}

else

{

CLR_SPI_MOSI;

}

CLR_SPI_CK;

// delay_us(10);

SET_SPI_CK;

// delay_us(10);

SPIData <<= 1;

}

// SET_SPI_CS;

}

//功能:寻卡

//参数说明: req_code[IN]:寻卡方式

// 0x52 = 寻感应区内所有符合14443A标准的卡

// 0x26 = 寻未进入休眠状态的卡

// pTagType[OUT]:卡片类型代码

// 0x4400 = Mifare_UltraLight

// 0x0400 = Mifare_One(S50)

// 0x0200 = Mifare_One(S70)

// 0x0800 = Mifare_Pro(X)

// 0x4403 = Mifare_DESFire

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdRequest(unsigned char req_code,unsigned char *pTagType)

{

char status;

unsigned int unLen;

unsigned char ucComMF522Buf[MAXRLEN];

ClearBitMask(Status2Reg,0x08);

WriteRawRC(BitFramingReg,0x07);

SetBitMask(TxControlReg,0x03);

ucComMF522Buf[0] = req_code;

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);

if ((status == MI_OK) && (unLen == 0x10))

{

*pTagType = ucComMF522Buf[0];

*(pTagType+1) = ucComMF522Buf[1];

}

else

{

status = MI_ERR;

}

return status;

}

/////////////////////////////////////////////////////////////////////

//功能:防冲撞

//参数说明: pSnr[OUT]:卡片序列号,4字节

//返回: 成功返回MI_OK

char PcdAnticoll(unsigned char *pSnr)

{

char status;

unsigned char i,snr_check=0;

unsigned int unLen;

unsigned char ucComMF522Buf[MAXRLEN];

ClearBitMask(Status2Reg,0x08);

WriteRawRC(BitFramingReg,0x00);

ClearBitMask(CollReg,0x80);

ucComMF522Buf[0] = PICC_ANTICOLL1;

ucComMF522Buf[1] = 0x20;

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&unLen);

if (status == MI_OK)

{

for (i=0; i<4; i++)

{

*(pSnr+i) = ucComMF522Buf[i];

snr_check ^= ucComMF522Buf[i];

}

if (snr_check != ucComMF522Buf[i])

{ status = MI_ERR; }

}

SetBitMask(CollReg,0x80);

return status;

}

/////////////////////////////////////////////////////////////////////

//功能:选定卡片

//参数说明: pSnr[IN]:卡片序列号,4字节

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdSelect(unsigned char *pSnr)

{

char status;

unsigned char i;

unsigned int unLen;

unsigned char ucComMF522Buf[MAXRLEN];

ucComMF522Buf[0] = PICC_ANTICOLL1;

ucComMF522Buf[1] = 0x70;

ucComMF522Buf[6] = 0;

for (i=0; i<4; i++)

{

ucComMF522Buf[i+2] = *(pSnr+i);

ucComMF522Buf[6] ^= *(pSnr+i);

}

CalulateCRC(ucComMF522Buf,7,&ucComMF522Buf[7]);

ClearBitMask(Status2Reg,0x08);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,9,ucComMF522Buf,&unLen);

if ((status == MI_OK) && (unLen == 0x18))

{ status = MI_OK; }

else

{ status = MI_ERR; }

return status;

}

/////////////////////////////////////////////////////////////////////

//功能:验证卡片密码

//参数说明: auth_mode[IN]: 密码验证模式

// 0x60 = 验证A密钥

// 0x61 = 验证B密钥

// addr[IN]:块地址

// pKey[IN]:密码

// pSnr[IN]:卡片序列号,4字节

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdAuthState(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr)

{

char status;

unsigned int unLen;

unsigned char i,ucComMF522Buf[MAXRLEN];

ucComMF522Buf[0] = auth_mode;

ucComMF522Buf[1] = addr;

for (i=0; i<6; i++)

{ ucComMF522Buf[i+2] = *(pKey+i); }

for (i=0; i<6; i++)

{ ucComMF522Buf[i+8] = *(pSnr+i); }

// memcpy(&ucComMF522Buf[2], pKey, 6);

// memcpy(&ucComMF522Buf[8], pSnr, 4);

status = PcdComMF522(PCD_AUTHENT,ucComMF522Buf,12,ucComMF522Buf,&unLen);

if ((status != MI_OK) || (!(ReadRawRC(Status2Reg) & 0x08)))

{ status = MI_ERR; }

return status;

}

/////////////////////////////////////////////////////////////////////

//功能:读取M1卡一块数据

//参数说明: addr[IN]:块地址

// pData[OUT]:读出的数据,16字节

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdRead(unsigned char addr,unsigned char *pData)

{

char status;

unsigned int unLen;

unsigned char i,ucComMF522Buf[MAXRLEN];

ucComMF522Buf[0] = PICC_READ;

ucComMF522Buf[1] = addr;

CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

if ((status == MI_OK) && (unLen == 0x90))

// { memcpy(pData, ucComMF522Buf, 16); }

{

for (i=0; i<16; i++)

{ *(pData+i) = ucComMF522Buf[i]; }

}

else

{ status = MI_ERR; }

return status;

}

/////////////////////////////////////////////////////////////////////

//功能:写数据到M1卡一块

//参数说明: addr[IN]:块地址

// pData[IN]:写入的数据,16字节

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdWrite(unsigned char addr,unsigned char *pData)

{

char status;

unsigned int unLen;

unsigned char i,ucComMF522Buf[MAXRLEN];

ucComMF522Buf[0] = PICC_WRITE;

ucComMF522Buf[1] = addr;

CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))

{ status = MI_ERR; }

if (status == MI_OK)

{

//memcpy(ucComMF522Buf, pData, 16);

for (i=0; i<16; i++)

{

ucComMF522Buf[i] = *(pData+i);

}

CalulateCRC(ucComMF522Buf,16,&ucComMF522Buf[16]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,18,ucComMF522Buf,&unLen);

if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))

{ status = MI_ERR; }

}

return status;

}

/////////////////////////////////////////////////////////////////////

//功能:命令卡片进入休眠状态

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdHalt(void)

{

volatile char status;

unsigned int unLen;

unsigned char ucComMF522Buf[MAXRLEN];

ucComMF522Buf[0] = PICC_HALT;

ucComMF522Buf[1] = 0;

CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

return MI_OK;

}

/////////////////////////////////////////////////////////////////////

//用MF522计算CRC16函数

/////////////////////////////////////////////////////////////////////

void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData)

{

unsigned char i,n;

ClearBitMask(DivIrqReg,0x04);

WriteRawRC(CommandReg,PCD_IDLE);

SetBitMask(FIFOLevelReg,0x80);

for (i=0; i

{ WriteRawRC(FIFODataReg, *(pIndata+i)); }

WriteRawRC(CommandReg, PCD_CALCCRC);

i = 0xFF;

do

{

n = ReadRawRC(DivIrqReg);

i--;

}

while ((i!=0) && !(n&0x04));

pOutData[0] = ReadRawRC(CRCResultRegL);

pOutData[1] = ReadRawRC(CRCResultRegM);

}

/////////////////////////////////////////////////////////////////////

//功能:复位RC522

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

char PcdReset(void)

{

SET_RC522RST;

delay_us(10);

CLR_RC522RST;

delay_us(10);

SET_RC522RST;

delay_us(10);

WriteRawRC(CommandReg,PCD_RESETPHASE);

delay_us(10);

WriteRawRC(ModeReg,0x3D); //和Mifare卡通讯,CRC初始值0x6363 WriteRawRC(TReloadRegL,30);

WriteRawRC(TReloadRegH,0);

WriteRawRC(TModeReg,0x8D);

WriteRawRC(TPrescalerReg,0x3E);

WriteRawRC(TxAutoReg,0x40);//必须要

return MI_OK;

}

//////////////////////////////////////////////////////////////////////

//设置RC632的工作方式

//////////////////////////////////////////////////////////////////////

char M500PcdConfigISOType(unsigned char type)

{

if (type == 'A') //ISO14443_A

{

ClearBitMask(Status2Reg,0x08);

WriteRawRC(ModeReg,0x3D);//3F

WriteRawRC(RxSelReg,0x86);//84

WriteRawRC(RFCfgReg,0x7F); //4F

WriteRawRC(TReloadRegL,30);//tmoLength);// TReloadVal = 'h6a =tmoLength(dec) WriteRawRC(TReloadRegH,0);

WriteRawRC(TModeReg,0x8D);

WriteRawRC(TPrescalerReg,0x3E);

delay_us(1000);

PcdAntennaOn();

}

else

{

return MI_NOTAGERR;

}

return MI_OK;

}

/////////////////////////////////////////////////////////////////////

//功能:读RC632寄存器

//参数说明:Address[IN]:寄存器地址

//返回:读出的值

///////////////////////////////////////////////////////////////////// unsigned char ReadRawRC(unsigned char Address)

{

unsigned char ucAddr;

unsigned char ucResult=0;

CLR_SPI_CS;

ucAddr = ((Address<<1)&0x7E)|0x80;

SPIWriteByte(ucAddr);

ucResult=SPIReadByte();

SET_SPI_CS;

return ucResult;

}

///////////////////////////////////////////////////////////////////// //功能:写RC632寄存器

//参数说明:Address[IN]:寄存器地址

// value[IN]:写入的值

///////////////////////////////////////////////////////////////////// void WriteRawRC(unsigned char Address, unsigned char value) {

unsigned char ucAddr;

CLR_SPI_CS;

ucAddr = ((Address<<1)&0x7E);

SPIWriteByte(ucAddr);

SPIWriteByte(value);

SET_SPI_CS;

}

///////////////////////////////////////////////////////////////////// //功能:置RC522寄存器位

//参数说明:reg[IN]:寄存器地址

// mask[IN]:置位值

///////////////////////////////////////////////////////////////////// void SetBitMask(unsigned char reg,unsigned char mask)

{

char tmp = 0x0;

tmp = ReadRawRC(reg);

WriteRawRC(reg,tmp | mask); // set bit mask

}

///////////////////////////////////////////////////////////////////// //功能:清RC522寄存器位

//参数说明:reg[IN]:寄存器地址

// mask[IN]:清位值

///////////////////////////////////////////////////////////////////// void ClearBitMask(unsigned char reg,unsigned char mask)

{

char tmp = 0x0;

tmp = ReadRawRC(reg);

WriteRawRC(reg, tmp & ~mask); // clear bit mask

}

///////////////////////////////////////////////////////////////////// //功能:通过RC522和ISO14443卡通讯

//参数说明:Command[IN]:RC522命令字

// pInData[IN]:通过RC522发送到卡片的数据

// InLenByte[IN]:发送数据的字节长度

// pOutData[OUT]:接收到的卡片返回数据

// *pOutLenBit[OUT]:返回数据的位长度

///////////////////////////////////////////////////////////////////// char PcdComMF522(unsigned char Command,

unsigned char *pInData,

unsigned char InLenByte,

unsigned char *pOutData,

unsigned int *pOutLenBit)

{

char status = MI_ERR;

unsigned char irqEn = 0x00;

unsigned char waitFor = 0x00;

unsigned char lastBits;

unsigned char n;

unsigned int i;

switch (Command)

{

case PCD_AUTHENT:

irqEn = 0x12;

waitFor = 0x10;

break;

case PCD_TRANSCEIVE:

irqEn = 0x77;

waitFor = 0x30;

break;

default:

break;

}

WriteRawRC(ComIEnReg,irqEn|0x80);

ClearBitMask(ComIrqReg,0x80);

WriteRawRC(CommandReg,PCD_IDLE);

SetBitMask(FIFOLevelReg,0x80);

for (i=0; i

{ WriteRawRC(FIFODataReg, pInData[i]); } WriteRawRC(CommandReg, Command);

if (Command == PCD_TRANSCEIVE)

{ SetBitMask(BitFramingReg,0x80); }

//i = 600;//根据时钟频率调整,操作M1卡最大等待时间25ms i = 2000;

do

{

n = ReadRawRC(ComIrqReg);

i--;

}

while ((i!=0) && !(n&0x01) && !(n&waitFor));

ClearBitMask(BitFramingReg,0x80);

if (i!=0)

{

if(!(ReadRawRC(ErrorReg)&0x1B))

{

status = MI_OK;

if (n & irqEn & 0x01)

{ status = MI_NOTAGERR; }

if (Command == PCD_TRANSCEIVE)

{

n = ReadRawRC(FIFOLevelReg);

lastBits = ReadRawRC(ControlReg) & 0x07;

if (lastBits)

{ *pOutLenBit = (n-1)*8 + lastBits; }

else

{ *pOutLenBit = n*8; }

if (n == 0)

{ n = 1; }

if (n > MAXRLEN)

{ n = MAXRLEN; }

for (i=0; i

{ pOutData[i] = ReadRawRC(FIFODataReg); } }

}

else

{ status = MI_ERR; }

}

SetBitMask(ControlReg,0x80); // stop timer now

WriteRawRC(CommandReg,PCD_IDLE);

return status;

}

/////////////////////////////////////////////////////////////////////

//开启天线

//每次启动或关闭天险发射之间应至少有1ms的间隔

///////////////////////////////////////////////////////////////////// void PcdAntennaOn(void)

{

unsigned char i;

i = ReadRawRC(TxControlReg);

if (!(i & 0x03))

{

SetBitMask(TxControlReg, 0x03);

}

}

/////////////////////////////////////////////////////////////////////

//关闭天线

///////////////////////////////////////////////////////////////////// void PcdAntennaOff(void)

{

ClearBitMask(TxControlReg, 0x03);

}

/////////////////////////////////////////////////////////////////////

//功能:扣款和充值

//参数说明: dd_mode[IN]:命令字

// 0xC0 = 扣款

// 0xC1 = 充值

// addr[IN]:钱包地址

// pValue[IN]:4字节增(减)值,低位在前

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

/*char PcdValue(unsigned char dd_mode,unsigned char addr,unsigned char *pValue)

{

char status;

unsigned int unLen;

unsigned char ucComMF522Buf[MAXRLEN];

//unsigned char i;

ucComMF522Buf[0] = dd_mode;

ucComMF522Buf[1] = addr;

CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))

{ status = MI_ERR; }

if (status == MI_OK)

{

memcpy(ucComMF522Buf, pValue, 4);

//for (i=0; i<16; i++)

//{ ucComMF522Buf[i] = *(pValue+i); }

CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);

unLen = 0;

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);

if (status != MI_ERR)

{ status = MI_OK; }

}

if (status == MI_OK)

{

ucComMF522Buf[0] = PICC_TRANSFER;

ucComMF522Buf[1] = addr;

CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))

{ status = MI_ERR; }

}

return status;

}*/

/////////////////////////////////////////////////////////////////////

//功能:备份钱包

//参数说明: sourceaddr[IN]:源地址

// goaladdr[IN]:目标地址

//返回: 成功返回MI_OK

/////////////////////////////////////////////////////////////////////

/*char PcdBakValue(unsigned char sourceaddr, unsigned char goaladdr)

{

char status;

unsigned int unLen;

unsigned char ucComMF522Buf[MAXRLEN];

ucComMF522Buf[0] = PICC_RESTORE;

ucComMF522Buf[1] = sourceaddr;

CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))

{ status = MI_ERR; }

if (status == MI_OK)

{

ucComMF522Buf[0] = 0;

ucComMF522Buf[1] = 0;

ucComMF522Buf[2] = 0;

ucComMF522Buf[3] = 0;

CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);

status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);

if (status != MI_ERR)

{ status = MI_OK; }

}

if (status != MI_OK)

{ return MI_ERR; }

看门狗程序

TMS320F2812 Watchdog范例程序 FILE: Example_28xWatchdog.c // // TITLE: DSP28 Watchdog interrupt test program. // // ASSUMPTIONS: // // This program requires the DSP28 header files. To compile the // program as is, it should reside in the DSP28/examples/watchdog // sub-directory. // // As supplied, this project is configured for "boot to H0" operation. // // DESCRIPTION: // This program exercises the watchdog on the F2812/F2810 parts. // // First the watchdog is connected to the WAKEINT interrupt of the // PIE block. The code is then put into an infinite loop. // // The user can select to feed the watchdog key register or not // by commenting one line of code in the infinite loop. // // If the watchdog key register is fed by the KickDog function // then the WAKEINT interrupt is not taken. If the key register // is not fed by the KickDog function then WAKEINT will be taken. // // Watch Variables: // LoopCount for the number of times through the infinite loop // WakeCount for the number of times through WAKEINT // //########################################################################### // // Ver | dd mmm yyyy | Who | Description of changes // =====|=============|======|=============================================== // 0.57| 29 May 2002 | L.H. | Initial Release //########################################################################### // Step 0. Include required header files // DSP28_Device.h: device specific definitions #include statements for // all of the peripheral .h definition files. // DSP28_Example.h is specific for the given example. #include "DSP28_Device.h"

字符设备基础

Linux 字符设备基础 字符设备驱动程序在系统中的位置 操作系统内核需要访问两类主要设备,简单的字符设备,如打印机,键盘等;块设备,如软盘、硬盘等。与此对应,有两类设备驱动程序。分别称为字符设备驱动程序和块设备驱动程序。两者的主要差异是:与字符设备有关的系统调用几乎直接和驱动程序的内部功能结合在一起。而读写块设备则主要和快速缓冲存储区打交道。只有需要完成实际的输入/输出时,才用到块设备驱动程序。见下图: Linux 设备驱动程序的主要功能有: ● 对设备进行初始化; ● 使设备投入运行和退出服务; ● 从设备接收数据并将它们送到内核; ● 将数据从内核送到设备; ● 检测和处理设备出现的错误。 当引导系统时,内核调用每一个驱动程序的初始化函数。它的任务之一是将这一设备驱动程序使用的主设备号通知内核。同时,初始化函数还将驱动程序中的函数地址结构的指针送给内核。 内核中有两X 表。一X 表用于字符设备驱动程序,另一X 用于块设备驱动程序。这两X 表用来保存指向file_operations 结构的指针, 设备驱动程序内部的函数地址就保

存在这一结构中。内核用主设备号作为索引访问file_operations结构,因而能访问驱动程序内的子程序。 从开机到驱动程序的载入 系统启动过程中可能出现几种不同的方式检测设备硬件。首先机器硬件启动时BIOS会检测一部分必要的设备,如内存、显示器、键盘和硬盘等等。机器会把检测到的信息存放在特定的位置,如CMOS数据区。而另外某些设备会由设备驱动程序进行检测。 1 开机 2 引导部分(linux/config.h,arch/i386/boot/bootsect.S) 3 实模式下的系统初始化(arch/i386/boot/setup.S) 4 保护模式下的核心初始化 5 启动核心(init/main.c) init函数中函数调用关系如下: main.c init() filesystems.c sys_setup() genhd.c device_setup() mem.c chr_dev_init() 至此,驱动程序驻入内存。 设备驱动程序基本数据结构: struct device_struct 系统启动过程中要登记的块设备和字符设备管理表的定义在文件fs/devices.c中:struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct device_struct blkdevs[MAX_BLKDEV]; 其实块设备表和字符设备表使用了相同的数据结构。在某些系统中,这些设备表也称作设备开关表,不同的是它们直接定义了一组函数指针进行对设备的管理。而这里系统用文件操作(file_operations)代替了那组开关。文件操作是文件系统与设备驱动程序之间的接口,系统特殊文件在建立的时候并没有把两者对应起来,只是把设备的缺省文件结构和i节点结构赋给设备文件,而真正的对应定义在系统启动之后,当设备被打开时时才进行的。 操作blkdev_open和chrdev_open定义在文件devices.c中,它们的基本功能是当设备文件初次打开时,根据该文件的i节点信息找到设备真正的文件操作接口,然后更新原来的设

软件看门狗和硬件看门狗

看门狗分硬件看门狗和软件看门狗。硬件看门狗是利用一个定时器电路,其定时输出连接到电路的复位端,程序在一定时间范围内对定时器清零(俗称“喂狗”),因此程序正常工作时,定时器总不能溢出,也就不能产生复位信号。如果程序出现故障,不在定时周期内复位看门狗,就使得看门狗定时器溢出产生复位信号并重启系统。软件看门狗原理上一样,只是将硬件电路上的定时器用处理器的内部定时器代替,这样可以简化硬件电路设计,但在可靠性方面不如硬件定时器,比如系统内部定时器自身发生故障就无法检测到。当然也有通过双定时器相互监视,这不仅加大系统开销,也不能解决全部问题,比如中断系统故障导致定时器中断失效。 看门狗本身不是用来解决系统出现的问题,在调试过程中发现的故障应该要查改设计本身的错误。加入看门狗目的是对一些程序潜在错误和恶劣环境干扰等因素导致系统死机而在无人干预情况下自动恢复系统正常工作状态。看门狗也不能完全避免故障造成的损失,毕竟从发现故障到系统复位恢复正常这段时间内怠工。同时一些系统也需要复位前保护现场数据,重启后恢复现场数据,这可能也需要一笔软硬件的开销。 图1:(a) 多任务系统看门狗示意图;(b) 相应的看门狗复位逻辑图。 在单任务系统中看门狗工作原理如上所述,容易实现。在多任务系统中情况稍为复杂。假如每个任务都像单任务系统那么做,如图1(a)所示,只要有一个任务正常工作并定期“喂狗”,看门狗定时器就不会溢出。除非所有的任务都故障,才能使得看门狗定时器溢出而复位,如图1(b)。 而往往我们需要的是只要有一个任务故障,系统就要求复位。或者选择几个关键的任务接受监视,只要一个任务出问题系统就要求复位,如图2(a)所示,相应的看门狗复位逻辑如图2(b)所示。 在多任务系统中通过创建一个监视任务TaskMonitor,它的优先级高于被监视的任务群Task1、Task2...Taskn。TaskMonitor在Task1~Taskn正常工作情况下,一定时间内对硬件看门狗定时器清零。如果被监视任务群有一个Task_x出现故障,TaskMonitor就不对看门狗定时器清零,也就达到被监视任务出现故障时系统自动重启的目的。另外任务TaskMonitor自身出故障时,也不能及时对看门狗定时器清零,看门狗也能自动复位重启。

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

字符设备驱动程序

Linux字符设备驱动(转载) 来源: ChinaUnix博客日期:2008.01.01 18:52(共有0条评论) 我要评论 Linux字符设备驱动(转载) 这篇文章描述了在Linux 2.4下,如何建立一个虚拟的设备,对初学者来说很有帮助。原文地址:https://www.sodocs.net/doc/2618210368.html,/186/2623186.shtml Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open ()、close ()、read ()、write () 等。 Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单。 下面我们来假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变量int global_var,而这个设备的名字叫做"gobalvar"。对"gobalvar"设备的读写等操作即是对其中全局变量global_var的操作。 驱动程序是内核的一部分,因此我们需要给其添加模块初始化函数,该函数用来完成对所控设备的初始化工作,并调用register_chrdev() 函数注册字符设备: static int __init gobalvar_init(void) { if (register_chrdev(MAJOR_NUM, " gobalvar ", &gobalvar_fops)) { //…注册失败 } else

STM32窗口看门狗程序

STM32窗口看门狗程序 窗口看门狗(WWDG)通常被用来监测由外部干扰或不可预见的逻辑条件造成的应用程序背离正常的运行序列而产生的软件故障。除非递减计数器的值在T6位(WWDG->;CR的第六位)变成0前被刷新,看门狗电路在达到预置的时间周期时,会产生一个MCU复位。在递减计数器达到窗口配置寄存器(WWDG->;CFR)数值之前,如果7位的递减计数器数值(在控制寄存器中)被刷新,那么也将产生一个MCU复位。这表明递减计数器需要在一个有限的时间窗口中被刷新。

图 3.6.1.1中,T[6:0]就是WWDG_CR的低七位,W[6:0]即是WWDG->;CFR的低七位。T[6:0]就是窗口看门狗的计数器,而W[6:0]则是窗口看门狗的上窗口,下窗口值是固定的(0X40)。当窗口看门狗的计数器在上窗口值之外被刷新,或者低于下窗口值都会产生复位。 上窗口值(W[6:0])是由用户自己设定的,根据实际要求来设计窗口值,但是一定要确保窗口值大于0X40,否则窗口就不存在了。 窗口看门狗的超时公式如下: Twwdg=(4096×2^WDGTB×(T[5:0]+1)) /Fpclk1; 其中: Twwdg:WWDG超时时间(单位为ms) Fpclk1:APB1的时钟频率(单位为Khz) WDGTB:WWDG的预分频系数 T[5:0]:窗口看门狗的计数器低6位 窗口看门狗寄存器介绍:

如何使用窗口看门狗: 1)使能WWDG时钟 2)设置WWDG_CFR和WWDG_CR两个寄存器 在时钟使能完后,我们设置WWDG的CFR和CR两个寄存器,对WWDG进行配置。包括使能窗口看门狗、开启中断、设置计数器的初始值、设置窗口值并设置分频数WDGTB 3)开启WWDG中断并分组 4)编写中断服务函数 软件例程: //---------------------------wdg.c----------------------- #include "wdg.h" #include "led.h" u8 wwdg_cnt=0x7f; //窗口看门狗计数器初值 void wwdg_init(u8 tr,u8 wr,u8 fprer) { RCC->;APB1ENR|=1;CFR|=fprer;CFR|=1;CFR&=0xff80; //窗口值清零 WWDG->;CFR|=wr; //设定窗口值 WWDG->;CR|=(wwdg_cnt|1;CR|=(cnt&0x7f); //喂狗值 } void WWDG_IRQHandler(void)

linux字符设备驱动课程设计报告

一、课程设计目的 Linux 系统的开源性使其在嵌入式系统的开发中得到了越来越广泛的应用,但其本身并没有对种类繁多的硬件设备都提供现成的驱动程序,特别是由于工程应用中的灵活性,其驱动程序更是难以统一,这时就需开发一套适合于自己产品的设备驱动。对用户而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以像对其它文件一样对此设备文件进行操作。 通过这次课程设计可以了解linux的模块机制,懂得如何加载模块和卸载模块,进一步熟悉模块的相关操作。加深对驱动程序定义和设计的了解,了解linux驱动的编写过程,提高自己的动手能力。 二、课程设计内容与要求 字符设备驱动程序 1、设计目的:掌握设备驱动程序的编写、编译和装载、卸载方法,了解设备文件的创建,并知道如何编写测试程序测试自己的驱动程序是否能够正常工作 2、设计要求: 1) 编写一个简单的字符设备驱动程序,该字符设备包括打开、读、写、I\O控制与释放五个基本操作。 2) 编写一个测试程序,测试字符设备驱动程序的正确性。 3) 要求在实验报告中列出Linux内核的版本与内核模块加载过程。 三、系统分析与设计 1、系统分析 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1、对设备初始化和释放; 2、把数据从内核传送到硬件和从硬件读取数据; 3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据; 4、检测和处理设备出现的错误。 字符设备提供给应用程序的是一个流控制接口,主要包括op e n、clo s e(或r ele as e)、r e ad、w r i t e、i o c t l、p o l l和m m a p等。在系统中添加一个字符设备驱动程序,实际上就是给上述操作添加对应的代码。对于字符设备和块设备,L i n u x内核对这些操作进行了统一的抽象,把它们定义在结构体fi le_operations中。 2、系统设计: 、模块设计:

STM32最小系统电路

STM32最小系统电路 原创文章,转载请注明出处: 1.电源供电方案 ● VDD = ~:VDD管脚为I/O管脚和内部调压器的供电。 ● VSSA,VDDA = ~:为ADC、复位模块、RC振荡器和PLL的模拟部分提供供电。使用ADC时,VDD不得小于。VDDA和VSSA必须分别连接到VDD和VSS。 ● VBAT = ~:当关闭VDD时,(通过内部电源切换器)为RTC、外部32kHz振荡器和后备寄存器供电。 采用(AMS1117)供电 ]

2.晶振 STM32上电复位后默认使用内部[精度8MHz左右]晶振,如果外部接了8MHz 的晶振,可以切换使用外部的8MHz晶振,并最终PLL倍频到72MHz。 3.JTAG接口 ~ 在官方给出的原理图基本是结合STM32三合一套件赠送的ST-Link II给出的JTAG接口。

ST-Link II SK-STM32F学习评估套件原理图的JTAG连接 很多时候为了省钱,所以很多人采用wiggler + H-JTAG的方案。H-JTAG其实是twentyone大侠开发的调试仿真烧写软件,界面很清新很简洁。 ) H-JTAG界面

H-JTAG软件的下载: H-JTAG官网:大侠的blog: 关于STM32 H-JTAG的使用,请看下一篇博文 Wiggler其实是一个并口下载方案,其实电路图有很多种,不过一些有可能不能使用,所以要注意。你可以在taobao上买人家现成做好的这种Wiggler下载线,最简便的方法是自己动手做一条,其实很简单,用面包板焊一个74HC244就可以了。 ! Wiggler电路图下载: 电路图中”RESET SELECT”和”RST JUMPER”不接,如果接上的话会识别不了芯片。

看门狗电路及原理

看门狗电路。在单片机中,为了能使得程序能够正常的运行。设定的及时根据程序所返回的值检测程序运行情况的定时电路。 在主程序中设定一定的值,把这个值在看门狗定时电路数值益处之前定时赋给看门狗赋给定时电路,让看门狗定时器复位。主程序的赋值周期要小于看门狗定时电路的运行周期。 看门狗 百科名片 单片机"看门狗" 在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称"看门狗"(watchdog) 目录[隐藏] 应用 基本原理 看门狗使用注意 看门狗运用 设计思路 [编辑本段]应用 看门狗电路的应用,使单片机可以在无人状态下实现连续工作,其工作原理是:看门狗芯片和单片机的一个I/O引脚相连,该I/O引脚通过程序控制它定时地往看门狗的这个引脚上送入高电平(或低电平),这一程序语句是分散地放在单片机其他控制语句中间的,一旦单片机由于干扰造成程序跑飞后而陷入某一程序段进入死循环状态时,写看门狗引脚的程序便不能被执行,这个时候,看门狗电路就会由于得不到单片机送来的信号,便在它和单片机复位引脚相连的引脚上送出一个复位信号,使单片机发生复位,

即程序从程序存储器的起始位置开始执行,这样便实现了单片机的自动复位。 [编辑本段]基本原理 看门狗,又叫watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗(kicking the dog or service the dog),一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就会给出一个复位信号到MCU,使MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。工作原理:在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位。所以在使用有看门狗的芯片时要注意清看门狗。硬件看门狗是利用了一个定时器,来监控主程序的运行,也就是说在主程序的运行过程中,我们要在定时时间到之前对定时器进行复位如果出现死循环,或者说PC指针不能回来。那么定时时间到后就会使单片机复位。常用的WDT芯片如MAX813 ,5045, IMP 813等,价格4~10元不等. 软件看门狗技术的原理和这差不多,只不过是用软件的方法实现,我们还是以51系列来讲,我们知道在51单片机中有两个定时器,我们就可以用这两个定时器来对主程序的运行进行监控。我们可以对T0设定一定的定时时间,当产生定时中断的时候对一个变量进行赋值,而这个变量在主程序运行的开始已经有了一个初值,在这里我们要设定的定时值要小于主程序的运行时间,这样在主程序的尾部对变量的值进行判断,如果值发生了预期的变化,就说明T0中断正常,如果没有发生变化则使程序复位。对于T1我们用来监控主程序的运行,我们给T1设定一定的定时时间,在主程序中对其进行复位,如果不能在一定的时间里对其进行复位,T1 的定时中断就会使单片机复位。在这里T1的定时时间要设的大于主程序的运行时间,给主程序留有一定的的裕量。而T1的中断正常与否我们再由T0定时中断子程序来监视。这样就够成了一个循环,T0监视T1,T1监视主程序,主程序又来监视T0,从而保证系统的稳定运行。51 系列有专门的看门狗定时器,对系统频率进行分频计数,定时器溢出时,将引起复位.看门狗可设定溢出率,也可单独用来作为定时器使用。凌阳61的看门狗比较单一,一个是时间单一,第二是功能在实际的使用中只需在循环当中加入清狗的指令就OK了。AVR系列中,avr-libc 提供三个API 支持对器件内部Watchdog 的操作,它们分别是:wdt_reset() // Watchdog 复位wdt_enable(timeout) // Watchdog 使能wdt_disable() // Watchdog 禁止C8051Fxxx单片机内部也有一个21位的使用系统时钟的定时器,该定时器检测对其控制寄存器的两次特定写操作的时间间隔。如果这个时间间隔超过了编程的极限值,将产生一个WDT复位。-------------------------------------------------------------------------------- [编辑本段]看门狗使用注意

一个简单字符设备驱动实例

如何编写Linux设备驱动程序 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。本文是在编写一块多媒体卡编制的驱动程序后的总结,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1)对设备初始化和释放; 2)把数据从内核传送到硬件和从硬件读取数据; 3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据; 4)检测和处理设备出现的错误。 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备

C51单片机看门狗电路及程序设计方案

C51单片机看门狗电路及 程序设计案 院系:信息工程学院 年级:2010级 电子一班禹豪 电子一班训虎 电子二班邓启新 一、引言 在由单片机构成的微型计算机系统中,程序的正常运行常常会因为来自外界的电磁场干扰等原因而被打断,从而造成程序的跑飞,而陷入死循环。由此导致单片机控制的系统无法继续工作,造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片或程序,俗称"看门狗"(watchdog) (1)看门狗电路基本原理 看门狗电路的应用,使单片机可以在无人状态下实现连续工作,其工作原理是:看门狗芯片和单片机的一个I/O引脚相连**,该I/O引脚通过程序控制它定时地往看门狗的这个引脚上送入高电平(或低电平),这一程序语句是分散地放在单片机其他控制语句中间的,一旦单片机由于干扰造成程序跑飞后而陷入某一程序段进入死循环状态时,写看门狗引脚的程序便不能被执行,这个时候,看门狗电路就会由于得不到单片机送来的信号,便在它和单片机复位引脚相连的引脚上送出一个复位信号,使单片机发生复位,即程序从程序存储器的起始位置开始执行,这样便实现了单片机的自动复位。 *此处设计原理实际上为下文中硬件看门狗设计思路。

(2)看门狗电路一般设计式 “看门狗”电路一般分为硬件看门狗与软件看门狗两种设计式。 硬件看门狗是利用了一个定时器,来监控主程序的运行,也就是说在主程序的运行过程中,我们要在定时时间到之前对定时器进行复位。如果出现死循环,或者说PC指针不能回来,那么定时时间到后就会使单片机复位。常用的WDT芯片如MAX813,5045,IMP 813等,价格4~10元不等. 软件看门狗技术的原理和硬件看门狗类似,只不过是用软件的法实现(即利用单片机部定时器资源,通过编程模拟硬件看门狗工作式),以51系列为例:因在51单片机中有两个定时器,在利用部定时器资源来对主程序的运行进行监控时。可以对T1(或T0)设定一定的定时时间(设定的定时值要小于主程序的运行时间),当产生定时中断的时候对一个变量进行赋值(此变量在主程序运行的开始已有一个初值)。当主程序运行至最后时对此变量的值进行判断,如果值发生了预期的变化,就说明T0中断正常,如果没有发生变化则使程序复位。 考虑到设计要求,本设计采用软件看门狗设计思路。 二、看门狗电路整体设计思路 根据设计要求,本设计利用C51单片机部自带的定时器1进行编程,并配合少量电路实现“看门狗“电路功能。整个设计分为软件部分与硬件部分,如下: (1)软件部分设计原理: 软件设计分为三部分:“看门狗“定时器设置程序、溢出中断服务程序和喂狗代码。 1.1设计思路: 1)在主程序开头,“看门狗“定时器设置程序设置定时器1计时50ms。 2)当定时达50ms时,定时器1产生溢出中断,溢出中断服务程序开始工作,将看门狗标志num加1。当num的值等于100时,说明看门狗定时器已经计时5s,此时,单片机I/O端口P1.0输出高电平,对程序进行复位。 3)在此过程中,喂狗代码将被穿插于程序中循环体末尾。当循环体结束时,喂狗代码执行,关闭定时器1、清空num并重新初始化定时器设置。若循环体进入死循环,喂狗代码无法执行,num将一直累加至100,此时程序复位。 注:喂狗代码放置位置可根据num预计数值进行调整:当num门限值较小,即看门狗计数时间较短时,喂狗代码可放于程序中各循环体之后或均匀分布于整个主程序中。当num门限值较大,即看门狗计数时间较长时,喂狗代码可放于程序主循环体末尾。但是需注意看门狗计数时间必须长于正常工作时间,以免非正常复位。 1.2软件设计流程图:

字符设备驱动步骤

编写字符设备驱动框架的步骤 Step 1: 申请设备号(主要是申请主设备号) 有两种方式: ⑴静态申请 通过下面这个函数实现: int register_chrdev_region(dev_t from, unsigned count, const char *name); /* register_chrdev_region() - register a range of device numbers * @from: the first in the desired range of device numbers; must include * the major number. * @count: the number of consecutive device numbers required * @name: the name of the device or driver. * * Return value is zero on success, a negative error code on failure.*/ 这种方式主要用于,驱动开发者事先知道该驱动主设备号的情况。 ⑵动态申请 通过下面这个函数实现: int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) /* alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers. The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev. Returns zero or a negative error code.*/ 这种方式由系统动态分配一个设备号,返回的设备号保存在参数dev中。 Step 2 :注册字符设备 在linux 内核中用struct cdev表示一个字符设备。 字符设备的注册与注销分别通过下面的两个函数来实现: int cdev_add(struct cdev *p, dev_t dev, unsigned count); /** * cdev_add() - add a char device to the system * @p: the cdev structure for the device * @dev: the first device number for which this device is responsible * @count: the number of consecutive minor numbers corresponding to this * device * * cdev_add() adds the device represented by @p to the system, making it * live immediately. A negative error code is returned on failure.

LINUX字符设备驱动编写基本流程

---简介 Linux下的MISC简单字符设备驱动虽然使用简单,但却不灵活。 只能建立主设备号为10的设备文件。字符设备比较容易理解,同时也能够满足大多数简 单的硬件设备,字符设备通过文件系统中的名字来读取。这些名字就是文件系统中的特 殊文件或者称为设备文件、文件系统的简单结点,一般位于/dev/目录下使用ls进行查 看会显示以C开头证明这是字符设备文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。 第一个数字是主设备号,第二个数字是次设备号。 ---分配和释放设备编号 1)在建立字符设备驱动时首先要获取设备号,为此目的的必要的函数是 register_chrdev_region,在linux/fs.h中声明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想 要分配的起始设备编号,first的次编号通常是0,count是你请求的连续设备编号的 总数。count如果太大会溢出到下一个主设备号中。name是设备的名字,他会出现在 /proc/devices 和sysfs中。操作成功返回0,如果失败会返回一个负的错误码。 2)如果明确知道设备号可用那么上一个方法可行,否则我们可以使用内核动态分配的设 备号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是个只输出的参数,firstminor请求的第一个要用的次编号, count和name的作用如上1)对于新驱动,最好的方法是进行动态分配 3)释放设备号,void unregister_chrdev_region(dev_t first unsigned int count); ---文件操作file_operations结构体,内部连接了多个设备具体操作函数。该变量内部 的函数指针指向驱动程序中的具体操作,没有对应动作的指针设置为NULL。 1)fops的第一个成员是struct module *owner 通常都是设置成THIS_MODULE。 linux/module.h中定义的宏。用来在他的操作还在被使用时阻止模块被卸载。 2)loff_t (*llseek) (struct file *, loff_t, int);该方法用以改变文件中的当前读/ 写位置 返回新位置。 3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);该函数用 以从设备文件 中读取数据,读取成功返回读取的字节数。

avr单片机看门狗程序

单片机看门狗程序 /*此程序实现单片机"看门狗"WDT的功能*/ #include "p18f458.h" unsigned long i; /*系统初始化子程序*/ void initial() { TRISD = 0X00; /*D口设为输出*/ } /*延时子程序*/ void DELAY() { for (i=19999;--i;) continue; } /*主程序*/ main () { initial(); /*初始化,设定看门狗的相关寄存器*/ PORTD = 0X00; /*D口送00H,发光二极管亮*/ DELAY(); /*给予一定时间的延时*/ PORTD = 0XFF; /*D口送FFH,发光二极管灭*/ while(1)

{ ; } /*死循环,等待看门狗溢出复位*/ } -------------------汇编语言版本的单片机看门狗程序---------------- ;此程序实现"看门狗"WDT的功能 ;此单片机看门狗由https://www.sodocs.net/doc/2618210368.html,独家提供 LIST P=18F458 INCLUDE "P18F458.INC" DEYH EQU 0X20 DEYL EQU DEYH+1 ORG 0X00 GOTO MAIN ORG 0X30 ;*************初始化子程序***************** INITIAL CLRF TRISD ;D口设为输出 RETURN ;**************延时子程序************************** DELAY MOVLW 0XFF MOVWF DEYH AGAIN1

如何设计看门狗(硬件看门狗与软件看门狗)

看门狗电路的概念和作用 2007/08/05 15:26 一般看门狗电路用来监视MCU内部程序运行状态,在程序跑飞或死锁情况下,可以自动复位。不过由于厂家、型号不同可能有些差别。 看门狗电路的工作原理是:当系统工作正常时,CPU将每隔一定时间输出一个脉冲给看门狗,即“喂狗”,若程序运行出现问题或硬件出现故障时而无法按时“喂狗”时,看门狗电路将迫使系统自动复位而重新运行程序。主要作用是防止程序跑飞或死锁 看门狗电路其实是一个独立的定时器,有一个定时器控制寄存器,可以设定时间(开狗),到达时间后要置位(喂狗),如果没有的话,就认为是程序跑飞,就会发出RESET指令 在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称"看门狗" 看门狗电路电路的应用,使单片机可以在无人状态下实现连续工作,其工作原理是:看门狗芯片和单片机的一个I/O引脚相连,该I/O引脚通过程序控制它定时地往看门狗的这个引脚上送入高电平(或低电平),这一程序语句是分散地放在单片机其他控制语句中间的,一旦单片机由于干扰造成程序跑飞后而陷入某一程序段进入死循环状态时,写看门狗引脚的程序便不能被执行,这个时候,看门狗电路就会由于得不到单片机送来的信号,便在它和单片机复位引脚相连的引脚上送出一个复位信号,使单片机发生复位,即程序从程序存储器的起始位置开始执行,这样便实现了单片机的自动复位. 看门狗,又叫 watchdog timer,是一个定时器电路, 一般有一个输入,叫喂狗,一个输出到MCU的RST端,MCU正常工作的时候,每隔一端时间输出一个信号到喂狗端,给 WDT 清零,如果超过规定的时间不喂狗,(一般在程序跑飞时),WDT 定时超过,就回给出一个复位信号到MCU,是MCU复位. 防止MCU死机. 看门狗的作用就是防止程序发生死循环,或者说程序跑飞。

实验二:字符设备驱动实验

实验二:字符设备驱动实验 一、实验目的 通过本实验的学习,了解Linux操作系统中的字符设备驱动程序结构,并能编写简单的字符设备的驱动程序以及对所编写的设备驱动程序进行测试,最终了解Linux操作系统如何管理字符设备。 二、准备知识 字符设备驱动程序主要包括初始化字符设备、字符设备的I/O调用和中 断服务程序。在字符设备驱动程序的file_operations结构中,需要定义字 符设备的基本入口点。 open()函数; release()函数 read()函数 write()函数 ioctl()函数 select()函数。 另外,注册字符设备驱动程序的函数为register_chrdev()。 register_chrdev() 原型如下: int register_chrdev(unsigned int major, //主设备号 const char *name, //设备名称 struct file_operations *ops); //指向设备操作函数指针 其中major是设备驱动程序向系统申请的主设备号。如果major为0, 则系统为该驱动程序动态分配一个空闲的主设备号。name是设备名称,ops 是指向设备操作函数的指针。 注销字符设备驱动程序的函数是unregister_chrdev(),原型如下:int unregister_chrdev(unsigned int major,const char *name); 字符设备注册后,必须在文件系统中为其创建一个设备文件。该设备文件可以在/dev目录中创建,每个设备文件代表一个具体的设备。 使用mknod命令来创建设备文件。创建设备文件时需要使用设备的主设备号和从设备号作为参数。 阅读教材相关章节知识,了解字符设备的驱动程序结构。

相关主题