代码是从用C写的单片机移植到linux上的,单片机的SPI通讯当然是直接IO口模拟的:
void SPIWriteByte(unsigned char SPIData) { unsigned char SPICount; // Counter used to clock out the data for (SPICount = 0; SPICount < 8; SPICount++) { if (SPIData & 0x80) { SET_SPI_MOSI; } else { CLR_SPI_MOSI; } nop();nop(); CLR_SPI_CK;nop();nop(); SET_SPI_CK;nop();nop(); SPIData <<= 1; } }
unsigned char SPIReadByte(void) { unsigned char SPICount; // Counter used to clock out the data unsigned char SPIData; SPIData = 0; for (SPICount = 0; SPICount < 8; SPICount++) // Prepare to clock in the data to be read { SPIData <<=1; // Rotate the data CLR_SPI_CK; nop(); nop(); // Raise the clock to clock the data out of the MAX7456 if(STU_SPI_MISO) { SPIData|=0x01; } SET_SPI_CK; nop(); nop(); // Drop the clock ready for the next bit } // and loop back return (SPIData); // Finally return the read data }
后来继续看单片机的代码,发现有些地方是读了之后马上写,或者写地址之后马上写值,如:
SPIWriteByte(ucAddr);
ucResult=SPIReadByte();
和
SPIWriteByte(ucAddr);
SPIWriteByte(value);
上述函数是分两次调用前面的读写函数,而linux下有标准的读写或者连写函数,将上述修改如下即可:
读写用spi_write_then_read(rc522_spi, &ucAddr, 1, &ucResult, 1)代替,而连写则用:
struct spi_transfer st[2]; struct spi_message msg; spi_message_init( &msg ); memset( st, 0, sizeof(st) ); st[ 0 ].tx_buf = &ucAddr; st[ 0 ].len = 1; spi_message_add_tail( &st[0], &msg ); st[ 1 ].tx_buf = &value; st[ 1 ].len = 1; spi_message_add_tail( &st[1], &msg ); spi_sync( rc522_spi, &msg );
要注意为了避免一张放在可读区域内的IC卡被多次读到,读完一次后用命令写卡将卡进入休眠状态,还有在循环检测区域内卡片代码中,不要复位RC522芯片,否则刚才休眠的卡将当新卡处理,即还是可以多次读到。
还有一点,SPI的工作模式要选对,如下一幅网上画的图片:
这是四种工作模式的时序图,而linux spi.h头文件中定义这四种模式为:
#define SPI_MODE_0 (0|0) /* (original MicroWire) */ #define SPI_MODE_1 (0|SPI_CPHA) #define SPI_MODE_2 (SPI_CPOL|0) #define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)mode 0:时钟空闲时候为低电平,时钟 第一个上升沿开始采集(或发送)数据 ( 第一个上升沿:先升后降,即第一个时钟采样)
mode 1:时钟空闲时候为低电平,时钟第一个下降沿开始采集(或发送)数据 (第一个下降沿:先升后降,即第二个时钟采样)
mode 2:时钟空闲时候为高电平,时钟第一个下降沿开始采集(或发送)数据 (第一个下降沿:先降后升,即第一个时钟采样)
mode 3:时钟空闲时候为高电平,时钟第一个上升沿开始采集(或发送)数据 (第一个上升沿:先降后升,即第二个时钟采样)
RC522才用mode 0或者mode 3均可工作。