Linux驱动程序--串口


文章摘要: 本文主要描述了基于串口驱动程序框架下,新建虚拟串口设备,不涉及硬件内容。
内核版本: Linux-3.8.13


注册串口驱动

// 串口驱动结构
static struct uart_driver serial_driver = {
.owner       = THIS_MODULE,
.driver_name = DRIVER_NAME,      // 驱动程序名称
.dev_name    = DEV_NAME,         // 设备文件称 ttyVn
.nr          = 20,                // 驱动程序支持串口的最大数量
//.cons      = &serial_console,  // 关连控制台
};

// 声明端口列表:端口列表应该放在设备私有数据中,
// 这里为了简单说明,采用了全局变量,
// 实际应用中不建议这么干
// 否则当多个驱动多个设备时,会出现严重问题。
static uart_port port[4];

// 注册串口驱动
ret = uart_register_driver(&serial_driver);

// 添加多个端口,port->line来表示端口号,端口号不能重复,  
// 几个相同设备共用一个驱动时,这里需注意调整;
for(i = 0; i < 4; i++)
{              
    port.line     = i;                  // 端口号
    port.ops      = serial_port_ops;    // 端口操作回调列表(后面详细说明)  
    
    port.type     = PORT_EFMUART;       // 端口类型
    port.iotype   = UPIO_MEM32;         // 32bit寄存器
    port.fifosize = 128;                // 发送FIFO深度        
    port.flags    = UPF_BOOT_AUTOCONF;  // 标志
    
    // 添加端口(正常情况下返回0)
    ret = uart_add_one_port(&serial_driver, &port[i]); 
}
    

此步操作后,将可以在设备目录下看到生成的4个串口设备。


移除设备

// 删除端口
for(i = 0; i < 4; i++)
{
    uart_remove_one_port(&serial_driver, &port[i]);     
} 

// 注销串口驱动
uart_unregister_driver(&serial_driver);

端口操作回调函数说明:

static struct uart_ops serial_port_ops = {
    .tx_empty     = is_tx_empty,        // 测试发送FOFI是否空
    .set_mctrl    = serial_set_mctrl,   // 硬件握手用
    .get_mctrl    = serial_get_mctrl,   // 硬件握手用
    .stop_tx      = serial_stop_tx,     // 函数调用
    .start_tx     = serial_start_tx,    // write时调用
    .stop_rx      = serial_stop_rx,     // 
    .enable_ms    = serial_enable_ms,    
    .break_ctl    = serial_break_ctl,     
    .startup      = serial_startup,     // open时调用
    .shutdown     = serial_shutdown,    // close时调用
    .set_termios  = serial_set_termios, // 设置参数,open时第2步调用
    .type         = serial_type,
    .release_port = serial_release_port,// 移除端口时调用
    .request_port = serial_request_port,
    .config_port  = serial_config_port, // 超级重要
    .verify_port  = serial_verify_port,
};
/*
 * 非常重要,添加端口后,自动调用的第一个回调函数
 * 如果不这里设置的值,则串口不会正常工作
 */
static void serial_config_port(struct uart_port *port, int type)
{   
    port->type = PORT_EFMUART;
}

- 阅读全文 -

Linux驱动程序--设备树资源引用


文章摘要: 描述一下设备树里的资源引用方法,知道怎么用就行了。

硬件平台: am335x
内核版本: linux-3.8.13


设备示例:

spiuart@0 {
    status = "okay";
    compatible = "hnsw,uart";
     
    spi-max-frequency=<24000000>;   // 最大频率
    reg = <1>;                      // 指的是标准的nCS引脚
        
    // 定义了3个片选引脚
    gpios-ncs = <&gpio3 14 1>,  
                <&gpio3 15 1>,  
                <&gpio3 16 1>; 

    // 定义了3个复位引脚                                          
    gpios-rst = <&gpio1 26 1>,  
                <&gpio2 28 1>,      
                <&gpio2 29 1>;
    
    /*
     * 定义了3个中断引脚,
     * 对于GPIO中断,不直接取中断向量定义  
     * 而是通过GPIO来定义  
     */  
    gpios-irq = <&gpio4 18 1>,
                <&gpio4 19 1>,      
                <&gpio4 21 1>;
};

GPIO示例:

/*
 * 通过compatible属性找到结点
 * 参数1:父结点(NULL表示根结点)
 * 参数2:设备类型
 * 参数3:compatible的属性值
 */
struct device_node *node = of_find_compatible_node(NULL, NULL,"hnsw,uart");
    
if(!node) {
    pr_err("can not find node.\r\n");
    return -1;      
}

// 获取gpios-ncs的数量,并获取端口编号
cnt = of_gpio_named_count(node, "gpios-ncs");

for(i = 0; i < cnt; i++)
{        
    spi_ncs[i] = of_get_named_gpio(node,"gpios-ncs",i); 
}

// 获取复位引脚端口    
cnt = of_gpio_named_count(node, "gpios-rst");
    
for(i = 0; i < cnt; i++)
{        
    spi_ncs[i] = of_get_named_gpio(node,"gpios-rst",i); 
}
    

中断示例

/*
 * 参数1 :结点指针
 * 参数2 :资源序号
 * 参数3 :资源结构(传出)
 * 返回值:中断号(0表示失败,用于判断结束)
 */

irq_num0 = of_irq_to_resource(node, 0, NULL);
irq_num1 = of_irq_to_resource(node, 1, r);

// of_irq_count函数实际上是通过of_irq_to_resource函数来计算
// 看起来比较鸡肋,所以没什么用

获取结点的几种方法:

// 通过路径获取结点
dev_node = of_find_node_by_path(spi->dev.of_node->full_name);

- 阅读全文 -

Linux驱动程序--SPI设备


硬件平台: am335x
内核版本:linux-3.8.13
编译器版本: arm-linux-gnueabihf-gcc


1.注册驱动程序

// 设备匹配列表
static const struct of_device_id spidev_dt_ids[] = {
    { .compatible = "oroct,uart" },
    {},
};
 
// spi驱动结构
static struct spi_driver spidev_spi_driver = {
    .driver = {
        .name = "oroct_spi",    //  这个名子不重要了
        .of_match_table = of_match_ptr(spidev_dt_ids),
    },
    .probe  =   spidev_probe,    // 探测函数
    .remove =   spidev_remove,   // 移除函数
};

static int __init hnsw_spidev_init(void)
{
    int ret; 
        
    // 注册SPI驱动程序
    ret = spi_register_driver(&spidev_spi_driver);   
         
    pr_info("spi_register_driver.ret = %d\r\n",ret);
            
    return ret;
}

static void __exit hnsw_spidev_exit(void)
{   
    // 注销SPI驱动
    spi_unregister_driver(&spidev_spi_driver);
}

module_init(hnsw_spidev_init);
module_exit(hnsw_spidev_exit);

2.添加代码树

&spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&mcspi1_pins>;
    
    // 之前添加的spidev设备
    spidev@0 {
        status = "okay";
        compatible = "rohm,dh2228fv";
        spi-max-frequency=<24000000>;
        reg = <0>;    
    };
    
    // 在这里添加新的spi接口的设备
    spiuart@1 {
        status = "okay";
        compatible = "oroct,uart";
        spi-max-frequency=<24000000>;
        reg = <1>;    // 片选线SPI_nCS1
    };
    

3.设备探测函数

static int spidev_probe(struct spi_device *spi)
{
       
      
}

SPI收发处理

static void spidev_complete(void *arg)
{
    complete(arg);
}

/* spi收发 */
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
    DECLARE_COMPLETION_ONSTACK(done);
    int status;

    message->complete = spidev_complete;    // 消息结束回调函数
    message->context = &done;

    spin_lock_irq(&spidev->spi_lock);
    if (spidev->spi == NULL) {
        status = -ESHUTDOWN;
    }
    else {
        status = spi_async(spidev->spi, message);   // 这一句才是干货
    }
    spin_unlock_irq(&spidev->spi_lock);

    if (status == 0) {
        wait_for_completion(&done);
        status = message->status;
        if (status == 0)
            status = message->actual_length;
    }
    return status;
}

//----------------------------------------------------------
/* spi发送操作 */
static inline ssize_t
spidev_sync_write(struct spidev_data* spidev, void* buff, int len)
{   
    struct spi_transfer t = {
        .tx_buf = buff,    // 发送缓存
        .len    = len,
    };
        
    // 标准的SPI收发流程
    struct spi_message  m;    
    spi_message_init(&m);
    spi_message_add_tail(&t,   &m);
    return spidev_sync(spidev, &m);
}

/* spi接收操作 */
static inline ssize_t
spidev_sync_read(struct spidev_data* spidev, void* buff, int len)
{
    struct spi_transfer t = {
        .rx_buf   = buff,    // 接收缓存
        .len      = len,
    };
    
    // 标准的SPI收发流程
    struct spi_message  m;
    spi_message_init(&m);           
    spi_message_add_tail(&t,  &m);
    return spidev_sync(spidev,&m);
}

- 阅读全文 -

Linux驱动程序--spidev设备


硬件平台: am335x
内核版本: linux-3.8.13
编译器版本: arm-linux-gnueabi-gcc 4.7.3


添加spidev设备

1.在内核配置菜单中选中SPI驱动

Device Drivers  --->
     [*] SPI support  --->
        <*>   McSPI driver for OMAP
        <*>   User mode SPI device driver support
  1. 在设备树上添加spidev设备
    查看相关代码driver/spi/spidev.c,其值为"rohm,dh2228fv"

修改dts文件,添加以下代码

&spi1 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&mcspi1_pins>;
    
    //创建SPI设备/dev/spidev1.0
    spidev@0 {
        status = "okay";
        compatible = "rohm,dh2228fv";    // 重要
        spi-max-frequency=<24000000>;
        reg = <0>;
    };
};

至此,可以在/dev/目录下看到spidev设备文件


- 阅读全文 -

DSP学习笔记--数字滤波器


文章摘要:
本文主要描述了关于数字滤波的原理,生成,及通过已知参数反算滤波器形状。

硬件平台: TMS320F38377D


数字滤波器原理:

稍后补充


通过Matlab来生成滤波器

应用程序-->Filter Design & Analysis

Response Type: 选择滤小波器类型
低通,高通,带通,带阻

经过测算:
100级滤波器运算时间为21.25us@200MHz,
滤波时间不能大于两次采样间隔,否则本次尚未计算完成,就有新数据又到来了;
别外还得保留一定的时间去处理其他代码;


滤波器实现

// 定义滤波器级数
#define FILTER_SERIES  20
//-----------------------------------------------
/*
 * 滤波器系数(系数之和为1)
 * Fc = 100/6400 * 2PI
 * 汉明窗20阶
 */
const float Hn[FILTER_SERIES + 1] = {
    0.007108650922711, 0.009180015082639,  0.01513516973633,
    0.02445967408233,  0.03628317103931,   0.04946582243642,
    0.06271405139908,  0.0747134966934,    0.08426512799562,
    0.09041003729335,  0.09252956663762,   0.09041003729335,
    0.08426512799562,  0.0747134966934,    0.06271405139908,
    0.04946582243642,  0.03628317103931,   0.02445967408233,
    0.01513516973633,  0.009180015082639,  0.007108650922711
};



static float Xn[50]={ 0 };   // 采样记录
//-------------------------------------------------
Uint16 Lowpass_Filter(float x)
{
    int i;
    float sum = 0;
    for (i = FILTER_SERIES; i > 0; i--)
    {
        Xn[i] = Xn[i - 1];      // 边计算边移位
        sum  += Xn[i] * Hn[i];
    }
    Xn[0] = x;
    sum  += Xn[0] * Hn[0];

    return(Uint16)(sum);
}

已知滤波器系数,反算滤波器特性
1.首先生成一个全频率波形(频域为直线,时域为尖脉冲)
2.将生成的波型通过滤波器
3.将滤波后的波形进行FFT计算,即可得出滤波器特性,并通过CCS自带工具生成波形。

#define SAMPLENUMBER    128

// 生成的点数要比计算点数稍多一点,主要用于滤波
float wave[256];    
float fft_out[SAMPLENUMBER/2]
void Make_All_Wave(void)
{
    int t;
    int j;
    float x;
    for(t = 0; t < 256; t++)
    {
        x = 0;  
        // 采样频率的一半即可
        for(j = 1; j < SAMPLENUMBER/2; j++)
        {
         w = j*2*PI/SAMPLENUMBER 
            x += cos(w * t) + 1;
        }
        x = Filter(x);  // 已知参数生成的滤波器
        wave[i] = x;
    }
}

// 执行FFT计算时跳过前几个字节
FFT(&wave[128], fft_out);

// 通过显示结果,显示出通过的频谱

频域图像为直线(所有频率分量都有,且分量相同,时域为尖脉冲)
时域为标准正弦波,频域图像为单点

方波:奇次谐波之和(注意系数)

- 阅读全文 -


Copyright©2025 春天花会开, All Rights Reserved. Email: webmaster@oroct.com