当前位置: 首页 > news >正文

基于用户空间操作IIC接口调试云台电机

         在云台机的产品中,会用到电机,电机也有很多不同控制方式的种类,比如基于GPIO的,两个电平控制来控制去,来回倒腾控制,完成你想要的控制目标,这种是最简单的,但是需要更多的IO引脚;也有基于SPI接口的,还有基于IIC接口的,从引脚数量来说,基于IIC的是最少的,在IO引脚紧张的情况下,选用IIC的是最合适的。

/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/edsam49原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

     IIC设备我们都知道要写一个IIC的设备驱动,传统的方式我们把设备驱动编译成一个KO,或者buildin到内核里面去。本文笔者将介绍基于用户空间操作IIC的接口来完成做云台电机的驱动控制;先看看IIC用户层接口吧!

#include <sys/types.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>/* I2c device */
typedef struct i2c_device {int bus;			        /* I2C Bus fd, return from i2c_open */unsigned short addr;		/* I2C device(slave) address */unsigned char tenbit;		/* I2C is 10 bit device address */unsigned char delay;		/* I2C internal operation delay, unit millisecond */unsigned short flags;		/* I2C i2c_ioctl_read/write flags */unsigned int page_bytes;    /* I2C max number of bytes per page, 1K/2K 8, 4K/8K/16K 16, 32K/64K 32 etc */unsigned int iaddr_bytes;   /* I2C device internal(word) address bytes, such as: 24C04 1 byte, 24C64 2 bytes */
} I2CDevice;/* Close i2c bus */
void i2c_close(int bus);/* Open i2c bus, return i2c bus fd */
int i2c_open(const char *bus_name);/* Initialize I2CDevice with default value */
void i2c_init_device(I2CDevice *device);/* Get i2c device description */
char *i2c_get_device_desc(const I2CDevice *device, char *buf, size_t size);/* Select i2c device on i2c bus */
int i2c_select(int bus, unsigned long dev_addr, unsigned long tenbit);/* I2C internal(word) address convert */
void i2c_iaddr_convert(unsigned int int_addr, unsigned int iaddr_bytes, unsigned char *addr);/* I2C file I/O read, write */
ssize_t i2c_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len);
ssize_t i2c_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len);/* I2c ioctl read, write can set i2c flags */
ssize_t i2c_ioctl_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len);
ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len);/* I2C read / write handle function */
typedef ssize_t (*I2C_READ_HANDLE)(const I2CDevice *dev, unsigned int iaddr, void *buf, size_t len);
typedef ssize_t (*I2C_WRITE_HANDLE)(const I2CDevice *dev, unsigned int iaddr, const void *buf, size_t len);

接口源码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "i2c.h"/* I2C default delay */
#define I2C_DEFAULT_DELAY 1/* I2C internal address max length */
#define INT_ADDR_MAX_BYTES 4/* I2C page max bytes */
#define PAGE_MAX_BYTES 4096#define GET_I2C_DELAY(delay) ((delay) == 0 ? I2C_DEFAULT_DELAY : (delay))
#define GET_I2C_FLAGS(tenbit, flags) ((tenbit) ? ((flags) | I2C_M_TEN) : (flags))
#define GET_WRITE_SIZE(addr, remain, page_bytes) ((addr) + (remain) > (page_bytes) ? (page_bytes) - (addr) : remain)static void i2c_delay(unsigned char delay);/*
**	@brief		:	Open i2c bus
**	#bus_name	:	i2c bus name such as: /dev/i2c-1
**	@return		:	failed return -1, success return i2c bus fd
*/
int i2c_open(const char *bus_name)
{int fd;/* Open i2c-bus devcice */if ((fd = open(bus_name, O_RDWR)) == -1) {return -1;}return fd;
}void i2c_close(int bus)
{close(bus);
}/*
**	@brief		:	Initialize I2CDevice with defualt value
**	#device	    :	I2CDevice struct
*/
void i2c_init_device(I2CDevice *device)
{/* 7 bit device address */device->tenbit = 0;/* 1ms delay */device->delay = 1;/* 8 bytes per page */device->page_bytes = 8;/* 1 byte internal(word) address */device->iaddr_bytes = 1;
}/*
**	@brief		:	Get I2CDevice struct desc
**	#device	    :	I2CDevice struct
**  #buf        :   Description message buffer
**  #size       :   #buf size
**	@return		:	return i2c device desc
*/
char *i2c_get_device_desc(const I2CDevice *device, char *buf, size_t size)
{memset(buf, 0, size);snprintf(buf, size, "Device address: 0x%x, tenbit: %s, internal(word) address: %d bytes, page max %d bytes, delay: %dms",device->addr, device->tenbit ? "True" : "False", device->iaddr_bytes, device->page_bytes, device->delay);return buf;
}/*
**	i2c_ioctl_read/write
**	I2C bus top layer interface to operation different i2c devide
**	This function will call XXX:ioctl system call and will be related
**	i2c-dev.c i2cdev_ioctl to operation i2c device.
**	1. it can choice ignore or not ignore i2c bus ack signal (flags set I2C_M_IGNORE_NAK)
**	2. it can choice ignore or not ignore i2c internal address
**
*/
ssize_t i2c_ioctl_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len)
{struct i2c_msg ioctl_msg[2];struct i2c_rdwr_ioctl_data ioctl_data;unsigned char addr[INT_ADDR_MAX_BYTES];unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);memset(addr, 0, sizeof(addr));memset(ioctl_msg, 0, sizeof(ioctl_msg));memset(&ioctl_data, 0, sizeof(ioctl_data));/* Target have internal address */if (device->iaddr_bytes) {i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);
//		for(int i =0 ;i<INT_ADDR_MAX_BYTES;i++)
//		{
//			printf("fsq_debug:%s-%d:addr[%d]:%d\n",__func__,__LINE__,i,addr[i]);
//		}/* First message is write internal address */ioctl_msg[0].len	=	device->iaddr_bytes;ioctl_msg[0].addr	= 	device->addr;ioctl_msg[0].buf	= 	addr;ioctl_msg[0].flags	= 	flags;/* Second message is read data */ioctl_msg[1].len	= 	len;ioctl_msg[1].addr	= 	device->addr;ioctl_msg[1].buf	=	buf;ioctl_msg[1].flags	=	flags | I2C_M_RD;/* Package to i2c message to operation i2c device */ioctl_data.nmsgs	=	2;ioctl_data.msgs		=	ioctl_msg;}/* Target did not have internal address */else {/* Direct send read data message */ioctl_msg[0].len	= 	len;ioctl_msg[0].addr	= 	device->addr;ioctl_msg[0].buf	=	buf;ioctl_msg[0].flags	=	flags | I2C_M_RD;/* Package to i2c message to operation i2c device */ioctl_data.nmsgs	=	1;ioctl_data.msgs		=	ioctl_msg;}/* Using ioctl interface operation i2c device */if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1) {perror("Ioctl read i2c error:");return -1;}return len;
}ssize_t i2c_ioctl_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
{ssize_t remain = len;size_t size = 0, cnt = 0;const unsigned char *buffer = buf;unsigned char delay = GET_I2C_DELAY(device->delay);unsigned short flags = GET_I2C_FLAGS(device->tenbit, device->flags);struct i2c_msg ioctl_msg;struct i2c_rdwr_ioctl_data ioctl_data;unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];while (remain > 0) {size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);/* Convert i2c internal address */memset(tmp_buf, 0, sizeof(tmp_buf));i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);/* Connect write data after device internal address */memcpy(tmp_buf + device->iaddr_bytes, buffer, size);/* Fill kernel ioctl i2c_msg */memset(&ioctl_msg, 0, sizeof(ioctl_msg));memset(&ioctl_data, 0, sizeof(ioctl_data));ioctl_msg.len	=	device->iaddr_bytes + size;ioctl_msg.addr	=	device->addr;ioctl_msg.buf	=	tmp_buf;ioctl_msg.flags	=	flags;ioctl_data.nmsgs =	1;ioctl_data.msgs	=	&ioctl_msg;if (ioctl(device->bus, I2C_RDWR, (unsigned long)&ioctl_data) == -1) {perror("Ioctl write i2c error:");return -1;}/* XXX: Must have a little time delay */i2c_delay(delay);cnt += size;iaddr += size;buffer += size;remain -= size;}return cnt;
}/*
**	@brief	:	read #len bytes data from #device #iaddr to #buf
**	#device	:	I2CDevice struct, must call i2c_device_init first
**	#iaddr	:	i2c_device internal address will read data from this address, no address set zero
**	#buf	:	i2c data will read to here
**	#len	:	how many data to read, lenght must less than or equal to buf size
**	@return : 	success return read data length, failed -1
*/
ssize_t i2c_read(const I2CDevice *device, unsigned int iaddr, void *buf, size_t len)
{ssize_t cnt;unsigned char addr[INT_ADDR_MAX_BYTES];unsigned char delay = GET_I2C_DELAY(device->delay);/* Set i2c slave address */if (i2c_select(device->bus, device->addr, device->tenbit) == -1) {return -1;}/* Convert i2c internal address */memset(addr, 0, sizeof(addr));i2c_iaddr_convert(iaddr, device->iaddr_bytes, addr);/* Write internal address to devide  */if (write(device->bus, addr, device->iaddr_bytes) != device->iaddr_bytes) {perror("Write i2c internal address error");return -1;}/* Wait a while */i2c_delay(delay);/* Read count bytes data from int_addr specify address */if ((cnt = read(device->bus, buf, len)) == -1) {perror("Read i2c data error");return -1;}return cnt;
}/*
**	@brief	:	write #buf data to i2c #device #iaddr address
**	#device	:	I2CDevice struct, must call i2c_device_init first
**	#iaddr	: 	i2c_device internal address, no address set zero
**	#buf	:	data will write to i2c device
**	#len	:	buf data length without '/0'
**	@return	: 	success return write data length, failed -1
*/
ssize_t i2c_write(const I2CDevice *device, unsigned int iaddr, const void *buf, size_t len)
{ssize_t remain = len;ssize_t ret;size_t cnt = 0, size = 0;const unsigned char *buffer = buf;unsigned char delay = GET_I2C_DELAY(device->delay);unsigned char tmp_buf[PAGE_MAX_BYTES + INT_ADDR_MAX_BYTES];/* Set i2c slave address */if (i2c_select(device->bus, device->addr, device->tenbit) == -1) {return -1;}/* Once only can write less than 4 byte */while (remain > 0) {size = GET_WRITE_SIZE(iaddr % device->page_bytes, remain, device->page_bytes);/* Convert i2c internal address */memset(tmp_buf, 0, sizeof(tmp_buf));i2c_iaddr_convert(iaddr, device->iaddr_bytes, tmp_buf);/* Copy data to tmp_buf */memcpy(tmp_buf + device->iaddr_bytes, buffer, size);/* Write to buf content to i2c device length  is address length andwrite buffer length */ret = write(device->bus, tmp_buf, device->iaddr_bytes + size);if (ret == -1 || (size_t)ret != device->iaddr_bytes + size){perror("I2C write error:");return -1;}/* XXX: Must have a little time delay */i2c_delay(delay);/* Move to next #size bytes */cnt += size;iaddr += size;buffer += size;remain -= size;}return cnt;
}/*
**	@brief	:	i2c internal address convert
**	#iaddr	:	i2c device internal address
**	#len	:	i2c device internal address length
**	#addr	:	save convert address
*/
void i2c_iaddr_convert(unsigned int iaddr, unsigned int len, unsigned char *addr)
{union {unsigned int iaddr;unsigned char caddr[INT_ADDR_MAX_BYTES];} convert;/* I2C internal address order is big-endian, same with network order */convert.iaddr = htonl(iaddr);//printf("fsq_debug:%s-%d:convert_iaddr:%x iaddr:%x\n",__func__,__LINE__,convert.iaddr,iaddr);/* Copy address to addr buffer */int i = len - 1;int j = INT_ADDR_MAX_BYTES - 1;while (i >= 0 && j >= 0) {addr[i--] = convert.caddr[j--];}
}/*
**	@brief		:	Select i2c address @i2c bus
**	#bus		:	i2c bus fd
**	#dev_addr	:	i2c device address
**	#tenbit		:	i2c device address is tenbit
**	#return		:	success return 0, failed return -1
*/
int i2c_select(int bus, unsigned long dev_addr, unsigned long tenbit)
{/* Set i2c device address bit */if (ioctl(bus, I2C_TENBIT, tenbit)) {perror("Set I2C_TENBIT failed");return -1;}/* Set i2c device as slave ans set it address */if (ioctl(bus, I2C_SLAVE, dev_addr)) {perror("Set i2c device address failed");return -1;}return 0;
}/*
**	@brief	:	i2c delay
**	#msec	:	milliscond to be delay
*/
static void i2c_delay(unsigned char msec)
{usleep(msec * 1e3);
}

那么怎么使用呢?

    int fd;I2CDevice device;
//    const char *data = "9876543";ssize_t ret;
unsigned char data[2];unsigned char buf[2];/* First open i2c bus */if ((fd = i2c_open("/dev/i2c-0")) == -1) {perror("Open i2c bus error");return -1;}/* Fill i2c device struct */device.bus = fd;device.addr = 0x30;device.tenbit = 0;device.delay = 10;device.flags = 0;device.page_bytes = 8;device.iaddr_bytes = 2; /* Set this to zero, and using i2c_ioctl_xxxx API will ignore chip internal address *//* Read */usleep(100000);memset(buf, 0, 2);ret = i2c_ioctl_read(&device, 0x3107, buf, 1);if (ret == -1){fprintf(stderr, "Read i2c error!\n");}printf("i2c_read_handle 0x3107 ret = %d\n", ret);printf("i2c_read_handle buf[0] = %#x\n", buf[0]);memset(buf, 0, 2);ret = i2c_ioctl_read(&device, 0x3108, buf, 1);if (ret == -1){fprintf(stderr, "Read i2c error!\n");}printf("i2c_read_handle 0x3108 ret = %d\n", ret);printf("i2c_read_handle buf[0] = %#x\n", buf[0]);

这个例子是通过接口获取一个sensor的ID。跟通过 i2ctransfer工具来读ID的功效是一样的,如下:

电机控制参考原厂提供的初始驱动,瑞萌的32008N,其实关键是把IIC接口用起来,电机具体驱动不是我们讨论的重点。

static int i2c_fd = -1;
static int motor_iccurt_open_flag = 0; //bit0:motor;bit1: ircut;int open_motor_i2c_device(void){if ((i2c_fd = i2c_open("/dev/i2c-0")) == -1) {printf("Open i2c bus error\n");return -1;}printf("Open i2c bus success!\n");return 0;
}int MS32008_open_motor_device(void)
{if(-1 == i2c_fd){i2c_fd = i2c_open("/dev/i2c-0");if(-1 == i2c_fd){printf("Open i2c bus error\n");return -1;}}motor_iccurt_open_flag |= (1 << 0);printf("Open i2c bus success!\n");return 0;
}int MS32008_close_motor_device(void)
{if(1 == (motor_iccurt_open_flag & (1 << 0))){motor_iccurt_open_flag &= ~(1 << 0);}if(0 == motor_iccurt_open_flag){if(-1 != i2c_fd){i2c_close(i2c_fd);}}return 0;
}

再做初始化电机:

void Init_MS32008(void)
{if(initedFlag)return;open_motor_i2c_device();uint8_t u8Reg[1];u16CHApps = 500;u16CHBpps = 500;u16CHApulse = u16CHApps / 10;u16CHBpulse = u16CHBpps / 10;u8CHAcurrent = 130;u8CHBcurrent = 130;MS32008_SoftReset();											//软件复位
//	u8Reg[0] = cmd_nRST_DISABLE | standby_DISABLE | stm_fsw_MUL2|useExtClk_ENABLE|oscOFF_DISABLE;u8Reg[0] = cmd_nRST_DISABLE | standby_DISABLE | stm_fsw_MUL2|oscOFF_DISABLE;
#if (COMM_MODE == IIC_MODE)IIC_WriteReg(0x00,1,&u8Reg[0]);
#endifMS32008_DC_CTRL(DCMotor_Hiz);MS32008_EventClr(uvloClr|otsClr);u8Reg[0] = CHx_PowDri_ENABLE|CHx_recordRev_DISABLE | 0x90;u8Reg[1] = CHx_msModeFull | CHx_ForceStopPosDiv2|CHx_Dir_CW;
#if (COMM_MODE == IIC_MODE)IIC_WriteReg(0x10,2,&u8Reg[0]);					//从地址10H开始写入2个数据 chaIIC_WriteReg(0x20,2,&u8Reg[0]);					//从地址20H开始写入2个数据	chb//printf("write reg 0x10/0x20 u8Reg[0] = %d\n", u8Reg[0]);
#endifMS32008_SetCHxPPS(CHA,u16CHApps);MS32008_SetCHxPPS(CHB,u16CHBpps);
//	MS32008_SetCHxStepNum(CHA,4000);
//	MS32008_SetCHxStepNum(CHB,4000);MS32008_SetCHxCurrent(CHA,u8CHAcurrent);MS32008_SetCHxCurrent(CHB,u8CHBcurrent);MS32008_CHxConfLoad(ACH_confLoad|BCH_confLoad);initedFlag = 1;}

 

Key_Function(uint32_t u32KeyValue)是电机小功能的测试代码;

在测试case里,这样处理:

	int functionNum = -1;int runTimes = 1;Init_MS32008();while(functionNum != 100){Key_Function(99);printf("1://步进电机电流增(无此功能)、DC_Motor正转\n");printf("2://步进电机电流减(无此功能)、DC_Motor反转\n");printf("3://步进电机转速增、DC_Motor刹车\n");printf("4://步进电机转速减、DC_Motor高阻\n");printf("7://通道使能/禁止\n");printf("8: //换向\n");printf("9://停机/启动\n");printf(">10  //通道选择11: A, 12:B, 13:E, 14:Z转\n");printf("请输入一个功能号整数: ");scanf("%d", &functionNum);printf("你输入的功能号整数是: %d\n", functionNum);printf("请输入一个运行次数整数: ");scanf("%d", &runTimes);printf("你输入的运行次数是: %d\n", runTimes);if(functionNum < 100){for(int i=0; i<runTimes;i++){Key_Function(functionNum);usleep(500*1000);}}}

demo运行情况:

刚开始调的时候,IIC不通,仔细看了一下原理图,数据和时钟都接反了,这客户硬件设计水平真是差劲,大家引以为戒,前面看起来还正常,不一条一条线看还看不大出来.哎,硬件挖大坑, 软件来填坑.最终通过跳线来完成调试.

http://www.dtcms.com/a/282766.html

相关文章:

  • 7.16 Java基础 | 集合框架(上)
  • 微服务架构中实现跨服务的字段级权限统一控制
  • PyTorch深度学习框架入门案例实战
  • 第一章 【vue】基础(超详细)
  • 【动归解题套路框架】【带备忘录的递归】【最优子结构】【自下而上DP table】
  • Spring核心注解@RequestMapping详解
  • Java 二维数组详解:从基础语法到实战应用,彻底掌握多维数据结构
  • 边缘计算革命:AWS Snowcone在智慧工厂的落地实践(2025工业4.0实战指南)
  • 笔试——Day10
  • 【AI交叉】天文学:人工智能如何赋能星辰大海的探索
  • 如何关闭Elasticsearch的安全认证的解决方法
  • Maven入门指南:生命周期、阶段和执行顺序详解
  • 基于深度学习的情感分析模型:从文本数据到模型部署
  • leetcode:990.等式方程的可满足性[图]
  • 推荐《Python 编程:从入门到实践》之Python编程的基础知识
  • 经典算法之基数排序
  • 算法精讲--正则表达式(二):分组、引用与高级匹配技术
  • 基站前传卡 加速卡 EU
  • 一个项目的完整一生 --- 一 窗口大小设置
  • NW956NW961美光固态闪存NW964NW968
  • 如何建立一個單一產品的 Shopify 商店
  • 倪海厦全套下载,八纲辨证,人纪,天纪,针灸,电子版
  • lesson15:Python的文件操作
  • Java-数构栈与队列
  • 第三次mysql作业
  • C# 8.0 创建一个简单的控制台应用程序
  • Python 进阶学习之全栈开发学习路线
  • 电力名词通俗解析5:计量系统
  • 电力名词通俗解析4:电网DCS与SCADA系统通俗解释
  • adb性能测试命令