本文主要是学习gpio模拟mdc/mdio通信。

运行环境是在ATMEL的sama5d35MCU,两个GPIO引脚模拟MDC/MDIO通信,读取百兆phy的寄存器的值。

 #include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
#include<linux/sched.h>
#include<linux/init.h>
#include<linux/sched.h>
#include<linux/completion.h>
#include <asm/system.h>
#include <linux/param.h>
#include<linux/gpio.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/device.h>
#include<linux/slab.h>
#include<asm/uaccess.h>
#include<linux/delay.h>
#include<linux/miscdevice.h> /* bb:bit-bang,通过gpio引脚,用软件模拟通信*/ #define MDIO 117 /* MDIO correspond PD21 */
#define MDC 116 /* MDC correspond PD20 */
#define MDIO_DELAY 250
#define MDIO_READ_DELAY 350 /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit
* IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips.
* */
#define MII_ADDR_C45 (1<<30) #define MDIO_READ 2
#define MDIO_WRITE 1 #define MDIO_C45 (1<<15)
#define MDIO_C45_ADDR (MDIO_C45 | 0)
#define MDIO_C45_READ (MDIO_C45 | 3)
#define MDIO_C45_WRITE (MDIO_C45 | 1) #define MDIO_SETUP_TIME 10
#define MDIO_HOLD_TIME 10 //#define READ_REG 0x37
//#define WRITE_REG 0x38 #define MDIO_C45_TEST 0 typedef struct gpio_ctrl_blk{
int pin;
int value;
}gpio_cblk_t; typedef struct phy_reg_blk{
unsigned int phy_address;
unsigned int reg_address;
unsigned int reg_value;
}phy_reg_cblk_t; #define MDIO_DEV_ID 't'
#define READ_REG _IOWR (MDIO_DEV_ID,0x37,phy_reg_cblk_t)
#define WRITE_REG _IOWR (MDIO_DEV_ID,0x38,phy_reg_cblk_t)
static void MDC_OUT(void);
static void MDIO_OUT(void);
static void MDIO_IN(void);
static void MDC_H(void);
static void MDC_L(void);
static int GET_MDIO(void);
static void SET_MDIO(int val); /* 设置MDC为输出引脚,在MDC输出时钟之前设置 */
static void MDC_OUT(void)
{
gpio_cblk_t gpio_dev;
gpio_dev.pin = MDC;
at91_set_gpio_output(gpio_dev.pin,);
} /* 设置MDIO的gpio引脚为输出引脚 */
static void MDIO_OUT(void)
{
gpio_cblk_t gpio_dev;
gpio_dev.pin = MDIO;
at91_set_gpio_output(gpio_dev.pin,);
} /* 设置MDIO的gpio引脚为输入引脚 */
static void MDIO_IN(void)
{
gpio_cblk_t gpio_dev;
gpio_dev.pin = MDIO;
at91_set_gpio_input(gpio_dev.pin,);
} /* MDC输出高电平,在MDC设置为输出后调用 */
static void MDC_H(void)
{
gpio_cblk_t gpio_dev; gpio_dev.pin = MDC;
gpio_dev.value = ;
at91_set_gpio_value(gpio_dev.pin,gpio_dev.value);
} /* MDC输出低电平,在MDC设置为输出后调用 */
static void MDC_L(void)
{
gpio_cblk_t gpio_dev; gpio_dev.pin = MDC;
gpio_dev.value = ;
at91_set_gpio_value(gpio_dev.pin,gpio_dev.value);
} /* 获得MDIO的数据,只获得一个bit */
static int GET_MDIO(void)
{
gpio_cblk_t gpio_dev; gpio_dev.pin = MDIO;
gpio_dev.value = at91_get_gpio_value(gpio_dev.pin); return gpio_dev.value;
} /* 设置MDIO的数据,一个bit */
static void SET_MDIO(int val)
{
gpio_cblk_t gpio_dev; gpio_dev.pin = MDIO;
gpio_dev.value = val;
at91_set_gpio_value(gpio_dev.pin,gpio_dev.value);
} /* MDIO发送一个bit的数据,MDIO必须已经被配置为输出 */
static void mdio_bb_send_bit(int val)
{
MDC_OUT();
SET_MDIO(val);
ndelay(MDIO_DELAY);
MDC_L();
ndelay(MDIO_DELAY);
MDC_H();
} /* MDIO 获取一个bit的数据,MDIO必须已经被配置为输入. */
static int mdio_bb_get_bit(void)
{
int value; MDC_OUT();
ndelay(MDIO_DELAY);
MDC_L();
ndelay(MDIO_READ_DELAY);
// ndelay(MDIO_DELAY);
MDC_H(); value = GET_MDIO(); return value;
} /*
* MDIO发送一个数据,MDIO 必须被配置为输出模式.
* value:要发送的数据
* bits:数据的位数
*
* */
static void mdio_bb_send_num(unsigned int value ,int bits)
{
int i;
MDIO_OUT(); for(i = bits - ; i >= ; i--)
mdio_bb_send_bit((value >> i) & );
} /*
* MDIO获取一个数据,MDIO 必须被配置为输入模式.
* bits:获取数据的位数
*
* */
static int mdio_bb_get_num(int bits)
{
int i;
int ret = ;
for(i = bits - ; i >= ; i--)
{
ret <<= ;
ret |= mdio_bb_get_bit();
} return ret;
} /* Utility to send the preamble, address, and
* register (common to read and write).
*/
static void mdio_bb_cmd(int op,int phy,int reg)
{
int i = ;
MDIO_OUT(); //设置MDIO引脚为输出引脚 /*发送32bit的1,这个帧前缀域不是必须的,某些物理层芯片的MDIO操作就没有这个域*/
for(i = ; i < ; i++)
mdio_bb_send_bit(); /* 发送开始位(01),和读操作码(10),写操作码(01)
* Clause 45 操作,开始位是(00),(11)为读,(10)为写
*/ #if MDIO_C45_TEST
mdio_bb_send_bit();
if(op & MDIO_C45)
mdio_bb_send_bit();
else
mdio_bb_send_bit(); #else
mdio_bb_send_bit();
mdio_bb_send_bit(); #endif
mdio_bb_send_bit((op >> ) & );
mdio_bb_send_bit((op >> ) & ); mdio_bb_send_num(phy,);
mdio_bb_send_num(reg,); } static int mdio_bb_cmd_addr(int phy,int addr)
{
unsigned int dev_addr = (addr >> ) & 0x1F;
unsigned int reg = addr & 0xFFFF; mdio_bb_cmd(MDIO_C45_ADDR,phy,dev_addr); /* send the turnaround (10) */
mdio_bb_send_bit();
mdio_bb_send_bit(); mdio_bb_send_num(reg,); MDIO_IN();
mdio_bb_get_bit(); return dev_addr;
} void mdio_set_turnaround(void)
{
int i = ; MDIO_IN();
MDC_OUT();
for(i=;i<;i++)
{
ndelay(MDIO_DELAY);
MDC_L();
ndelay(MDIO_DELAY);
MDC_H();
}
} static unsigned int mdio_bb_read(int phy,int reg)
{
unsigned int ret,i; #if MDIO_C45_TEST
/* 寄存器是否满足有C45标志 */
if(reg & MII_ADDR_C45)
{
reg = mdio_bb_cmd_addr(phy,reg);
mdio_bb_cmd(MDIO_C45_READ,phy,reg);
}
else
mdio_bb_cmd(MDIO_READ,phy,reg);
#else
mdio_bb_cmd(MDIO_READ,phy,reg);
#endif
MDIO_IN();
//mdio_set_turnaround();
/* check the turnaround bit: the PHY should be driving it to zero */
if(mdio_bb_get_bit() != )
{
/* PHY didn't driver TA low -- flush any bits it may be trying to send*/
for(i = ; i < ; i++)
mdio_bb_get_bit();
return 0xFFFF;
} ret = mdio_bb_get_num();
mdio_bb_get_bit(); return ret;
} static int mdio_bb_write(unsigned int phy,unsigned int reg,unsigned int val)
{
#if MDIO_C45_TEST
if(reg & MII_ADDR_C45)
{
reg = mdio_bb_cmd_addr(phy,reg);
mdio_bb_cmd(MDIO_C45_WRITE,phy,reg);
}
else
mdio_bb_cmd(MDIO_WRITE,phy,reg);
#else
mdio_bb_cmd(MDIO_WRITE,phy,reg);
#endif #if 1
/* send the turnaround (10) */
mdio_bb_send_bit();
mdio_bb_send_bit();
#else
mdio_set_turnaround();
#endif
mdio_bb_send_num(val,); MDIO_IN();
//mdio_bb_get_bit(); return ;
} static int mdio_ctrl_drv_open(struct inode *inode, struct file *file )
{
return ;
} static int mdio_ctrl_drv_release(struct inode *inode, struct file *file )
{
return ;
} static long mdio_ctrl_drv_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
phy_reg_cblk_t phy_reg;
int ret = ; void __user *argp = (void __user *)arg;
if( argp==NULL )
{
return -EFAULT;
} if (copy_from_user(&phy_reg, argp, sizeof(phy_reg_cblk_t))) {
return -EFAULT;
} switch (cmd) {
case READ_REG:
phy_reg.reg_value = mdio_bb_read(phy_reg.phy_address,phy_reg.reg_address);
if(copy_to_user(argp,&phy_reg,sizeof(phy_reg_cblk_t)))
{
return -EFAULT;
}
break;
case WRITE_REG:
ret = mdio_bb_write(phy_reg.phy_address,phy_reg.reg_address,phy_reg.reg_value);
default:
return -EINVAL; } return ;
} static struct file_operations mdio_ctl_drv_fileops = {
.owner = THIS_MODULE,
.open = mdio_ctrl_drv_open,
.unlocked_ioctl = mdio_ctrl_drv_unlocked_ioctl,
.release = mdio_ctrl_drv_release
}; static struct miscdevice mdio_dev = {
MISC_DYNAMIC_MINOR,
"mdio_dev",
&mdio_ctl_drv_fileops,
}; int mdio_ctrl_drv_module_init(void)
{
int ret = ; ret = misc_register(&mdio_dev);
if(ret != )
{
ret = -EFAULT;
return ret;
}
printk("mdio_drv_init ok\n");
return ;
} void mdio_ctrl_drv_module_exit(void)
{
misc_deregister(&mdio_dev);
printk("mdio_drv_exit ok\n");
} module_init(mdio_ctrl_drv_module_init);
module_exit(mdio_ctrl_drv_module_exit);
MODULE_LICENSE("GPL");

gpio模拟mdc/mdio通信的更多相关文章

  1. S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动

    目录:一. 说明 二. 驱动程序说明及问题 三. 案例一       四. 案例二 一. 说明 mini210开发板上带了at24c08, 看了linux内核自带的at24.c的驱动程序,编译下载到看 ...

  2. gpio模拟i2c驱动

    前段时间做项目,需要gpio模拟i2c通信,最后参考了一些资料,然后编写了一个程序.现在发出来,以免以后忘记,也为一些需要的朋友提供参考.不喜勿喷哈. /* 说明:该程序是基于atmel公司的sama ...

  3. GPIO模拟IIC接口信号质量分析

    信号质量有问题的波形001: 信号质量有问题的波形002: 从上图可以看出,GPIO口模拟的I2C接口,电平都存在半高的情况. 因为I2C的接口是通过GPIO模拟实现的,该时钟信号线SCL内部默认为下 ...

  4. GPIO模拟串口注意是事项

    GPIO模拟串口需要注意的事项如下:(程序见我的博客第一篇) 1.由于串口是异步通信,则串口发送必须满足宽度要求. (1)假设串口的波特率是9600bps(1s传输9600个bit),则传输1bit需 ...

  5. 通用GPIO模拟串口,提供源代码,本人经过测试OK(第一版)

    --------------------------serial.h------------------------------------------ #ifndef _SERIAL_H_ #def ...

  6. STM32F207 两路ADC连续转换及GPIO模拟I2C给MT9V024初始化参数

    1.为了更好的方便调试,串口必须要有的,主要打印一些信息,当前时钟.转换后的电压值和I2C读出的数据. 2.通过GPIO 模拟I2C对镁光的MT9V024进行参数初始化.之前用我以前公司SP0A19芯 ...

  7. GPIO模拟SPI

    上次用gpio模拟i2c理解i2c协议.相同的,我用gpio模拟spi来理解spi协议. 我用的是4线spi,四线各自是片选.时钟.命令/数据.数据. 数据在时钟上升沿传递,数据表示的是数据还是命令由 ...

  8. linux SPI驱动——gpio模拟spi驱动(三)

    一:首先在我的平台注册platform_device,保证能让spi-gpio.c能执行到probe函数. 1: struct spi_gpio_platform_data { 2: unsigned ...

  9. 【转载】GPIO模拟i2c通信

    I2C总线的通信过程(见图4-8)主要包含三个主要阶段:起始阶段.数据传输阶段和终止阶段. 1. 起始阶段 在I2C总线不工作的情况下,SDA(数据线)和SCL(时钟线)上的信号均为高电平.如果此时主 ...

随机推荐

  1. B站学习的回顾总结

    视频地址 https://www.bilibili.com/video/av50680998/ 1.MVC 和MVVM有什么区别? MVC 是后端开发的概念: Model   view  contro ...

  2. java基础(二)--main方法讲解

    main()函数是如下的固定格式,除了args可以修改名字,其余均不可以修改 public class TestBase02MainMath { public static void main(Str ...

  3. Seaborn实现单变量分析

    import numpy as np import pandas as pd from scipy import stats,integrate import matplotlib.pyplot as ...

  4. Python os.dup2() 方法

    概述 os.dup2() 方法用于将一个文件描述符 fd 复制到另一个 fd2.高佣联盟 www.cgewang.com Unix, Windows 上可用. 语法 dup2()方法语法格式如下: o ...

  5. Python os.close() 方法

    概述 os.close() 方法用于关闭指定的文件描述符 fd.高佣联盟 www.cgewang.com 语法 close()方法语法格式如下: os.close(fd); 参数 fd -- 文件描述 ...

  6. Oracle APEX 发送邮件

    1.网络服务安全设置 Oracle 11gR2的版本,可能导致邮件发送失败(ORA-24247: network access denied by access control list (ACL)) ...

  7. Linux下关闭防火墙

    1:查看防火状态 **systemctl status firewalld** **service iptables status** 2:暂时关闭防火墙 systemctl stop firewal ...

  8. IDEA插件配置推荐

    一.配置 [自动编译]如下图配置:推荐指数[***] [忽略大小写]说明:IDEA默认是匹配大小写,此开关如果未关,你输入字符一定要符合大小写.比如敲string是不会出现代码提示或只能补充.但是如果 ...

  9. sql developer连接mysql数据库

    1 首先打开sql developer ,选择上方菜单,工具(tools)--->首选项,如下图 2  数据库(database)--->第三方 JDBC驱动程序 ; “添加条目”,选择m ...

  10. Hive: Reflect UDF

    Reflect UDF JAVA类和方法通常用于处理用户希望在hive中使用的确切的函数.相比与封装一个UDF然后调用这个方法,这些 方法主要通过反射UDF去调用.Reflect使用Java反射去实例 ...