在https://blog.csdn.net/mike8825/article/details/80955992中,介绍了几种红外方案,这里拿spi的方式来diy一个遥控器。
如果内核版本是v4.14,直接编译kernel4.14/drivers/media/rc/ir-spi.c文件,调试下,应该就可以用了。这里稍微改造了ir-spi.c,更加容易移植。
dts参考配置
记的打开平台的dma模式(在https://blog.csdn.net/mike8825/article/details/80955992已说明原因)
&spi1 {
status = "okay";
sprd,dma-mode = <1>;
sprd,spi-mode = <3>;
irled@0 {
compatible = "ir-spi-led";
reg = <0x0>;
spi-max-frequency = <5000000>;
duty-cycle = /bits/ 8 <60>;
};
};
如果mos管是低电平导通,加上led-active-low;
ir-spi.c
// SPDX-License-Identifier: GPL-2.0
// SPI driven IR LED device driver
//
// Copyright (c) 2016 Samsung Electronics Co., Ltd.
// Copyright (c) Andi Shyti <andi@etezian.org>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
//#include <media/rc-core.h>
#define IR_SPI_DRIVER_NAME "ir-spi"
#define IR_SPI_DEFAULT_FREQUENCY 38000
#define IR_SPI_MAX_BUFSIZE 4096*4
#define TX_IR _IOW(0xf5, 0, int)
#define SET_TX_CARRIER _IOW(0xf5, 1, int)
struct ir_spi_data {
u32 freq;
bool negated;
u16 tx_buf[IR_SPI_MAX_BUFSIZE];
u16 pulse;
u16 space;
struct spi_device *spi;
struct regulator *regulator;
};
struct ir_spi_data idata;
static int ir_spi_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t ir_spi_tx(struct file *file, const char __user * user_buf,
size_t count, loff_t * ppos)
{
int i;
int ret;
unsigned int len = 0;
int *buffer;
struct spi_transfer xfer;
pr_info("ir_spi_tx start\n");
buffer= memdup_user(user_buf, count);
count /= sizeof(int);
/* convert the pulse/space signal to raw binary signal */
for (i = 0; i < count; i++) {
unsigned int periods;
int j;
u16 val;
periods = DIV_ROUND_CLOSEST(buffer[i] * idata.freq, 1000000);
pr_debug("i=%d periods=%d\n",i,periods);
if (len>= IR_SPI_MAX_BUFSIZE){
pr_info("len=%d\n",len);
return -EINVAL;
}
/*
* the first value in buffer is a pulse, so that 0, 2, 4, ...
* contain a pulse duration. On the contrary, 1, 3, 5, ...
* contain a space duration.
*/
val = (i % 2) ? idata.space : idata.pulse;
for (j = 0; j < periods; j++)
idata.tx_buf[len++] = val;
}
memset(&xfer, 0, sizeof(xfer));
xfer.speed_hz = idata.freq * 16;
xfer.len = len * sizeof(*idata.tx_buf);
xfer.tx_buf = idata.tx_buf;
xfer.bits_per_word = 16;
ret = regulator_enable(idata.regulator);
if (ret)
return ret;
ret = spi_sync_transfer(idata.spi, &xfer, 1);
if (ret)
dev_err(&idata.spi->dev, "unable to deliver the signal\n");
regulator_disable(idata.regulator);
kfree(buffer);
pr_info("ir_spi_tx stop\n");
return ret ? ret : count*sizeof(int);
}
static long ir_spi_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret;
switch (cmd) {
case SET_TX_CARRIER:
if (get_user(idata.freq,(unsigned int __user *)arg)){
return -EFAULT;
}
idata.spi->max_speed_hz = idata.freq*16;
idata.spi->bits_per_word = 16;
ret = spi_setup(idata.spi);
if(ret<0){
dev_err(&idata.spi->dev, "set up spi speed fail\n");
return ret;
}
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static int ir_spi_set_duty_cycle(u32 duty_cycle)
{
int bits = (duty_cycle * 15) / 100;
idata.pulse = GENMASK(bits, 0);
if (idata.negated) {
idata.pulse = ~idata.pulse;
idata.space = 0xffff;
} else {
idata.space = 0;
}
return 0;
}
static int ir_spi_release(struct inode *node, struct file *filp)
{
return 0;
}
static struct file_operations ir_spi_fops = {
.owner = THIS_MODULE,
.open = ir_spi_open,
.unlocked_ioctl = ir_spi_ioctl,
.write = ir_spi_tx,
.release = ir_spi_release,
};
static struct miscdevice ir_spi_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = IR_SPI_DRIVER_NAME,
.fops = &ir_spi_fops,
};
static int ir_spi_probe(struct spi_device *spi)
{
int ret;
u8 dc;
idata.spi= spi;
idata.regulator = devm_regulator_get(&spi->dev, "irda_regulator");
if (IS_ERR(idata.regulator))
pr_err("could no found irda regu_regulator %ld",
PTR_ERR(idata.regulator));
idata.negated =
of_property_read_bool(spi->dev.of_node, "led-active-low");
ret = of_property_read_u8(spi->dev.of_node, "duty-cycle", &dc);
if (ret)
dc = 50;
ir_spi_set_duty_cycle(dc);
idata.freq = IR_SPI_DEFAULT_FREQUENCY;
ret = misc_register(&ir_spi_dev);
return 0;
}
static int ir_spi_remove(struct spi_device *spi)
{
return 0;
}
static const struct of_device_id ir_spi_of_match[] = {
{.compatible = "ir-spi-led"},
{},
};
static struct spi_driver ir_spi_driver = {
.probe = ir_spi_probe,
.remove = ir_spi_remove,
.driver = {
.name = IR_SPI_DRIVER_NAME,
.of_match_table = ir_spi_of_match,
},
};
module_spi_driver(ir_spi_driver);
MODULE_AUTHOR("Andi Shyti <andi@etezian.org>");
MODULE_DESCRIPTION("SPI IR LED");
MODULE_LICENSE("GPL v2");
hardware/libhardware/modules/consumerir/consumerir.c
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "ConsumerIrHal"
#include <errno.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <log/log.h>
#include <hardware/consumerir.h>
#include <hardware/hardware.h>
#define TX_IR _IOW(0xf5, 0, int)
#define SET_TX_CARRIER _IOW(0xf5, 1, int)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static const consumerir_freq_range_t consumerir_freqs[] = {
{.min = 30000,.max = 30000},
{.min = 33000,.max = 33000},
{.min = 36000,.max = 36000},
{.min = 38000,.max = 38000},
{.min = 40000,.max = 40000},
{.min = 56000,.max = 56000},
};
static int consumerir_transmit(struct consumerir_device *dev __unused,
int carrier_freq, const int pattern[],
int pattern_len)
{
int fd;
int ret;
int length;
int freq;
fd = open("/dev/ir-spi", O_RDWR);
if (fd < 0) {
ALOGE("open /dev/ir-spi error");
return -1;
}
freq = carrier_freq;
ret = ioctl(fd, SET_TX_CARRIER, &freq);
if (ret < 0)
ALOGE("set ir freq fail\n");
length = pattern_len * sizeof(int);
ret = write(fd, pattern, length);
if (ret != length) {
ALOGE("consumerir_transmit fail ret=%d length=%d\n", ret,
length);
} else {
ALOGE("consumerir_transmit ok\n");
}
return 0;
}
static int consumerir_get_num_carrier_freqs(struct consumerir_device *dev
__unused)
{
return ARRAY_SIZE(consumerir_freqs);
}
static int consumerir_get_carrier_freqs(struct consumerir_device *dev __unused,
size_t len,
consumerir_freq_range_t * ranges)
{
size_t to_copy = ARRAY_SIZE(consumerir_freqs);
to_copy = len < to_copy ? len : to_copy;
memcpy(ranges, consumerir_freqs,
to_copy * sizeof(consumerir_freq_range_t));
return to_copy;
}
static int consumerir_close(hw_device_t * dev)
{
free(dev);
return 0;
}
/*
* Generic device handling
*/
static int consumerir_open(const hw_module_t * module, const char *name,
hw_device_t ** device)
{
if (strcmp(name, CONSUMERIR_TRANSMITTER) != 0) {
return -EINVAL;
}
if (device == NULL) {
ALOGE("NULL device on open");
return -EINVAL;
}
consumerir_device_t *dev = malloc(sizeof(consumerir_device_t));
memset(dev, 0, sizeof(consumerir_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t *)module;
dev->common.close = consumerir_close;
dev->transmit = consumerir_transmit;
dev->get_num_carrier_freqs = consumerir_get_num_carrier_freqs;
dev->get_carrier_freqs = consumerir_get_carrier_freqs;
*device = (hw_device_t *) dev;
return 0;
}
static struct hw_module_methods_t consumerir_module_methods = {
.open = consumerir_open,
};
consumerir_module_t HAL_MODULE_INFO_SYM = {
.common = {
.tag = HARDWARE_MODULE_TAG,
.module_api_version = CONSUMERIR_MODULE_API_VERSION_1_0,
.hal_api_version = HARDWARE_HAL_API_VERSION,
.id = CONSUMERIR_HARDWARE_MODULE_ID,
.name = "Demo IR HAL",
.author = "The Android Open Source Project",
.methods = &consumerir_module_methods,
},
};
其他的修改见 https://blog.csdn.net/mike8825/article/details/79670891,不再描述。