linux驱动
前言
qmc5883芯片用于磁力计指南针。此代码在实际项目中测试过,功能正常可以放心使用。
一、qmc5883源码
qmc5883.h
#ifndef QMC5883L_H
#define QMC5883L_H
#include <stdint.h>void qmc5883_init();
void qmc5883_reset();
int qmc5883_ready();
void qmc5883_reconfig();
int qmc5883_readHeading();
int qmc5883_readRaw( int16_t *x, int16_t *y, int16_t *z, int16_t *t );
void qmc5883_resetCalibration();
void qmc5883_setSamplingRate( int rate );
void qmc5883_setRange( int range );
void qmc5883_setOversampling( int ovl );
struct QMC5883L {
int16_t xhigh, xlow;
int16_t yhigh, ylow;
uint8_t addr;
uint8_t mode;
uint8_t rate;
uint8_t range;
uint8_t oversampling;
};#endif
qmc5883.c
#include <stdio.h>
#include <stdarg.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
//#include "hal_gpio.h"
#include "i2c.h"
#include <math.h>
#include "qmc5883.h"/* The default I2C address of this chip */
#define QMC5883L_ADDR (0x0D<<1)/* Register numbers */
#define QMC5883L_X_LSB 0
#define QMC5883L_X_MSB 1
#define QMC5883L_Y_LSB 2
#define QMC5883L_Y_MSB 3
#define QMC5883L_Z_LSB 4
#define QMC5883L_Z_MSB 5
#define QMC5883L_STATUS 6
#define QMC5883L_TEMP_LSB 7
#define QMC5883L_TEMP_MSB 8
#define QMC5883L_CONFIG 9
#define QMC5883L_CONFIG2 10
#define QMC5883L_RESET 11
#define QMC5883L_RESERVED 12
#define QMC5883L_CHIP_ID 13/* Bit values for the STATUS register */
#define QMC5883L_STATUS_DRDY 1
#define QMC5883L_STATUS_OVL 2
#define QMC5883L_STATUS_DOR 4/* Oversampling values for the CONFIG register */
#define QMC5883L_CONFIG_OS512 0b00000000
#define QMC5883L_CONFIG_OS256 0b01000000
#define QMC5883L_CONFIG_OS128 0b10000000
#define QMC5883L_CONFIG_OS64 0b11000000/* Range values for the CONFIG register */
#define QMC5883L_CONFIG_2GAUSS 0b00000000
#define QMC5883L_CONFIG_8GAUSS 0b00010000/* Rate values for the CONFIG register */
#define QMC5883L_CONFIG_10HZ 0b00000000
#define QMC5883L_CONFIG_50HZ 0b00000100
#define QMC5883L_CONFIG_100HZ 0b00001000
#define QMC5883L_CONFIG_200HZ 0b00001100/* Mode values for the CONFIG register */
#define QMC5883L_CONFIG_STANDBY 0b00000000
#define QMC5883L_CONFIG_CONT 0b00000001/* Apparently M_PI isn't available in all environments. */
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif#define DEV_I2C_PATH ("/dev/i2c-1")
static unsigned int g_Fd = 0;
static struct QMC5883L qmc5883;
/**
* @brief: 打开I2C。
* @param: unsigned char I2CNum
* @param: unsigned char u32DevAddr
* @return: 成功返回GUIDEIR_OK。失败返回GUIDEIR_ERR。
* @note:
**/
int I2COpen(unsigned char I2CNum, unsigned char u32DevAddr)
{
printf("Enter I2COpen I2CNum == %d, u32DevAddr == 0x%x 7bit=0x%x\n", I2CNum, u32DevAddr,u32DevAddr>>1);
g_Fd = i2c_open(DEV_I2C_PATH, u32DevAddr>>1);
if (g_Fd < 0) {
return -1;
}return 0;
}/**
* @brief: 关闭I2C。
* @param: unsigned char I2CNum
* @return: 成功返回GUIDEIR_OK。失败返回GUIDEIR_ERR。
* @note:
**/
int I2CClose(unsigned char I2CNum)
{
printf("Enter I2CClose I2CNum == %d\n", I2CNum);
i2c_close(g_Fd);
//i2c_stop(&g_I2cDev);
return 0;
}/**
* @brief: I2C读寄存器。
* @param: unsigned char u8Addr 寄存器地址。
* @param: unsigned char * pu8Val 读出的寄存器值。
* @return: 成功返回GUIDEIR_OK。失败返回GUIDEIR_ERR。
* @note:
**/
int I2CRead(unsigned char u8Addr, unsigned char * pu8Val)
{
int rc;
rc = i2c_read(g_Fd,u8Addr,pu8Val);
if(rc<0){
return -1;
}//printf("I2CRead OK, u8Addr == 0x%x, pu8Val == 0x%x\n", u8Addr, *pu8Val);
return 0;
}/**
* @brief: I2C写寄存器。
* @param: unsigned char u8Addr 寄存器地址。
* @param: unsigned char u8Val 写入的寄存器值。
* @return: 成功返回GUIDEIR_OK。失败返回GUIDEIR_ERR。
* @note:
**/
int I2CWrite(unsigned char u8Addr, unsigned char u8Val)
{//printf("I2CWrite OK, u8Addr == 0x%x, u8Val == 0x%x\n", u8Addr, u8Val);
int rc;
//rc=i2c_write_reg(&g_I2cDev,u8Addr,u8Val);
rc = i2c_write(g_Fd,u8Addr,u8Val);
if(rc<0){
return -1;
}//printf("I2CWrite OK, u8Addr == 0x%x, u8Val == 0x%x\n", u8Addr, u8Val);
return 0;
}void qmc5883_read(uint8_t start_reg_addr, uint8_t *pdata, uint8_t size){
int i=0;
for(i=0;i<size;i++){
I2CRead(start_reg_addr,pdata);
start_reg_addr++;
pdata++;
}
}void qmc5883_write(uint8_t start_reg_addr, uint8_t *pdata, uint8_t size){
int i=0;
for(i=0;i<size;i++){
I2CWrite(start_reg_addr,*pdata);
start_reg_addr++;
pdata++;
}
}
void qmc5883_reconfig()
{
uint8_t val;
val=qmc5883.oversampling|qmc5883.range|qmc5883.rate|qmc5883.mode;
qmc5883_write(QMC5883L_CONFIG,&val,1);
}void qmc5883_reset()
{
uint8_t val;
val=1;
qmc5883_write(QMC5883L_RESET,&val,1);
qmc5883_reconfig();
}void qmc5883_setOversampling( int x )
{
switch(x) {
case 512:
qmc5883.oversampling = QMC5883L_CONFIG_OS512;
break;
case 256:
qmc5883.oversampling = QMC5883L_CONFIG_OS256;
break;
case 128:
qmc5883.oversampling = QMC5883L_CONFIG_OS128;
break;
case 64:
qmc5883.oversampling = QMC5883L_CONFIG_OS64;
break;
}
qmc5883_reconfig();
}void qmc5883_setRange( int x )
{
switch(x) {
case 2:
qmc5883.range = QMC5883L_CONFIG_2GAUSS;
break;
case 8:
qmc5883.range = QMC5883L_CONFIG_8GAUSS;
break;
}
qmc5883_reconfig();
}void qmc5883_setSamplingRate( int x )
{
switch(x) {
case 10:
qmc5883.rate = QMC5883L_CONFIG_10HZ;
break;
case 50:
qmc5883.rate = QMC5883L_CONFIG_50HZ;
break;
case 100:
qmc5883.rate = QMC5883L_CONFIG_100HZ;
break;
case 200:
qmc5883.rate = QMC5883L_CONFIG_200HZ;
break;
}
qmc5883_reconfig();
}void qmc5883_init() {
/* This assumes the wire library has been initialized. */
qmc5883.addr = QMC5883L_ADDR;
qmc5883.oversampling = QMC5883L_CONFIG_OS512;
qmc5883.range = QMC5883L_CONFIG_2GAUSS;
qmc5883.rate = QMC5883L_CONFIG_50HZ;
qmc5883.mode = QMC5883L_CONFIG_CONT;if(I2COpen(1,QMC5883L_ADDR)){
printf("QMC5883_init I2COpen failed\n");
return -1;
}
qmc5883_reset();
}int qmc5883_ready()
{
//if(!read_register(addr,QMC5883L_STATUS,1)) return 0;
//uint8_t status = Wire.read();
uint8_t status;
qmc5883_read(QMC5883L_STATUS,&status,1);
return status & QMC5883L_STATUS_DRDY;
}int qmc5883_readRaw( int16_t *x, int16_t *y, int16_t *z, int16_t *t )
{uint8_t buf[6];
int16_t raw[3];
while(!qmc5883_ready()) {
usleep(10);}qmc5883_read(QMC5883L_X_LSB,buf,6);
raw[0] = (int16_t)(((uint16_t)buf[1] << 8) | buf[0]); /* get x raw */
raw[1] = (int16_t)(((uint16_t)buf[3] << 8) | buf[2]); /* get y raw */
raw[2] = (int16_t)(((uint16_t)buf[5] << 8) | buf[4]); /* get z raw */
*x = raw[0];
*y = raw[1];
*z = raw[2];
return 1;
}void qmc5883_resetCalibration() {
qmc5883.xhigh = qmc5883.yhigh = 0;
qmc5883.xlow = qmc5883.ylow = 0;
}int qmc5883_readHeading()
{
int16_t x, y, z, t;if(!qmc5883_readRaw(&x,&y,&z,&t)) return 0;
/* Update the observed boundaries of the measurements */
if(x<qmc5883.xlow) qmc5883.xlow = x;
if(x>qmc5883.xhigh) qmc5883.xhigh = x;
if(y<qmc5883.ylow) qmc5883.ylow = y;
if(y>qmc5883.yhigh) qmc5883.yhigh = y;/* Bail out if not enough data is available. */
if( qmc5883.xlow==qmc5883.xhigh || qmc5883.ylow==qmc5883.yhigh ) return 0;/* Recenter the measurement by subtracting the average */
x -= (qmc5883.xhigh+qmc5883.xlow)/2;
y -= (qmc5883.yhigh+qmc5883.ylow)/2;/* Rescale the measurement to the range observed. */
float fx = (float)x/(qmc5883.xhigh-qmc5883.xlow);
float fy = (float)y/(qmc5883.yhigh-qmc5883.ylow);int heading = 180.0*atan2(fy,fx)/M_PI;
if(heading<=0) heading += 360;
return heading;
}
main.c 驱动测试代码测试qmc5883功能
#include <stdio.h>
#include <stdlib.h>
#include "qmc5883.h"
#include <unistd.h>
#include <getopt.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#define APP_TOOL_DIR "/tmp/apptool/"
static void usage()
{
printf("\n");
printf("Usage:\n");
printf("Examples:\n");
printf(" qmc5883 -h\n");
printf(" qmc5883 -s 1 -t 500 \n");
printf(" qmc5883 -c 1 -n 20 -t 500 \n");
printf(" qmc5883 -p 1 -t 500 \n");
printf("Options:\n");
printf(" -s single read mode \n");
printf(" -c mutiple read mode \n");
printf(" -p permanent read mode\n");
printf("\n");
exit(0);
}static void dump_to_file(const char* file_path, const void* data, int len)
{
FILE* fp = fopen(file_path, "w");
if (fp == NULL)
{
printf("open file %s error\n", file_path);
return;
}
fwrite(data, len, 1, fp);
fclose(fp);
//printf("save to file %s success\n", file_path);
}/**
* @brief main function
* @param[in] argc is arg numbers
* @param[in] **argv is the arg address
* @return status code
* - 0 success
* @note none
*/
int main(int argc, char **argv)
{
uint8_t res;
int c,i;
int conti_act=0;
int conti_num=0;
int single_act=0;
int interval_time=500;
int permanent=0;
int heading ;
char str[100];
char filename[100];
char path[100];
if(argc < 2) {
usage();
exit(0);
}
while((c = getopt(argc,argv,"s:c:n:t:p:h")) != -1){
switch(c){
case 's':
single_act = atoi(optarg);
break;
case 'c':
conti_act = atoi(optarg);
break;
case 'n':
conti_num = atoi(optarg);
break;
case 't':
interval_time = atoi(optarg);
break;
case 'p':
permanent = atoi(optarg);
break;
case 'h':
usage();
break;
default:
usage();
break;
}
}
qmc5883_init();
qmc5883_setSamplingRate(50);if(access(APP_TOOL_DIR, F_OK) != 0 ) {
printf("to create apptool dir: %s\n", APP_TOOL_DIR);
if((mkdir(APP_TOOL_DIR, 0777)) < 0)
{
printf("mkdir %s failed\n", APP_TOOL_DIR);
return -1;
}
}
while(1)
{
heading = qmc5883_readHeading();
if(heading==0) {
/* Still calibrating, so measure but don't print */
} else {
break;
}
}
sprintf(path,"%s%s",APP_TOOL_DIR,"qmc5883");
if(single_act){
heading = qmc5883_readHeading();
sprintf(str,"%d",heading);
dump_to_file(path,str,strlen(str));
//printf("heading=%d\n",heading);
}else if(conti_num){for(i=0;i<conti_num;i++){
heading = qmc5883_readHeading();
sprintf(str,"%d",heading);
dump_to_file(path,str,strlen(str));
//printf("heading=%d\n",heading);
usleep(interval_time*1000);
}}else if(permanent){
for(;;){
heading = qmc5883_readHeading();
sprintf(str,"%d",heading);
dump_to_file(path,str,strlen(str));
//printf("heading=%d\n",heading);
usleep(interval_time*1000);
}
}
return 0;
}
总结
注意事项:实际使用中,需要将装载有qmc5883芯片的设备旋转一周才能完成qmc5883的磁场校准。