最近、SPIインターフェイスを使用してNFCモジュールfm17550をデバッグする必要がある顧客に出会いました。ただし、顧客にはレジスタの読み取り時間と書き込み時間の要件があります。各レジスタ(8ビット)の読み取り時間は60usを超えることはできません。
Androidドライバーのネイティブコードを使用して、印刷を追加します
。printk(KERN_ERR“ start read reg \ n”);
tmp = Read_Reg((u8)tmp);
printk(KERN_ERR“ end cmd =%d%02X \ n”、cmd 、(u8)tmp);
[900.232668] start read reg
[900.235575] cmd = 8 00
[900.237496] start read reg
[900.240704] cmd = 8 03
レジスタの読み取り時間は約4msです(もちろん、printk印刷の増加にはもっと時間がかかります)今回のギャップと顧客要件は1桁ではありません。
Linuxドライバーでのspiの読み取りと書き込みの時間はどこにかかりますか?印刷を増やし続けます:
Read_Reg–>
spi_write_then_read(kernel \ drivers \ spi \ spi.c)–>
spi_sync->
__spi_sync->
wait_for_completion(&done);
ここでwait_for_completionはプロセスが完了するのを待つ必要があります。
QualcommのSPIドライバーには、パイプと割り込みの設定が含まれます。レジスターに書き込む前に、spiのステータスを確認し、レジスターを読み取る
ときに割り込みを待ちます。writel_relaxed(word、dd-> base + SPI_OUTPUT_FIFO);
data_in = readl_relaxed(dd-> base + SPI_INPUT_FIFO );
IOctrlを使用してユーザー層からドライバーに呼び出す場合、時間は要件を満たすことができません。
その理由は、Linuxはリアルタイムシステムではなく、spiドライバーは通常、リアルタイムパフォーマンスを考慮しないためです。これらはすべて、メッセージをspiコントローラーのキューにハングアップしてから、queue_workを呼び出すか、同様のメソッドを使用します。
ドライバーレイヤーで変更する方法が機能しません。インターネット上のヒントを参照し、ユーザースペースにGPIOアドレスをマッピングして、レジスターを直接操作してみてください。
参照コードは次のとおりです。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define Anticollision 0x02
#define TIMEOUT_Err 0x20
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivIEnReg 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 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 MfTxReg 0x1C
#define MfRxReg 0x1D
#define SerialSpeedReg 0x1F
#define CRCMSBReg 0x21
#define CRCLSBReg 0x22
#define ModWidthReg 0x24
#define GsNOffReg 0x23
#define TxBitPhaseReg 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsPReg 0x28
#define ModGsPReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadMSBReg 0x2C
#define TReloadLSBReg 0x2D
#define TCounterValMSBReg 0x2E
#define TCounterValLSBReg 0x2F
#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 TLMM_BASE_ADDR 0x1000000
#define GPIO_CONFIG_ADDR(x) (TLMM_BASE_ADDR + (x)*0x1000)
#define GPIO_IN_OUT_ADDR(x) (TLMM_BASE_ADDR + 0x00000004 + (x)*0x1000)
//#define MAP_SIZE 0x100
#define MAP_SIZE 0x4
/* GPIO TLMM: Direction */
#define GPIO_INPUT 2
#define GPIO_OUTPUT 0
/* GPIO TLMM: Pullup/Pulldown */
#define GPIO_NO_PULL 0
#define GPIO_PULL_DOWN 1
#define GPIO_KEEPER 2
#define GPIO_PULL_UP 3
/* GPIO TLMM: Drive Strength */
#define GPIO_2MA 0
#define GPIO_4MA 1
#define GPIO_6MA 2
#define GPIO_8MA 3
#define GPIO_10MA 4
#define GPIO_12MA 5
#define GPIO_14MA 6
#define GPIO_16MA 7
/* GPIO TLMM: Status */
#define GPIO_ENABLE 0
#define GPIO_DISABLE 1
/* GPIO_IN_OUT register shifts. */
#define GPIO_IN 1
#define GPIO_OUT 2
static int dev_fd;
//stone added for nfc-test
unsigned char *g_CsnMapBase;
unsigned char *g_ClkMapBase;
unsigned char *g_MosiMapBase;
unsigned char *g_MisoMapBase;
#define NFC_CSN 2
#define NFC_CLK 3
#define NFC_MOSI 0
#define NFC_MISO 98//1
#define NFC_CSN_LOW *(volatile unsigned int *)(g_CsnMapBase + 4) = 0;
#define NFC_CSN_HIGH *(volatile unsigned int *)(g_CsnMapBase + 4) = 02;
#define NFC_CLK_LOW *(volatile unsigned int *)(g_ClkMapBase + 4) = 0;
#define NFC_CLK_HIGH *(volatile unsigned int *)(g_ClkMapBase + 4) = 02;
#define NFC_MOSI_LOW *(volatile unsigned int *)(g_MosiMapBase + 4) = 0;
#define NFC_MOSI_HIGH *(volatile unsigned int *)(g_MosiMapBase + 4) = 02;
#define NFC_GET_MISO (*(volatile unsigned int *)(g_MisoMapBase + 4))& GPIO_IN;
/***********************************************/
void SpiSendByte( unsigned char ucByte )
{
unsigned int i;
for (i=0; i<8; i++)
{
NFC_CLK_LOW;
//usleep(1);
if (ucByte & 0x80)
{
NFC_MOSI_HIGH;
}
else
{
NFC_MOSI_LOW;
}
ucByte=ucByte<<1;
//TODO:usleep may need
//usleep(1);
NFC_CLK_HIGH;
//TODO:usleep may need
//usleep(1);
}
}
unsigned char SpiSendByteThenRecv( unsigned char ucByte )
{
unsigned int i;
unsigned char ucData=0;
unsigned char ucMISO=0;
for (i=0; i<8; i++)
{
NFC_CLK_LOW;
//usleep(1);
if (ucByte & 0x80)
{
NFC_MOSI_HIGH;
}
else
{
NFC_MOSI_LOW;
}
ucByte=ucByte<<1;
//TODO:usleep may need
usleep(1);
//上升沿发送数据
NFC_CLK_HIGH;
//usleep(1);
//TODO:usleep may need
}
usleep(2);
for (i=0; i<8; i++)
{
ucData = ucData<<1;
NFC_CLK_LOW;
usleep(1);
NFC_CLK_HIGH;
//g_MisoMapBase=(unsigned char * )mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED , dev_fd, GPIO_CONFIG_ADDR(NFC_MISO) );
//*(volatile unsigned int *)(g_MisoMapBase) = GPIO_NO_PULL|(GPIO_INPUT<<2)|(GPIO_6MA<<6)|(GPIO_DISABLE<<9);
//usleep(1);
//上升沿读取数据
ucMISO = NFC_GET_MISO;
if (ucMISO == 1)
ucData = ucData | 1;
//TODO:usleep may need
//usleep(1);
}
return ucData;
}
unsigned char Read_Reg(unsigned char address)
{
unsigned char buf, reg_data;
NFC_CSN_LOW;
buf = 0x80|(address<<1);
reg_data = SpiSendByteThenRecv(buf);
NFC_CSN_HIGH;
return reg_data;
}
unsigned char Write_Reg(unsigned char address, unsigned char reg_data)
{
NFC_CSN_LOW;
SpiSendByte( address<<1 );
SpiSendByte( reg_data );
NFC_CSN_HIGH;
return 0;
}
/***********************************************/
int main(int argc, char **argv)
{
dev_fd = open("/dev/mem", O_RDWR | O_NDELAY);
if (dev_fd < 0)
{
printf("open(/dev/mem) failed.");
return 0;
}
//配置GPIO2为输出功能 CS
g_CsnMapBase=(unsigned char * )mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED , dev_fd, GPIO_CONFIG_ADDR(NFC_CSN) );
*(volatile unsigned int *)(g_CsnMapBase) = GPIO_PULL_DOWN|(GPIO_OUTPUT<<2)|(GPIO_16MA<<6)|(GPIO_DISABLE<<9);
//配置GPIO1为输入功能 MISO
g_MisoMapBase=(unsigned char * )mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED , dev_fd, GPIO_CONFIG_ADDR(NFC_MISO) );
*(volatile unsigned int *)(g_MisoMapBase) = GPIO_NO_PULL|(GPIO_INPUT<<2)|(GPIO_6MA<<6)|(GPIO_DISABLE<<9);
//配置GPIO0为输出功能 MOSI
g_MosiMapBase=(unsigned char * )mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED , dev_fd, GPIO_CONFIG_ADDR(NFC_MOSI) );
*(volatile unsigned int *)(g_MosiMapBase) = GPIO_PULL_DOWN|(GPIO_OUTPUT<<2)|(GPIO_16MA<<6)|(GPIO_DISABLE<<9);
//配置GPIO3为输出功能 CLK
g_ClkMapBase=(unsigned char * )mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED , dev_fd, GPIO_CONFIG_ADDR(NFC_CLK) );
*(volatile unsigned int *)(g_ClkMapBase) = GPIO_PULL_DOWN|(GPIO_OUTPUT<<2)|(GPIO_16MA<<6)|(GPIO_DISABLE<<9);
//printf("set GPIO_0,2,3 func \n");
//int i;
//for(i=0; i<10; i++)
Write_Reg(ControlReg, 0x10); // Start Reader mode [4]:1,As an NFC communication initiator
Write_Reg(GsNReg, 0xF0|0x04); // CWGsN = 0xF; ModGsN = 0x4
char ucRegVal = Read_Reg(GsNReg); // Determine if the SPI interface communicates properly
if(ucRegVal!=0xF4) //Verify that the interface is correct
printf("GsNReg set error! \n");
else
printf("GsNReg set ok! \n");
while(1)
{
#if 1
char tmp = Read_Reg(7);
Write_Reg(0x16,0x5a);
printf("get Reg_7=%d \n", tmp);
usleep(2);
#endif
#if 0
NFC_CSN_LOW;
printf("set cs=0 \n");
usleep(1000000);
NFC_CSN_HIGH;
printf("set cs=1 \n");
usleep(1000000);
#endif
#if 0
unsigned char ucMISO = NFC_GET_MISO;
printf("MISO =%d\n", ucMISO);
usleep(200000);
#endif
}
if(dev_fd)
close(dev_fd);
munmap(g_CsnMapBase,MAP_SIZE);
munmap(g_MisoMapBase,MAP_SIZE);
munmap(g_MosiMapBase,MAP_SIZE);
munmap(g_ClkMapBase,MAP_SIZE);
return 0;
}
この変更後、レジスタを読み取るためのテスト時間は約60usであり、これは顧客の要件を満たしています。
もちろん、このような変更には制限があります。GPIOシミュレーション方法では、フォールトトレランスとバッチ送信を保証できません。これは、レジスタ数が少なく、時間効率の要件が比較的高い場合にのみ使用できます。
/ dev / mem:物理メモリの完全ミラー。物理メモリへのアクセスに使用できます。アプリケーションはユーザースペースで実行され、仮想メモリを使用するため、物理アドレススペースに直接アクセスすることはできません。/dev/memファイルを使用して、システムのアドレススペース全体にアクセスできます。
mmap関数を使用する場合、物理アドレスの開始アドレスは少し特殊です。単にアドレスをマッピングするのではなく、指定されたアドレスをページ整列、つまり4K整列する必要があります。たとえば、次の例では、 gpioアドレスは0x11000000 + 0x100、ベースアドレスは0x11000000、オフセットは0x100ですが、0x11000100アドレスをユーザースペースに直接マップしてから、返された仮想アドレスを読み書きすると、これは間違っています。 in / dev / memドライバーでは、0x11000100アドレスは4Kで整列されてから、ユーザースペースに返されます。現時点では、返されるアドレスは希望どおりではありません。正しいアプローチは、0x11000000アドレスをユーザースペースにマップしてから、返された仮想マシンアドレスvaddに0x100を追加することです。
注:/ dev / memを使用する場合は、configでCONFIG_DEVMEMを開く必要があります。