Wifi模块的主要功能是将信号从串口或TTL电平转化为符合Wifi无线网络通信标准的嵌入式模块。
Wifi模块主要分成三类
比如手机、笔记本、平板电脑上的USB或者SDIO接口模块,Wi-Fi协议栈和驱动是在安卓、Windows、Linux的系统里跑的,是需要非常强大的CPU来完成应用。
典型的是家用路由器,协议和驱动是借助拥有强大Flash和Ram资源的芯片加Linux操作系统。
32位单片机,内置Wi-Fi驱动和协议,接口为一般的MCU接口如UART等。适合于各类智能家居或智能硬件单品。
这里简单讲一下普通wifi与嵌入式wifi模块的区别:
1.通用wifi模块对处理器要求比较高,普通的mcu(单片机)带不起来。
2.嵌入式wifi模块功耗比较低,如果考虑做低功耗,是个不错的选择。
再介绍一下对于开发者而言,普通wifi与嵌入式wifi模块使用的区别:
普通wifi模块: 普通wifi模块是要借助操作系统来跑的,所以需要加载wifi驱动。同时也需要根据wifi的接口是sdio或是usb来加载相应的接口驱动。
嵌入式wifi模块: 这个比较简单,根据wifi模块的封装来决定是uart通信还是spi通信。使用时只需要往里面往uart或者spi里面进行收发数据就好了。
这里我认为不用过多地去纠结他们的名字叫法,毕竟编程和开发都不是为了去闭卷考试。更该关注的是在使用上的差异。
SDIO接口的WIFI模块就是基于SDIO接口的WIFI模块(说得好像废话),可以实现在用户主平台(这里使用Linux操作系统)通过SDIO口到WIFI模块之间的数据转换。SDIO传输数据贼快,在嵌入式领域里使用非常广泛。
SDIO接口就是SD的I/O接口的意思,SD是记忆卡的协议标准,在它的上面再引出一些I/O接口,这种接口就叫做SDIO接口。
SDIO总线和USB总线类似,SDIO总线有两端,一端是HOST端,一端是device端。通信时,HOST端发送命令,Device端只要能够解析命令,就能实现相互通信。
CLK信号: HOST给DEVICE的 时钟信号,每个时钟周期传输一个命令。
CMD信号: 双向的信号,用于传送命令和反应。
DAT0-DAT3 信号: 四条用于传送的数据线。
热插拔就是实现检测你插入了设备还是拔出了设备的意思,嘿嘿。
方法:设置一个 定时器检查 或 插拔中断检测
硬件:假如GPG10(EINT18)用于SD卡检测
GPG10 为高电平,即没有插入SD卡
GPG10为低电平,即插入了SD卡
SDIO总线上都是HOST端发起请求,然后DEVICE端回应请求。sdio命令由6个字节组成。
a – Command:用于开始传输的命令,是由HOST端发往DEVICE端的。其中命令是通过CMD信号线传送的。
b – Response:回应是DEVICE返回的HOST的命令,作为Command的回应。也是通过CMD线传送的。
c – Data:数据是双向的传送的。可以设置为1线模式,也可以设置为4线模式。数据是通过DAT0-DAT3信号线传输的。
SDIO的每次操作都是由HOST在CMD线上发起一个CMD,对于有的CMD,DEVICE需要返回Response,有的则不需要。
对于读命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个读传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。
对于写命令,首先HOST会向DEVICE发送命令,紧接着DEVICE会返回一个握手信号,此时,当HOST收到回应的握手信号后,会将数据放在4位的数据线上,在传送数据的同时会跟随着CRC校验码。当整个写传送完毕后,HOST会再次发送一个命令,通知DEVICE操作完毕,DEVICE同时会返回一个响应。
下面我们要开始干正事了,开始编写SDIO接口的驱动。首先要了解内核与驱动层如何交互。
通过访问[ /include/linux/mmc/host.h ],发现内核中有如下几种数据结构,它们的名称和功能如下所示:
struct mmc_host 用来描述卡控制器
struct mmc_card 用来描述卡
struct mmc_driver 用来描述 mmc 卡驱动
struct sdio_func 用来描述 功能设备
struct mmc_host_ops 用来描述卡控制器操作接口函数功能,用于从主机控制器层向core层注册操作函数,从而将core层与具体的主机控制器隔离。也就是说core要操作主机控制器,就用这个ops当中给的函数指针操作,不能直接调用具体主控制器的函数。
下面参考S3C24XX的驱动程序
static struct platform_driver s3cmci_driver = {
.driver = {
.name = "s3c-sdi", //名称和平台设备定义中的对应
.owner = THIS_MODULE,
.pm = s3cmci_pm_ops,
},
.id_table = s3cmci_driver_ids,
.probe = s3cmci_probe, //平台设备探测接口函数
.remove = __devexit_p(s3cmci_remove),
.shutdown = s3cmci_shutdown,
};
s3cmci_probe(struct platform_device *pdev)
{
//....
struct mmc_host *mmc;
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); //分配mmc_host结构体
//.....
}
/*注册中断处理函数s3cmci_irq,来处理数据收发过程引起的各种中断*/
request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host) //注册中断处理函数s3cmci_irq
/*注册中断处理s3cmci_irq_cd函数,来处理热拨插引起的中断,中断触发的形式为上升沿、下降沿触发*/
request_irq(host->irq_cd, s3cmci_irq_cd,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, DRIVER_NAME, host)
mmc_add_host(mmc); //initialise host hardware //向MMC core注册host驱动
----> device_add(&host->class_dev); //添加设备到mmc_bus_type总线上的设备链表中
----> mmc_start_host(host); //启动mmc host
/*MMC drivers should call this when they detect a card has been inserted or removed.检测sd卡是否插上或移除*/
---->mmc_detect_change(host, 0);
/*Schedule delayed work in the MMC work queue.调度延时工作队列*/
mmc_schedule_delayed_work(&host->detect, delay);
搜索host->detected得到以下信息:[/drivers/mmc/core/host.c]
NIT_DELAYED_WORK(&host->detect, mmc_rescan);
mmc_rescan(struct work_struct *work)
---->mmc_bus_put(host);//card 从bus上移除时,释放它占有的总线空间
/*判断当前mmc host控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0
*如果为1,那么会在while(1)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 *mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他 程序获得mmc主控制器的使用权
*/
mmc_claim_host(host);
mmc_rescan_try_freq(host, max(freqs[i], host->f_min);
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
…
/* Order's important: probe SDIO, then SD, then MMC */
if (!mmc_attach_sdio(host))
return 0;
if (!mmc_attach_sd(host))
return 0;
if (!mmc_attach_mmc(host))
return 0;
….
}
mmc_attach_sdio(struct mmc_host *host) //匹配sdio接口卡
--->mmc_attach_bus(host, &mmc_sdio_ops);
/*当card与总线上的驱动匹配,就初始化card*/
mmc_sdio_init_card(host, host->ocr, NULL, 0);
--->card = mmc_alloc_card(host, NULL);//分配一个card结构体
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); //设置mmc_bus的工作模式
struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; //SDIO functions (devices)
sdio_init_func(host->card, i + 1);
--->func = sdio_alloc_func(card); //分配struct sdio_fun(sdio功能设备)结构体
mmc_io_rw_direct();
card->sdio_func[fn - 1] = func;
mmc_add_card(host->card); //将具体的sdio设备挂载到mmc_bus_types 总线
sdio_add_func(host->card->sdio_func[i]); //将sdio功能设备挂载到sdio_bus_types总线
这里采用中断的方式来检测SDIO设备的插拔状况。当检测到SDIO插拔时,会触发中断,并通知CPU,然后跳到中断服务函数中。mmc_detect_change->mmc_schedule_delayed_work(&host->detect,delay),INIT_DELAYED_WORK(&host->detect, mmc_rescan)会调度mmc_rescan函数延时调度工作队列,这样也会触发SDIO设备的初始化流程,检测到有效的SDIO设备后,会将它注册到系统中去。
static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
{
struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
........
mmc_detect_change(host->mmc, msecs_to_jiffies(500));
return IRQ_HANDLED;
}
通过上述的代码,我们完成了主机端的驱动。