博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【龙印】龙芯1c上双路16位AD芯片TM7705的linux驱动
阅读量:2360 次
发布时间:2019-05-10

本文共 11585 字,大约阅读时间需要 38 分钟。

本文为在用龙芯1c做3D打印机过程中的笔记。龙芯1c做的3d打印机简称“龙印”,git地址“https://gitee.com/caogos/marlin_ls1c”

TM7705和热敏电阻一起实现3d打印机的温度测量。本文重点放在tm7705的linux驱动上,关于温度测量后面另外写一篇详细介绍。

硬件电路

测试用的硬件为“安富莱”推出的TM7705模块。这里着重强调一下TM7705是深圳天微电子的AD芯片,很多淘宝商家把名字都写错了。

为了使原理图更加清晰,把整个原理图分成了几块分别截图了。下面谈点个人对本模块的理解,仅供参考。

1,本模块的模拟输入端AIN1+,AIN1-和AIN2+,AIN2-分别通过两个10k电阻并联分压。这样的目的是为了把输入电压范围从0-2.5v扩大到0-5v,即满量程电压为5v。

因为使用的是2.5v的基准电源,所以实际上AIN引脚的输入范围为0-2.5v,通过两个10k电阻分压,而AIN1+和AIN2+则接到两个10k电阻中间,这样就实现了把测量范围从0-2.5v扩大到0-5v。凡事有两面性,正是由于引入了两个10k电阻,同时也引入了误差。

2,这个模块使用的参考电源是2.5v的,tm7705允许参考电源电压为0到vdd之间的任意值。

对于使用在3d打印机上来说,最后改为3.3v或者5v的参考电源(如果有的话)。marlin中有个脚本createTemperatureLookup.py可以生成一个AD值与温度对应的表格,用于程序中快速计算温度值。

假如待测试的电压很小,比如是毫伏级的,那么可以选择电源低一些的基准电源,这样有利于提高精度,同时如果需要,可以把增益设置大一点,tm7705最大可以支持128的增益。

3,tm7705是支持3.3v和5v供电的,具体选择哪个根据spi sclk的电平决定。如果SPI的SCLK线上的高电平为3.3v,那么推荐使用3.3v的电源给tm7705供电。比如龙芯1c的spi的SCLK是3.3v的,如果tm7705的vdd接5v,那么出现接口迷失的概率大大增加。

4,复位(reset)引脚。我在这里犯了低级错误,拿到电路图后,发现图上说已经上拉到vcc了,并且R13没贴。后面在接线时没接复位脚,悬空的;移植tm7705驱动时,没有操作复位脚。后来发现tm7705采集始终不是很稳定,每次上电后可以成功采集几次,过后就超时,或者一直采集到的值为0xfff,并且把龙芯1c热复位还不一定能解决。用示波器检查了,没发现问题,代码也检查了,没发现问题。经过两周的煎熬后,突然想起来把R13焊上,用软件复位tm7705试试。结果发现一切OK,并且后续如果出错,通过复位tm7705也都能解决,今天我从上午不断电连续测到现在都没问题。即使超时了,代码中也通过复位tm7705很快就解决了。

所以这里强烈建议:把tm7705复位脚接龙芯1c的一个gpio,每次上电后初始化时必须手动复位tm7705,如果出现超时或其它错误时,也可以通过复位tm7705来解决。

tm7705模块使用龙芯1c的SPI0_CS1

DRDY    --------    I2S_DI/GPIO87
RESET     --------    I2S_LRCK/GPIO89
VDD      --------     3.3V

源码

在文件arch\mips\loongson\ls1x\ls1c\platform.c中,

找到“static struct spi_board_info ls1x_spi0_devices[]”,在里面添加

#ifdef CONFIG_SPI_TM7705    {        .modalias       = "TM7705",        .bus_num        = 0,        .chip_select    = SPI0_CS1,        // 当前cpu频率=252.00Mhz BUS=126.00Mhz        // SPI最大的分频系数为4096        // 所以spi最小频率=126Mhz/4096=30.7khz        // 这里只是设置最大频率,实际频率为最接近并且小于最大频率的BUS分频后的值        // 经过测试,最大频率设为为1mhz也能正常采集        .max_speed_hz   = 500*1000,//        .platform_data  =         .mode           = SPI_MODE_3,    },#endif

tm7705.c

/* * * 双路16位spi接口AD芯片--TM7705的驱动 */ #include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// 通信寄存器bit定义enum { // 寄存器选择 RS2 RS1 RS0 TM7705_REG_COMM = (0 << 4), // 通信寄存器 TM7705_REG_SETUP = (1 << 4), // 设置寄存器 TM7705_REG_CLOCK = (2 << 4), // 时钟寄存器 TM7705_REG_DATA = (3 << 4), // 数据寄存器 TM7705_REG_TEST = (4 << 4), // 测试寄存器 TM7705_REG_OFFSET = (6 << 4), // 偏移寄存器 TM7705_REG_GAIN = (7 << 4), // 增益寄存器 // 读写操作 TM7705_WRITE = (0 << 3), // 写操作 TM7705_READ = (1 << 3), // 读操作 // 通道 TM7705_CH_1 = 0, // AIN1+ AIN1- TM7705_CH_2 = 1, // AIN2+ AIN2- TM7705_CH_3 = 2, // AIN1- AIN1- TM7705_CH_4 = 3 // AIN1- AIN2-};/* 设置寄存器bit定义 */enum{ TM7705_MD_NORMAL = (0 << 6), /* 正常模式 */ TM7705_MD_CAL_SELF = (1 << 6), /* 自校准模式 */ TM7705_MD_CAL_ZERO = (2 << 6), /* 校准0刻度模式 */ TM7705_MD_CAL_FULL = (3 << 6), /* 校准满刻度模式 */ TM7705_GAIN_1 = (0 << 3), /* 增益 */ TM7705_GAIN_2 = (1 << 3), /* 增益 */ TM7705_GAIN_4 = (2 << 3), /* 增益 */ TM7705_GAIN_8 = (3 << 3), /* 增益 */ TM7705_GAIN_16 = (4 << 3), /* 增益 */ TM7705_GAIN_32 = (5 << 3), /* 增益 */ TM7705_GAIN_64 = (6 << 3), /* 增益 */ TM7705_GAIN_128 = (7 << 3), /* 增益 */ /* 无论双极性还是单极性都不改变任何输入信号的状态,它只改变输出数据的代码和转换函数上的校准点 */ TM7705_BIPOLAR = (0 << 2), /* 双极性输入 */ TM7705_UNIPOLAR = (1 << 2), /* 单极性输入 */ TM7705_BUF_NO = (0 << 1), /* 输入无缓冲(内部缓冲器不启用) */ TM7705_BUF_EN = (1 << 1), /* 输入有缓冲 (启用内部缓冲器) */ TM7705_FSYNC_0 = 0, // 模拟调制器和滤波器正常处理数据 TM7705_FSYNC_1 = 1 // 模拟调制器和滤波器不启用};/* 时钟寄存器bit定义 */enum{ TM7705_CLKDIS_0 = (0 << 4), /* 时钟输出使能 (当外接晶振时,必须使能才能振荡) */ TM7705_CLKDIS_1 = (1 << 4), /* 时钟禁止 (当外部提供时钟时,设置该位可以禁止MCK_OUT引脚输出时钟以省电 */ TM7705_CLKDIV_0 = (0 << 3), // 不分频 TM7705_CLKDIV_1 = (1 << 3), // 2分频,外部晶振为4.9152Mhz时,应2分频 TM7705_CLK_0 = (0 << 2), // 主时钟=1Mhz并且CLKDIV=0,主时钟=2Mhz并且CLKDIV=1 TM7705_CLK_1 = (1 << 2), // 主时钟=2.4576Mhz并且CLKDIV=0, 主时钟=4.9152Mhz并且CLKDIV=1 // 注意输出更新率与clk位有关 // 当TM7705_CLK_0时,输出更新率只能为20,25,100,200 TM7705_UPDATE_20 = (0), TM7705_UPDATE_25 = (1), TM7705_UPDATE_100 = (2), TM7705_UPDATE_200 = (3), // 当TM7705_CLK_1时,输出更新率只能为50,60,250,500 TM7705_UPDATE_50 = (0), TM7705_UPDATE_60 = (1), TM7705_UPDATE_250 = (2), TM7705_UPDATE_500 = (3)};#define TM7705_CHANNEL_NUM (2) // tm7705通道个数#define TM7705_DRDY_PIN (87) // GPIO87/I2S_DI tm7705的引脚DRDY #define TM7705_RESET_PIN (89) // GPIO89/I2S_LRCK tm7705的引脚RESETstruct tm7705 { struct device *hwmon_dev; struct mutex lock;};// 通过reset脚复位tm7705static void tm7705_reset(void){ gpio_direction_output(TM7705_RESET_PIN, 1); msleep(1); gpio_direction_output(TM7705_RESET_PIN, 0); msleep(2); gpio_direction_output(TM7705_RESET_PIN, 1); msleep(1); return ;}// 同步spi接口时序static void tm7705_sync_spi(struct spi_device *spi){ u8 tx_buf[4] = {0xFF}; // 至少32个串行时钟内向TM7705的DIN线写入逻辑"1" spi_write(spi, tx_buf, sizeof(tx_buf)); return ;}// 等待内部操作完成static int tm7705_wait_DRDY(void){ int i = 0; int time_cnt = 500*1000; for (i=0; i
= time_cnt) { return -1; } return 0;}// 自校准static void tm7705_calib_self(struct spi_device *spi, u8 channel){ u8 tx_buf[2] = {0}; tx_buf[0] = TM7705_REG_SETUP | TM7705_WRITE | channel; tx_buf[1] = TM7705_MD_CAL_SELF | TM7705_GAIN_1 | TM7705_UNIPOLAR | TM7705_BUF_EN | TM7705_FSYNC_0; spi_write(spi, tx_buf, sizeof(tx_buf)); tm7705_wait_DRDY(); /* 等待内部操作完成 --- 时间较长,约180ms */ msleep(50); return ;}// 配置tm7705的指定通道static void tm7705_config_channel(struct spi_device *spi, u8 channel){ u8 tx_buf[2] = {0}; tx_buf[0] = TM7705_REG_CLOCK | TM7705_WRITE | channel; tx_buf[1] = TM7705_CLKDIS_0 | TM7705_CLKDIV_1 | TM7705_CLK_1 | TM7705_UPDATE_50; spi_write(spi, tx_buf, sizeof(tx_buf)); // 自校准 tm7705_calib_self(spi, channel); return ;}// 复位tm7705并重新配置static void tm7705_reset_and_reconfig(struct spi_device *spi){ // 通过reset脚复位tm7705 tm7705_reset(); // 同步spi接口时序 msleep(5); tm7705_sync_spi(spi); msleep(5); // 配置tm7705时钟寄存器 tm7705_config_channel(spi, TM7705_CH_1);// tm7705_config_channel(spi, TM7705_CH_2); return ;}/* * 读取一个通道的值 * @dev 设备描述符 * @channel 通道 * ad 读到的ad值 */static int tm7705_read_channel(struct device *dev, u8 channel, u16 *ad){ struct spi_device *spi = to_spi_device(dev); struct tm7705 *adc = spi_get_drvdata(spi); int ret = 0; u16 value = 9; u8 tx_buf[1] = {0}; u8 rx_buf[2] = {0}; if (mutex_lock_interruptible(&adc->lock)) { return -ERESTARTSYS; } // 等待转换完成 ret = tm7705_wait_DRDY(); if(ret) { printk(KERN_ERR "[%s] tm7705_wait_DRDY() time out.\n", __FUNCTION__); goto fail; } tx_buf[0] = TM7705_REG_DATA | TM7705_READ | channel; ret = spi_write_then_read(spi, tx_buf, sizeof(tx_buf), rx_buf, sizeof(rx_buf)); value = (rx_buf[0]<<8) + rx_buf[1]; if (0 > ret) // spi通信失败 { printk(KERN_ERR "[%s] tm7705_read_byte() fail. ret=%d\n", __FUNCTION__, ret); goto fail; } if (0xfff == value) // tm7705上电一段时间后,可能会出现读到的值一直是0xfff的情况 { printk(KERN_ERR "[%s] value=0xfff\n", __FUNCTION__); ret = -1; goto fail; } // 输出AD值 *ad = value;fail: mutex_unlock(&adc->lock); return ret;}/* sysfs hook function */static ssize_t tm7705_get_sensor_value(struct device *dev, struct device_attribute *devattr, char *buf){ struct spi_device *spi = to_spi_device(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int ret = 0; u16 ad = 0; int i = 0; /* * 为了避免通道切换造成读数失效,读2次 * 实际上每次读到的是上一次采集的结果(可以两个通道交替采集就能看到效果) */ for (i=0; i<2; i++) { ret = tm7705_read_channel(dev, attr->index, &ad); if (ret) { // 失败,则重启tm7705并重新配置 tm7705_reset_and_reconfig(spi); printk(KERN_ERR "[%s] tm7705 reset and reconfig.\n", __FUNCTION__); return ret; } printk(KERN_DEBUG "[%s] tm7705 ad=0x%x\n", __FUNCTION__, ad); // ls1c的速度相当TM7705太快,延时一下避免在一次读完后DRDY还未及时改变状态ls1c又开始了下一次读写 msleep(1); } // 将ad值传递给用户程序 ret = sprintf(buf, "%u\n", ad); return ret;}static struct sensor_device_attribute ad_input[] = { SENSOR_ATTR(ch1, S_IRUGO, tm7705_get_sensor_value, NULL, TM7705_CH_1), SENSOR_ATTR(ch2, S_IRUGO, tm7705_get_sensor_value, NULL, TM7705_CH_2),};static int __devinit tm7705_probe(struct spi_device *spi){ struct tm7705 *adc; int i; int status; adc = kzalloc(sizeof *adc, GFP_KERNEL); if (!adc) { return -ENOMEM; } mutex_init(&adc->lock); mutex_lock(&adc->lock); spi_set_drvdata(spi, adc); for (i=0; i
dev, &ad_input[i].dev_attr); if (status) { dev_err(&spi->dev, "device_create_file() failed.\n"); goto fail_crete_file; } } adc->hwmon_dev = hwmon_device_register(&spi->dev); if (IS_ERR(adc->hwmon_dev)) { dev_err(&spi->dev, "hwmon_device_register() fail.\n"); status = PTR_ERR(adc->hwmon_dev); goto fail_crete_file; } // gpio初始化 status = gpio_request(TM7705_DRDY_PIN, "TM7705"); // tm7705 DRDY pin if (status) { dev_err(&spi->dev, "gpio_request(TM7705_DRDY_PIN) fail.\n"); goto fail_device_register; } gpio_direction_input(TM7705_DRDY_PIN); status = gpio_request(TM7705_RESET_PIN, "TM7705"); // tm7705 reset pin if (status) { dev_err(&spi->dev, "gpio_request(TM7705_RESET_PIN) fail.\n"); goto fail_request_drdy_pin; } gpio_direction_output(TM7705_RESET_PIN, 1); // 复位tm7705并重新配置 tm7705_reset_and_reconfig(spi); mutex_unlock(&adc->lock); return 0;fail_request_drdy_pin: gpio_free(TM7705_DRDY_PIN);fail_device_register: hwmon_device_unregister(adc->hwmon_dev);fail_crete_file: for (i--; i>=0; i--) { device_remove_file(&spi->dev, &ad_input[i].dev_attr); } spi_set_drvdata(spi, NULL); mutex_unlock(&adc->lock); kfree(adc); return status;}static int __devexit tm7705_remove(struct spi_device *spi){ struct tm7705 *adc = spi_get_drvdata(spi); int i; mutex_lock(&adc->lock); gpio_free(TM7705_DRDY_PIN); gpio_free(TM7705_DRDY_PIN); hwmon_device_unregister(adc->hwmon_dev); for (i=0; i
dev, &ad_input[i].dev_attr); } spi_set_drvdata(spi, NULL); mutex_unlock(&adc->lock); kfree(adc); return 0;}static struct spi_driver tm7705_driver = { .driver = { .name = "TM7705", .owner = THIS_MODULE, }, .probe = tm7705_probe, .remove = __devexit_p(tm7705_remove),};static int __init init_tm7705(void){ return spi_register_driver(&tm7705_driver);}static void __exit exit_tm7705(void){ spi_unregister_driver(&tm7705_driver);}module_init(init_tm7705);module_exit(exit_tm7705);MODULE_AUTHOR("勤为本 1207280597@qq.com");MODULE_DESCRIPTION("TM7705 linux driver");MODULE_LICENSE("GPL");

在“drivers\hwmon\Kconfig”中,增加
config SPI_TM7705
    tristate "Titan Micro Electronics TM7705"
    depends on SPI_MASTER && EXPERIMENTAL
    help
      say yes here to build support for Titan Micro Electronics TM7705
      analog to digital converter.
      
在“drivers\hwmon\Makefile”中,增加
obj-$(CONFIG_SPI_TM7705)     += tm7705.o
make menuconfig
Device Drivers  --->
  [*] SPI support  --->
    <*>   Loongson1 SPI
      ls1x spi cs mode (softcs mode)  --->
        (X) softcs mode
      ls1x spi control mode (poll mode)  --->
        (X) poll mode
  <*> Hardware Monitoring support  --->
    <*>   Titan Micro Electronics TM7705
    
去掉声卡的配置,tm7705使用了GPIO89/I2S_LRCK和GPIO87/I2S_DI    
Device Drivers  --->
  < > Sound card support  --->
 
手动测试可以使用命令
cat /sys/bus/spi/drivers/TM7705/spi0.1/ch1

也可以用下面的程序来不间断自动测试测试

#include 
#include
#include
#include
#include
#include
#include
#include
#include
void read_channel(char *dev_file_path){ int fd = 0; int ret = 0; unsigned char buff[128] = {0}; fd = open(dev_file_path, O_RDONLY); if (-1 == fd) { printf("[%s] open device file fail.\n", __FUNCTION__); return ; } memset(buff, 0, 128); ret = read(fd, buff, 128); if (0 > ret) { printf("[%s] not read data. ret=%d\n", __FUNCTION__, ret); } printf("[%s] buff=%s\n", __FUNCTION__, buff); close(fd); return ;}int main(void){ char ch1_path[] = {"/sys/bus/spi/drivers/TM7705/spi0.1/ch1"}; char ch2_path[] = {"/sys/bus/spi/drivers/TM7705/spi0.1/ch2"}; while (1) { read_channel(ch1_path); usleep(100*1000);/* read_channel(ch2_path); usleep(300*1000);*/ }}

你可能感兴趣的文章
Android模拟器无法上网问题
查看>>
抱歉,进程android.process.media,已停止运行的解决办法
查看>>
第一个helloword小例
查看>>
oracle新建方案
查看>>
android的坑
查看>>
java.net.SocketException:socket failed:EACCES (Permission denied)
查看>>
android.os.NetworkOnMainThreadException
查看>>
Android:单元测试Junit的配置
查看>>
国际产业
查看>>
制造业
查看>>
混沌的有关概念——1
查看>>
混沌的有关概念——2
查看>>
混沌理论的简要观点
查看>>
知识的经济学分析:一个文献综述——基于范式演进的视点
查看>>
reading the path of picture
查看>>
Could a 32bit software install on a 64bit-system computer
查看>>
No reship system to recovery disk AHCI diriven and just do it
查看>>
debug compare with release
查看>>
QT5.7 deploys opencv2.4.13(the version of opencv is not newer than QT)
查看>>
the principle of laplacian filter
查看>>