gpio模拟mdc/mdio通信
本文主要是学习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通信的更多相关文章
- S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动
目录:一. 说明 二. 驱动程序说明及问题 三. 案例一 四. 案例二 一. 说明 mini210开发板上带了at24c08, 看了linux内核自带的at24.c的驱动程序,编译下载到看 ...
- gpio模拟i2c驱动
前段时间做项目,需要gpio模拟i2c通信,最后参考了一些资料,然后编写了一个程序.现在发出来,以免以后忘记,也为一些需要的朋友提供参考.不喜勿喷哈. /* 说明:该程序是基于atmel公司的sama ...
- GPIO模拟IIC接口信号质量分析
信号质量有问题的波形001: 信号质量有问题的波形002: 从上图可以看出,GPIO口模拟的I2C接口,电平都存在半高的情况. 因为I2C的接口是通过GPIO模拟实现的,该时钟信号线SCL内部默认为下 ...
- GPIO模拟串口注意是事项
GPIO模拟串口需要注意的事项如下:(程序见我的博客第一篇) 1.由于串口是异步通信,则串口发送必须满足宽度要求. (1)假设串口的波特率是9600bps(1s传输9600个bit),则传输1bit需 ...
- 通用GPIO模拟串口,提供源代码,本人经过测试OK(第一版)
--------------------------serial.h------------------------------------------ #ifndef _SERIAL_H_ #def ...
- STM32F207 两路ADC连续转换及GPIO模拟I2C给MT9V024初始化参数
1.为了更好的方便调试,串口必须要有的,主要打印一些信息,当前时钟.转换后的电压值和I2C读出的数据. 2.通过GPIO 模拟I2C对镁光的MT9V024进行参数初始化.之前用我以前公司SP0A19芯 ...
- GPIO模拟SPI
上次用gpio模拟i2c理解i2c协议.相同的,我用gpio模拟spi来理解spi协议. 我用的是4线spi,四线各自是片选.时钟.命令/数据.数据. 数据在时钟上升沿传递,数据表示的是数据还是命令由 ...
- linux SPI驱动——gpio模拟spi驱动(三)
一:首先在我的平台注册platform_device,保证能让spi-gpio.c能执行到probe函数. 1: struct spi_gpio_platform_data { 2: unsigned ...
- 【转载】GPIO模拟i2c通信
I2C总线的通信过程(见图4-8)主要包含三个主要阶段:起始阶段.数据传输阶段和终止阶段. 1. 起始阶段 在I2C总线不工作的情况下,SDA(数据线)和SCL(时钟线)上的信号均为高电平.如果此时主 ...
随机推荐
- Flutter-Tips
1.报错:flutter: Another exception was thrown: Could not find a generator for route RouteSettings原因是一个工 ...
- idea2020安装教程
2019最新版IDEA亲测可用, 2020最新版IDEA亲测可用, 重要的事说三遍: 如果自己破解不成功建议加群咨询群主:422167709 成功的也可以进群交流 激活码1 N757JE0KCT- ...
- Learning in the Frequency Domain 解读
论文:Learning in the Frequency Domain, CVPR 2020 代码:https://github.com/calmevtime/DCTNet 实际的图像尺寸比较大,无法 ...
- 使用expect在script中切换到root用户(精华)
使用expect在script中切换到root用户 1.尚观版本 http://www.uplook.cn/biancheng/133/1335040/ 1 a. 命令行: /usr/bin/expe ...
- expect使用技巧
1) 获取命令行参数,例如通过./abc.exp a1 a2执行expect脚本 set 变量名1 [lindex $argv 0] 获取第1个参数a1 set 变量名2 [lindex $argv ...
- Django开发之Ajax POST提交403报错
问题现象 Django开发时,前端post提交数据时,由于csrf机制,如果不做处理会报403报错 问题解决 通过在data字段中添加 csrfmiddlewaretoken: '{{ csrf_to ...
- Django开发之Datetime类型JSON序列化时报错
前提回顾 在进行django开发view视图时,如果数据库字段是 datetime类型,在JSON序列化返回时,会出现异常 异常现象 TypeError: Object of type datetim ...
- Vue + Element 实现多选框选项上限提示与限定
上图先,看效果!!! //vue文件夹内<el-form :model="form" class="form-inline"> <!-- :s ...
- Python打开和关闭文件
Python打开和关闭文件: open(文件名,打开文件的模式[,寄存区的缓冲]): 文件名:字符串值 注:文件名带有后缀名 # 打开创建好的 test.txt 文件 f = open("t ...
- PHP xml_parser_get_option() 函数
定义和用法 xml_parser_get_option() 函数从 XML 解析器获取选项.高佣联盟 www.cgewang.com 如果成功,该函数则返回选项值.如果失败,则返回 FALSE 和一个 ...