一、ESP8266的三种工作模式
STA模式(Station):
STA 模式就像是你的手机连接到 Wi-Fi 路由器上一样,ESP8266 作为一个 Wi-Fi 客户端,通过接收路由器发出的信号来连接互联网。在这种模式下,ESP8266 是一个被动的设备,它需要等待路由器发出的信号来连接到互联网。
AP模式(Wireless Access Point):
AP 模式就像是你的手机开启 Wi-Fi 热点一样,ESP8266 作为 Wi-Fi 接入点,它创建了一个 Wi-Fi 热点,其他设备可以通过连接到这个热点来实现局域网的无线通信。在这种模式下,ESP8266 是一个主动的设备,其他设备需要通过连接到它来实现通信。
STA+AP模式:
STA+AP 模式就像是你的手机即可以连接到 Wi-Fi 路由器,又可以开启 Wi-Fi 热点一样,ESP8266 同时作为 Wi-Fi 客户端和 Wi-Fi 接入点。它可以连接到一个现有的 Wi-Fi 网络中,同时也可以作为一个 Wi-Fi 热点,使其他设备连接到它。这种模式可以实现广域网与局域网的无缝切换,非常灵活。
如果欲使用ESP8266作为F1C200s的无线网卡,需要使用STA模式或者STA+AP模式,本文所使用的8266固件来自github,具体使用什么模式暂未知晓,欢迎评论区交流。
注:ESP-12F是安信可用ESP8266EX芯片做的一款WiFi模组,安信可ESP8266有几个爆款模组,ESP-01S,ESP-12E,ESP-12F,ESP -12S。
二、驱动入口函数解析
1、虚拟驱动注册及硬件测试
&spi0 {
status = "okay";
esp8089@0 {
status = "okay";
compatible = "boss,esp8089";
spi-cpol;
spi-cpha;
reg = <0>;
spi-max-frequency = <30000000>;
reset= <135>; //PE7
interrupt= <136>; //PE8
debug= <0>;
};
};
根据设备树中
compatible信息”boss,esp8089″,
我们可以找到对应的spi驱动程序,在spi_sif_esp.c文件中,也就意味着驱动和设备匹配成功之后,这个spi的probe函数就会自动执行。
我们首先来看这个驱动程序中的
驱动入口函数__init esp_spi_init(void),
即使编译进内核,该函数在内核启动时同样会被执行:
static int __init esp_spi_init(void) {
#define ESP_WAIT_UP_TIME_MS 11000
int err;
u64 ver;
int retry = 3;
bool powerup = false;
int edf_ret = 0;
long long sem_timeout = 0;
#ifdef TESETS
printk("TESETS hava!\n");
#endif
#ifdef DTESETS
printk("DTESETS hava!\n");
#endif
...
}
首先是一些变量的定义,以及调试信息的输出,esp_debugfs_init函数和调试相关,我们都无需关心。下面的代码是
使用Linux内核唤醒锁功能来防止ESP芯片进入低功耗状态,
ESP芯片有需要长时间运行的任务时,可以获取唤醒锁来防止低功耗,保证任务正常运行,任务结束后释放唤醒锁,ESP芯片重新进入低功耗模式:
esp_wakelock_init(); //加锁
esp_wake_lock(); //锁住
然后是一个do while循环,首先初始化信号量,
通过 sif_platform_target_poweron() 进行硬件重启,随后使用
spi_register_driver(&esp_spi_dummy_driver) 函数注册了一个模拟spi驱动,注意不是真是的spi驱动。
do {
sema_init(&esp_powerup_sem, 0);
sif_platform_target_poweron(); //操作reset_GPIO,硬重启
err = spi_register_driver(&esp_spi_dummy_driver); //注册SPI驱动(模拟SPI)
if (err) {
esp_dbg(ESP_DBG_ERROR, "esp8089_spi: eagle spi driver registration failed, error code: %d\n", err);
goto _fail;
}
#ifdef REGISTER_SPI_BOARD_INFO
spi = sif_platform_new_device(); //这里手动添加的SPI_device,也就是从设备
if (spi) {
printk("esp8089_spi: register board OK\n");
//err = esp_spi_dummy_probe(spi);
}
else
printk("esp8089_spi: No slave to probe\n");
if (err) {
printk("esp8089_spi: dummy probe failure");
goto _fail;
}
#endif
sem_timeout = down_timeout(&esp_powerup_sem, msecs_to_jiffies(ESP_WAIT_UP_TIME_MS)); //获取信号量
printk("esp8089_spi: sem_timeout = %lld\n", sem_timeout);
if (sem_timeout == 0) {
powerup = true;
msleep(200);
break;// 一次结束
}
esp_dbg(ESP_SHOW, "esp8089_spi: %s ------ RETRY ------ \n", __func__);
sif_record_retry_config(); //retry_reset = 1;
spi_unregister_driver(&esp_spi_dummy_driver); //卸载,进行下一步
sif_platform_target_poweroff();//reset_GPIO电平=0
//再来一次
} while (--retry);
我们可以看到上面注册的驱动实际对应下面的操作结构体和函数,
eagle_dummy没有在其他文件找到,而且此处我们使用的是设备树进行描述硬件信息,所以这个驱动没有对应的设备!
没有设备那么驱动和设备就不会相互匹配,那么下面的probe函数也就不会执行!
static int esp_spi_dummy_probe(struct spi_device *spi)
{
esp_dbg(ESP_DBG_ERROR, "esp8089_spi: %s enter\n", __func__);
up(&esp_powerup_sem);
return 0;
}
static int esp_spi_dummy_remove(struct spi_device *spi)
{
return 0;
}
struct spi_driver esp_spi_dummy_driver = {
.id_table = esp_spi_id,
.driver = {
.name = "eagle_dummy",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = esp_spi_dummy_probe,
.remove = esp_spi_dummy_remove,
};
不会执行那不行啊,因此紧接着使用了
sif_platform_new_device()手动添加了一个设备。
struct spi_device* sif_platform_new_device(void) {
struct device *dev;
char str [32];
master = spi_busnum_to_master(esp_board_spi_devices[0].bus_num);// 寻找主设备,返回一个指向spi_master结构体的指针
if(!master)
printk("esp8089_spi: FAILED to find master\n");
snprintf(str, sizeof(str), "%s.%u", dev_name(&master->dev), esp_board_spi_devices[0].chip_select);
dev = bus_find_device_by_name(&spi_bus_type, NULL, str);// 用来查找是否存在名为str的从设备
if(dev) {
printk("esp8089_spi: Find a dev using spi ,deleting! \n");
device_del(dev);
}
else {
printk("esp8089_spi: No dev using spi! \n");// 没有设备使用此总线
}
spi = spi_new_device( master, esp_board_spi_devices );//esp_spi_dummy_probe
if(!spi)
printk("esp8089_spi: FAILED to create slave\n");
if(spi_setup(spi))
printk("esp8089_spi: FAILED to setup slave\n");
return spi;
}
这里为什么需要手动添加SPI设备,一般不是直接通过 spi_register_driver函数注册spi设备驱动就可以了嘛(此处是模拟的SPI设备,并不是真实存在的,因此需要手动添加)?
硬件平台不支持自动探测:有些硬件平台可能不支持自动探测SPI设备,需要手动添加设备节点并初始化设备。
多个SPI设备共用一个驱动程序:当多个SPI设备共用同一个驱动程序时,需要手动添加每个设备节点以便内核能够正确识别和区分不同的设备。
其中
spi_busnum_to_master()函数
是一个用于查找SPI总线主设备的函数,其作用是
将SPI总线号转换为对应的SPI总线主设备,以便对该总线上的从设备进行操作。
struct spi_master *spi_busnum_to_master(unsigned busnum);
其中,busnum表示需要查找的SPI总线号(如0、1,分别代表SPI0、SPI1)。该函数返回一个指向spi_master结构体的指针,如果该总线不存在或者未被初始化,则返回NULL。
注:在Linux内核中,
每个SPI总线都有一个对应的spi_master结构体表示,
该结构体包含了该总线的详细信息,如总线名称、时钟频率、总线模式等。SPI从设备通过连接到SPI总线上来与主设备进行通信。
注:该函数只能查找已经被初始化的SPI总线主设备,如果对应的SPI总线尚未被初始化,则该函数返回
NULL
。(此处SPI0肯定是被初始化了,因为设备树中提供了SPI0的设备信息,对应的驱动直接进行匹配初始化,普通驱动开发者无需关心SPI总线驱动)
注:
在SPI总线上,可以连接多个从设备,但只能有一个主设备,因为主设备负责控制总线的时序和数据传输。
SPI主机驱动程序就是SPI主设备驱动程序,用于控制和管理SPI总线上的从设备。通常情况下,非芯片原厂的普通驱动开发者接触到的是SPI从设备,而不是SPI主设备。
再往下看,
bus_find_device_by_name()函数
的作用(
用来查找是否存在名为name的
从设备
)
具体来说,bus_find_device_by_name()函数的参数包括一个bus_type类型的指针,一个start类型的设备节点指针和一个name类型的设备名称字符串。函数的返回值是一个指向设备节点的指针,如果未找到对应名称的设备节点,则返回NULL。
在Linux设备模型中,设备节点名称通常是唯一的,用于标识设备节点。为了保证设备节点名称的唯一性,通常需要使用一些特定的命名规则来生成设备节点名称。对于SPI从设备来说,设备节点名称通常由两部分组成:设备界定名称(device qualifier name)和从设备片选信号(slave select signal)。设备界定名称用于标识连接到同一SPI总线上的不同从设备,从设备片选信号用于标识特定的从设备。
找到主设备,确保没有同名的从设备挂载后,开始添加从设备。
struct spi_device *spi_new_device(struct spi_master *master,
struct spi_board_info *info);
spi_new_device()函数是Linux SPI子系统中的一个函数,用于创建并注册一个新的SPI从设备。
函数返回值为指向新创建的SPI从设备对象的指针,如果创建失败则返回NULL。当然,申请设备后,设备和驱动匹配完成,会执行虚拟spi的probe函数,只是输出了一条调试信息。
从设备注册完成后需要进行初始化:
int spi_setup(struct spi_device *spi);
在进行SPI通信之前,必须先调用spi_setup()函数对SPI设备进行初始化。在初始化SPI设备时,该函数会执行以下操作:函数返回值为0表示成功,返回负数表示失败。
- 根据设备属性来设置SPI控制器的工作模式,包括时钟极性、时钟相位、最大时钟频率等等。
- 初始化SPI设备的一些默认参数,如传输字节顺序、传输模式等等。
在初始化SPI设备之后,就可以通过SPI总线和主设备来访问该SPI设备,并进行数据传输等操作。
回到init入口函数中,继续往下看,我们发现他居然把spi从设备卸载掉了!
(虚拟spi注册成功后跳出do while,然后卸载)我们不是要spi接口通信嘛?怎么卸载掉了?
第一次注册spi设备后,什么都没做,为什么又卸载掉了是什么原因?
在代码中,
第一次注册的驱动程序esp_spi_dummy_driver只是用于测试主机驱动硬件,以及验证从机设备是否正常初始化。
在这种情况下,该驱动程序仅仅用于测试,没有实际的功能,因此在探测完成后就被卸载掉了。通过这种方式,
可以在不影响正常工作的情况下,对硬件进行测试和验证,确保系统能够正常工作。
随后,真正的驱动程序esp_spi_driver被注册,用于控制和管理SPI从设备的操作。
2、SPI设备两次注册
继续看init入口函数,通过一下代码记录一下状态,
表明是第一次初始化。
sif_sdio_state = ESP_SDIO_STATE_FIRST_INIT;
然后使用以下代码
注册一个实际的spi设备,
这次注册的应该是我们可以直接使用的spi设备了!
spi_register_driver(&esp_spi_driver);
在主设备已经启动的情况下(spi0设备树节点,也就是dtsi文件中的设备树,已经匹配了主机驱动,对主机进行了初始化),如果在运行时动态地注册新的SPI从设备,
那么在注册完成之后,设备驱动的probe函数会被立即调用。
OK,现在我们要跳到真正的probe函数中了:
static int esp_spi_probe(struct spi_device *spi)
{
int err;
struct esp_pub *epub;
struct esp_spi_ctrl *sctrl;
static int dt_get_n=1;
if(dt_get_n > 0 ) {
esp8089_get_dt_data(spi); // 获取设备树信息
dt_get_n--;
}
...
}
首先是
通过 esp8089_get_dt_data 函数调用 esp8089_probe_dt 获取设备树信息
,这里不再展开讲解。
int esp8089_get_dt_data(struct spi_device *spi)
{
struct device *dev;
int ret;
dev = &spi->dev;
esp8089_dts_info = devm_kzalloc(dev, sizeof(*esp8089_dts_info), GFP_KERNEL);
if (!esp8089_dts_info)
return -ENOMEM;
ret = esp8089_probe_dt(dev);// 获取信息
if(ret != 0)
dev_info(dev," esp8089_probe_dt error\n");
dev_info(dev,"Finish got dt data\n");
return 0;
}
然后使用esp_setup_spi函数
分配了主机spi通信的缓冲区内存,并设置了一些参数值。
err = esp_setup_spi(spi); //分配一些内存,设置参数
然后通过上面sif_spi_protocol_init函数通过spi发送了一些指令,初始化配置从机的spi。
err = sif_spi_protocol_init(spi); //发送一堆指令
我们来看一下这个sif_spi_protocol_init函数,函数的内容如下:
int sif_spi_protocol_init(struct spi_device *spi) { unsigned char spi_proto_ini_status = 0; unsigned char rx_buf1[10]; unsigned char tx_buf1[10]; unsigned char dummy_tx_buf[10]; memset(dummy_tx_buf,0xff,sizeof(dummy_tx_buf)); printk("esp8089_spi: %s\n", __func__); do { //会依次执行下面的命令, if( spi_proto_ini_status == 0 ) { int fail_count = 0; do { //第二层循环 tx_buf1[0]=0x40; tx_buf1[1]=0x00; tx_buf1[2]=0x00; tx_buf1[3]=0x00; tx_buf1[4]=0x00; tx_buf1[5]=0x95; //printf("CMD0 \n"); printk("esp8089_spi: %s, %d\n", __FILE__, __LINE__); printk("esp8089_spi: fail_count = %d\n", fail_count); sif_spi_write_raw_proto(spi, tx_buf1, 6); mdelay(100); sif_spi_write_async_read_proto(spi, dummy_tx_buf, rx_buf1, 10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1],rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); mdelay(100); if(++fail_count > 10) return -ETIMEDOUT; } while( rx_buf1[2] != 0x01 ); } else if( spi_proto_ini_status == 1 ) { tx_buf1[0]=0x45; tx_buf1[1]=0x00; tx_buf1[2]=0x20; //0x04; tx_buf1[3]=0x00; tx_buf1[4]=0x00; tx_buf1[5]=0x01; //spi_err("CMD 5 1st\n"); printk("esp8089_spi: %s, %d\n", __FILE__, __LINE__); sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1],rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); } ... else if (spi_proto_ini_status==15) { //spi_err("CMD52 Write Reg addr 0x110 \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x80; //func0 should be tx_buf1[2]=0x02; tx_buf1[3]=0x20; tx_buf1[4]=(unsigned char)(SPI_BLOCK_SIZE & 0xff); //0x02; tx_buf1[5]=0x01; printk("esp8089_spi: %s, %d\n", __FILE__, __LINE__); sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //spi_err("CMD52 Write Reg addr 0x111 \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x80; tx_buf1[2]=0x02; tx_buf1[3]=0x22; tx_buf1[4]=(unsigned char)(SPI_BLOCK_SIZE>>8); //0x00; tx_buf1[5]=0x01; printk("esp8089_spi: %s, %d\n", __FILE__, __LINE__); sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1],rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //spi_err("CMD52 Write Reg addr 0x111 \n"); /* set boot mode */ tx_buf1[0]=0x74; tx_buf1[1]=0x80; tx_buf1[2]=0x41; tx_buf1[3]=0xe0; tx_buf1[4]=0x01; //0x00; tx_buf1[5]=0x01; printk("esp8089_spi: %s, %d\n", __FILE__, __LINE__); sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1],rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); } else if (spi_proto_ini_status==16) { #if 0 //printf("CMD52 Write Reg addr 0x40 \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x80; tx_buf1[4]=0x91; //0x02; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //sif_spi_read_bytes( 0x0c,rx_buf1, 4); //printf("CMD52 Write Reg addr 0x3c \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x78; tx_buf1[4]=0x3f; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //printf("CMD52 Write Reg addr 0x3d \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x7a; tx_buf1[4]=0x34; //0x02; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //printf("CMD52 Write Reg addr 0x3e \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x7c; tx_buf1[4]=0xfe; //0x02; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //printf("CMD52 Write Reg addr 0x3f \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x7e; tx_buf1[4]=0x00; //0x02; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); //printf("CMD52 Write Reg addr 0x40 \n"); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x80; tx_buf1[4]=0xd1; //0x02; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); tx_buf1[0]=0x74; tx_buf1[1]=0x90; tx_buf1[2]=0x00; tx_buf1[3]=0x52; tx_buf1[4]=0x30; //0x02; tx_buf1[5]=0x01; sif_spi_write_raw(spi, tx_buf1, 6); sif_spi_write_async_read(spi,dummy_tx_buf, rx_buf1,10); esp_dbg(ESP_DBG_ERROR, "rx:[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x],[0x%02x]\n", rx_buf1[0],rx_buf1[1] ,rx_buf1[2],rx_buf1[3],rx_buf1[4],rx_buf1[5],rx_buf1[6],rx_buf1[7],rx_buf1[8],rx_buf1[9]); #endif } else { break; } mdelay(500); spi_proto_ini_status++; } while (1); return 0; }
可以看到,这个函数主要是发送了一些SDIO的CMD指令,什么鬼?为什么发送的是SDIO指令呢?我们用的不是SPI通信协议嘛?
先看一下我们的硬件连接,
[GPIO15, GPIO0, GPIO2] = [1, 1, 0]。
在看一下8266的boot mode,
boot mode 由 strapping 管脚的 3 位值 [GPIO15, GPIO0, GPIO2] 共同决定。
如下表所示:(由乐鑫官方
提供
)
所以说我们硬件配置了boot mode为SDIO highspeed v1 uart boot,但是我们的主控F1C200s使用的是SPI通信,那怎么办呢?8266的
SDIO接口的支持传输模式!
参考这里
https://bbs.csdn.net/topics/210052065
。也就是说我们需要通过F1将8266的
SDIO接口设置为SPI兼容模式。
但是F1和8266上电之后一个是SPI通信,一个是SDIO通信,怎么通信呢?不能通信怎么将8266设置为SPI兼容模式呢?不用担心,
由于 ESP8266 启动时,默认读取程序的 SPI 接口与 SDIO 接口复用相同的芯片管脚
(摘自
https://www.espressif.com.cn/sites/default/files/documentation/esp8266-technical_reference_cn.pdf
)
所以这个函数的大致功能(发送的具体指令的参数不知道什么意思)就是,通过SPI命令配置SDIO接口为SPI模式。
首先复位卡(CMD0),进入idle状态;
CMD5命令被用于将SDIO接口从默认的SDIO模式切换到SPI模式;ESP8266的CMD52命令是一条SDIO命令,用于访问SDIO设备的寄存器;
最后将 ESP8266 的启动模式设置为 “Flash boot” 模式,即
在启动时从 Flash 存储器中加载固件程序。(不确定)
继续来看probe函数, 为esp_spi_ctrl结构体类型的sctrl(意为spi ctrl)分配了空间,sctrl描述了ESP8266芯片与外部SPI设备之间的通信控制信息。
sctrl = kzalloc(sizeof(struct esp_spi_ctrl), GFP_KERNEL);
esp_pub_alloc_mac80211函数的主要作用是为ESP8266芯片分配一个
struct esp_pub类型的结构体变量,
并对该变量的一些成员变量进行初始化。
该结构体的主要作用是存储驱动程序的公共数据,包括设备节点、网络设备、虚拟接口、硬件接口等等。
通过这些成员变量,驱动程序可以与Linux内核和Mac80211子系统进行交互,实现网络设备的注册、配置、发送和接收数据等功能。
epub = esp_pub_alloc_mac80211(&spi->dev);
然后通过check_target_id函数检查ESP芯片的目标ID,并根据目标ID设置相关的配置选项。
check_target_id(epub);
再往后开
执行 esp_pub_init_all(epub) 函数,
该函数定义在esp_main.c中
我们来看一下这个函数的详细内容:
为什么初始化了一次驱动,又卸载掉了?
第一次初始化时进行SDIO总线和卡的配置,启动SDIO传输;在第二次初始化时进行SDIO总线的重置,再次进行SDIO总线和卡的配置,启动SDIO传输等。同时,在出现错误时,驱动程序可以根据当前的状态进行相应的处理,例如进行重试、回滚操作等,以确保SDIO总线的正确性和可靠性
在ESP8089中,”fw” 通常指 “firmware”,即固件。固件是一种嵌入式软件,通常被存储在设备的非易失性存储器中,并用于控制设备的硬件操作和功能。在ESP8089中,固件是指用于控制ESP芯片的操作和功能的软件程序。
参考内容