达芬奇平台DM644X(ARM9, Linux-2.6.10)BSP之gpio.c分析

2024-03-05 00:58

本文主要是介绍达芬奇平台DM644X(ARM9, Linux-2.6.10)BSP之gpio.c分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

 以下是两个文件gpio.c和gpio.h的注释和分析。
gpio.c
/*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include linux/errno.h>
#include linux/kernel.h>
#include linux/list.h>
#include linux/module.h>
#include linux/err.h>
#include linux/bitops.h>
#include asm/irq.h>
#include asm/io.h>
#include asm/hardware/clock.h>
#include asm/arch/irqs.h>
#include asm/arch/hardware.h>
#include asm/arch/gpio.h>
#include asm/arch/cpu.h>
#include asm/mach/irq.h>
/*
该文件实现了gpio的各种应用功能和向内核注册gpio的中断例程等功能。
用户的驱动程序可调用gpio_request和gpio_free使用或释放该gpio,
可以调用gpio_direction_input和gpio_direction_output函数设置gpio输入输出方向,
调用gpio_get_value和gpio_set_value获取设置值。
*/
static DEFINE_SPINLOCK(gpio_lock);
/* 总共有DAVINCI_N_GPIO(71)个gpio引脚,故使用相应多的bit来记录这些引脚的使用状态 */
static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO);
/*
申请一个gpio,其实就是检查该gpio是否空闲,如果空闲就可以使用并将该gpio相应的bit置位
(在gpio_in_use中)。
*/
int gpio_request(unsigned gpio, const char *tag)
{
    if (gpio >= DAVINCI_N_GPIO)
        return -EINVAL;
    if (test_and_set_bit(gpio, gpio_in_use))
        return -EBUSY;
    return 0;
}
EXPORT_SYMBOL(gpio_request);
/*
释放一个gpio,其实就是清除gpio相应的控制bit位(在gpio_in_use中)。
*/
void gpio_free(unsigned gpio)
{
    if (gpio >= DAVINCI_N_GPIO)
        return;
    clear_bit(gpio, gpio_in_use);
}
EXPORT_SYMBOL(gpio_free);
/* 获得gpio_controller结构体指针,gpio_controller结构体是gpio的核心控制单元,里面包含
gpio的设置和数据寄存器。该结构体和__gpio_to_controller函数在/include/asm-arm/
arch-davinci/gpio.h中定义,具体如下:
struct gpio_controller {
    u32    dir;
    u32    out_data;
    u32    set_data;
    u32    clr_data;
    u32    in_data;
    u32    set_rising;
    u32    clr_rising;
    u32    set_falling;
    u32    clr_falling;
    u32    intstat;
};
static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
    void *__iomem ptr;
    if (gpio >= DAVINCI_N_GPIO)
        return NULL;
    if (gpio
/* create a non-inlined version */
static struct gpio_controller *__iomem gpio2controller(unsigned gpio)
{
    return __gpio_to_controller(gpio);
}
/*
向某个gpio设置值,0或1。如果向gpio写1,则向set_data寄存器相应的位置1,如果写0,
则向clr_data寄存器相应的位置1.__gpio_mask函数在gpio.h中定义,定义如下,
static inline u32 __gpio_mask(unsigned gpio)
{
    return 1
/*
* Assuming the pin is muxed as a gpio output, set its output value.
*/
void __gpio_set(unsigned gpio, int value)
{
    struct gpio_controller *__iomem g = gpio2controller(gpio);
    // 设置gpio的值
    __raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data);
}
EXPORT_SYMBOL(__gpio_set);
/*
通过读取in_data寄存器相应该gpio的位来读取gpio的值。
使用这个函数之前,必须确认该gpio设置成输入模式,否则获得到值不可预料。
*/
/*
* Read the pin's value (works even if it's set up as output);
* returns zero/nonzero.
*
* Note that changes are synched to the GPIO clock, so reading values back
* right after you've set them may give old values.
*/
int __gpio_get(unsigned gpio)
{
    struct gpio_controller *__iomem g = gpio2controller(gpio);
   
    /* 读取gpio的值,!!的目的是使得返回的值为0或1.*/
    return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));   
}                                                                                                                    }
EXPORT_SYMBOL(__gpio_get);
/*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输入,故设置dir寄存器为1.
正如应为所说的,必须确认该引脚是作为gpio功能,而不是某个模块到功能,比如spi。通过PINMUX0
和PINMUX1两个寄存器来设置。
*/
/*--------------------------------------------------------------------------*/
/*
* board setup code *MUST* set PINMUX0 and PINMUX1 as
* needed, and enable the GPIO clock.
*/
int gpio_direction_input(unsigned gpio)
{
    struct gpio_controller *__iomem g = gpio2controller(gpio);
    u32 temp;
    u32 mask;
    if (!g)
        return -EINVAL;
    spin_lock(&gpio_lock);
    mask = __gpio_mask(gpio);
    temp = __raw_readl(&g->dir);
    temp |= mask;    // 设置成1
    __raw_writel(temp, &g->dir);    // 设置该gpio为输入
    spin_unlock(&gpio_lock);
    return 0;
}
EXPORT_SYMBOL(gpio_direction_input);
/*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输出,故设置dir寄存器为0.
value参数用于选择gpio设置成输出后该gpio输出的值。
*/
int gpio_direction_output(unsigned gpio, int value)
{
    struct gpio_controller *__iomem g = gpio2controller(gpio);
    u32 temp;
    u32 mask;
    if (!g)
        return -EINVAL;
    spin_lock(&gpio_lock);
    mask = __gpio_mask(gpio);
    temp = __raw_readl(&g->dir);
    temp &= ~mask;    // 设置成0
   
    //设置该gpio输出值
    __raw_writel(mask, value ? &g->set_data : &g->clr_data);
    __raw_writel(temp, &g->dir);    // 设置gpio为输出
    spin_unlock(&gpio_lock);
    return 0;
}
EXPORT_SYMBOL(gpio_direction_output);
/*
向gpio设置值,0或1。
*/
void gpio_set_value(unsigned gpio, int value)
{
    if (__builtin_constant_p(value)) {
        struct gpio_controller *__iomem g;
        u32 mask;
        if (gpio >= DAVINCI_N_GPIO)
            __error_inval_gpio();
        g = __gpio_to_controller(gpio);
        mask = __gpio_mask(gpio);
        if (value)
            __raw_writel(mask, &g->set_data);    // 该gpio输出高
        else
            __raw_writel(mask, &g->clr_data);    // 该gpio输出低
        return;
    }
    __gpio_set(gpio, value);
}
EXPORT_SYMBOL(gpio_set_value);
/*
读取gpio的值,0或1.
*/
int gpio_get_value(unsigned gpio)
{
    struct gpio_controller *__iomem g;
    if (!__builtin_constant_p(gpio))/* 判断该gpio值是否为编译时常数,如果是常数,
                                       函数返回 1,否则返回 0 */
        return __gpio_get(gpio);
    if (gpio >= DAVINCI_N_GPIO)
        return __error_inval_gpio();
    g = __gpio_to_controller(gpio);
   
    // 读取该gpio的值
    return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
}
EXPORT_SYMBOL(gpio_get_value);
/*
* We expect irqs will normally be set up as input pins, but they can also be
* used as output pins ... which is convenient for testing.
*
* NOTE:  GPIO0..GPIO7 also have direct INTC hookups, which work in addition
* to their GPIOBNK0 irq (but with a bit less overhead).  But we don't have
* a good way to hook those up ...
*
* All those INTC hookups (GPIO0..GPIO7 plus five IRQ banks) can also
* serve as EDMA event triggers.
*/
/*
禁止相应该irq的gpio的中断。每个gpio都可以作为中断的来源,其中gpio0-gpio7是独立的中断来源,
也就是分配独立的中断号,其他gpio则共用5个GPIOBNK中断线。其优先级可以在board-evm.c
中设置(已经介绍过)。在dm644x平台上,中断是电平边缘触发的,禁止中断其实就是既不设置
上升沿触发,也不设置下降沿触发。
*/
static void gpio_irq_disable(unsigned irq)
{
    struct gpio_controller *__iomem g = get_irq_chipdata(irq);
    u32 mask = __gpio_mask(irq_to_gpio(irq));
    __raw_writel(mask, &g->clr_falling);    // 清除下降沿触发
    __raw_writel(mask, &g->clr_rising);        // 清除上升沿触发
}
/*
中断使能。
在dm644x平台上,中断是电平边缘触发的,其实就是设置为上升沿或下降沿中断。
*/
static void gpio_irq_enable(unsigned irq)
{
    struct gpio_controller *__iomem g = get_irq_chipdata(irq);
    u32 mask = __gpio_mask(irq_to_gpio(irq));
    // 如果先前为下降沿中断,则使能为下降沿中断
    if (irq_desc[irq].status & IRQT_FALLING)
        __raw_writel(mask, &g->set_falling);
   
    // 如果先前为上升沿中断,则使能为上升沿中断
    if (irq_desc[irq].status & IRQT_RISING)   
        __raw_writel(mask, &g->set_rising);
}
/*
设置中断类型。
在dm644x平台上,中断有上升沿和下降沿两种触发方式。
*/
static int gpio_irq_type(unsigned irq, unsigned trigger)
{
    struct gpio_controller *__iomem g = get_irq_chipdata(irq);
    u32 mask = __gpio_mask(irq_to_gpio(irq));
    if (trigger & ~(IRQT_FALLING | IRQT_RISING))
        return -EINVAL;
    irq_desc[irq].status &= ~IRQT_BOTHEDGE;
    irq_desc[irq].status |= trigger;
    __raw_writel(mask, (trigger & IRQT_FALLING)
             ? &g->set_falling : &g->clr_falling);     // 设置为下降沿触发
    __raw_writel(mask, (trigger & IRQT_RISING)
             ? &g->set_rising : &g->clr_rising);    // 设置为上升沿触发
    return 0;
}
/*
该结构体用于注册到所有irq的中断描述结构体中(struct irqdesc),
而所有中断描述结构体定义成一个全局数组irq_desc 。
*/
static struct irqchip gpio_irqchip = {
    .unmask        = gpio_irq_enable, /* 用于使能中断,
                                      在enable_irq()等内核函数中会用到。*/   
    .mask        = gpio_irq_disable,/* 用于禁止中断,
                                      在disable_irq()等内核函数中会用到。*/
    .type        = gpio_irq_type,   /* 用于设置中断类型,
                                      在set_irq_type()内核函数中会用到。*/
};
/*
该函数将在下面的davinci_gpio_irq_setup中使用,将被注册到五个gpio bank中断的
irq_desc结构中,目的是处理所有级联的gpio中断。所谓级联的中断, 就是指有n个中断
共用同一个中断线。
在dm644x平台中,除了gpio0-gpio7外,其他63个gpio都共用五个gpiobank中断线,在这里,
gpio0-gpio7也被注册到gpiobank中断线,但实际上并不会使用,因为它们拥有自己的
中断线。其中,gpio0-gpio15共用IRQ_GPIOBNK0(56)中断线,gpio16-gpio31共用
IRQ_GPIOBNK1(57)中断线,gpio32-gpio47共用IRQ_GPIOBNK2(58)中断线,
gpio48-gpio63共用IRQ_GPIOBNK4(59)中断线,gpio64-gpio70共用
IRQ_GPIOBNK5(60)中断线,
因为寄存器是32位的,所以实际上只有三组寄存器,第一组包含bank0和bank1,
也就是gpio0-gpio31,第二组包含bank2和bank3,也就是gpio32-gpio63,
第三组包含bank4和bank5,也就是gpio64-gpio70,剩余了25个位没有使用。
*/
static void
gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs)
{
    struct gpio_controller *__iomem g = get_irq_chipdata(irq);
    u32 mask = 0xffff;
    /* we only care about one bank */
    // 如果bank中断线是寄数,则说明该中断的中断状态位在INTSTATn寄存器的高16位
    if (irq & 1)
        mask = 16;
    /* temporarily mask (level sensitive) parent IRQ */
    desc->chip->ack(irq);// 该ack函数会在arch/arm/mach-davinci/irq.c中注册。
    while (1) {
        u32        status;
        struct irqdesc    *gpio;
        int        n;
        int        res;
        /* ack any irqs */
        /*gpio中断发生后,硬件会在INTSTATn寄存器中置位相应位,
          以备程序查询,确定是哪个gpio*/
        status = __raw_readl(&g->intstat) & mask;
        if (!status)
            break;
        __raw_writel(status, &g->intstat);    // 向该位写1清除
        if (irq & 1)
            status >>= 16;
        /* now demux them to the right lowlevel handler */
        // 从下面的davinci_gpio_irq_setup函数可以看出来以下程序的运作。
        n = (int)get_irq_data(irq);    // 获取该bank对应的第一个gpio号
        gpio = &irq_desc[n];    // 获取该bank第一个gpio号对应的中断描述符
        while (status) {    // 该bank可能有多个gpio发生了中断
            res = ffs(status);    // 获取第一个发生了中断的位(1-32)
            n += res;    /* 获得该gpio的中断线(系统实际上只有64(0-63)个中断线,
                        但那些共用的gpio的中断也有自己的断描述符和中断线(从64开始),
                        仅仅是为了管理,不能通过request_irq()函数来申请。*/
            gpio += res;    //     获得该gpio的中断描述符
            
            /* 调用下面注册的do_simple_IRQ例程
               其又会调用用户通过request_irq()
               注册的中断例程
            */
            desc_handle_irq(n - 1, gpio - 1, regs);   
            status >>= res;        
        }
    }
    desc->chip->unmask(irq);    // 打开该irq中断线
    /* now it may re-trigger */
}
/*
* NOTE:  for suspend/resume, probably best to make a sysdev (and class)
* with its suspend/resume calls hooking into the results of the set_wake()
* calls ... so if no gpios are wakeup events the clock can be disabled,
* with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
* can be set appropriately for GPIOV33 pins.
*/
/*
注册gpio中断例程到内核中,并初始化了一些寄存器。
该函数将会被board_evm.c(其分析已经发表)中的evm_init()函数调用。
*/
int __init davinci_gpio_irq_setup(void)
{
    unsigned    gpio, irq, bank, banks;
    struct clk    *clk;
    clk = clk_get(NULL, "gpio");    // 获取时钟
    if (IS_ERR(clk)) {
        printk(KERN_ERR "Error %ld getting gpio clock?\n",
               PTR_ERR(clk));
        return 0;
    }
    clk_enable(clk);    // 使能gpio时钟并打开该模块电源
    for (gpio = 0, irq = gpio_to_irq(0), bank = (cpu_is_davinci_dm355() ?
         IRQ_DM355_GPIOBNK0 : (cpu_is_davinci_dm6467() ?
         IRQ_DM646X_GPIOBNK0 : IRQ_GPIOBNK0));    // dm644x的IRQ_GPIOBNK0(56)
         gpio  DAVINCI_N_GPIO; bank++) {    // dm644x的DAVINCI_N_GPIO(71)
        struct gpio_controller    *__iomem g = gpio2controller(gpio);
        unsigned        i;
        // 关该bank所有gpio的中断
        __raw_writel(~0, &g->clr_falling);
        __raw_writel(~0, &g->clr_rising);
        /* set up all irqs in this bank */
        // 同一个bank的所有gpio共用一个中断例程gpio_irq_handler
        set_irq_chained_handler(bank, gpio_irq_handler);
        set_irq_chipdata(bank, g);
        set_irq_data(bank, (void *)irq);
        for (i = 0; i  16 && gpio  DAVINCI_N_GPIO;
             i++, irq++, gpio++) {
            set_irq_chip(irq, &gpio_irqchip);    /* 注册用于gpio中断禁止、设能
                                                   和类型选择的回调例程 */
            set_irq_chipdata(irq, g);            // 保存控制结构体(寄存器)的地址
            set_irq_handler(irq, do_simple_IRQ);/* 为每个gpio中断设置同一个中
                                                    断例程do_simple_IRQ*/
            set_irq_flags(irq, IRQF_VALID);        // fiq中断有效
        }
    }
/*   
一个共用bank中断线的gpio中断发生后的大致的流程是:
--> gpio_irq_handler --> do_simple_IRQ --> __do_irq -->
action->handler(用户使用request_irq()注册的中断例程)
*/
    /* BINTEN -- per-bank interrupt enable. genirq would also let these
     * bits be set/cleared dynamically.
     */
    if (cpu_is_davinci_dm355())
        banks = 0x3f;
    else
        banks = 0x1f;
   
    // 向BINTEN寄存器写入0x1f(共5个位,每个位控制1个bank),打开所有的bank中断
    __raw_writel(banks, (void *__iomem)
             IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08));
    printk(KERN_INFO "DaVinci: %d gpio irqs\n", irq - gpio_to_irq(0));
    return 0;
}
gpio.h
/*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef    __DAVINCI_GPIO_H
#define    __DAVINCI_GPIO_H
/*
* basic gpio routines
*
* board-specific init should be done by arch/.../.../board-XXX.c (maybe
* initializing banks together) rather than boot loaders; kexec() won't
* go through boot loaders.
*
* the gpio clock will be turned on when gpios are used, and you may also
* need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are
* used as gpios, not with other peripherals.
*
* GPIOs are numbered 0..(DAVINCI_N_GPIO-1).  For documentation, and maybe
* for later updates, code should write GPIO(N) or:
*  - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53)
*  - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70)
*
* For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc
* for now, that's != GPIO(N)
*/
#define    GPIO(X)        (X)        /* 0
#define    GPIOV18(X)    (X)        /* 1.8V i/o; 0
#define    GPIOV33(X)    ((X)+54)    /* 3.3V i/o; 0
/*
寄存器都是32位到,每位对应一个gpio。
*/
struct gpio_controller {
    u32    dir;            // gpio方向设置寄存器
    u32    out_data;        // gpio设置为输出时,表示输出状态(0或1)
    u32    set_data;        // gpio设置为输出时,用于输出高电平
    u32    clr_data;        // gpio设置为输出时,用于输出低电平
    u32    in_data;        // gpio设置为输入时,用于读取输入值
    u32    set_rising;        // gpio中断上升沿触发设置
    u32    clr_rising;        // gpio中断上升沿触发清除
    u32    set_falling;    // gpio中断下降沿触发设置
    u32    clr_falling;    // gpio中断下降沿触发清除
    u32    intstat;        // gpio中断状态位,由硬件设置,可读取,写1时清除。
};
/* The __gpio_to_controller() and __gpio_mask() functions inline to constants
* with constant parameters; or in outlined code they execute at runtime.
*
* You'd access the controller directly when reading or writing more than
* one gpio value at a time, and to support wired logic where the value
* being driven by the cpu need not match the value read back.
*
* These are NOT part of the cross-platform GPIO interface
*/
static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
    void *__iomem ptr;
    if (gpio >= DAVINCI_N_GPIO)
        return NULL;
    if (gpio  32)
        ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
    else if (gpio  64)
        ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
    else if (gpio  96)
        ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
    else
        ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);
    return ptr;
}
static inline u32 __gpio_mask(unsigned gpio)
{
    return 1  (gpio % 32);
}
/* The get/set/clear functions will inline when called with constant
* parameters, for low-overhead bitbanging.  Illegal constant parameters
* cause link-time errors.
*
* Otherwise, calls with variable parameters use outlined functions.
*/
extern int __error_inval_gpio(void);
extern void __gpio_set(unsigned gpio, int value);
extern int __gpio_get(unsigned gpio);
/* Returns zero or nonzero; works for gpios configured as inputs OR
* as outputs.
*
* NOTE: changes in reported values are synchronized to the GPIO clock.
* This is most easily seen after calling gpio_set_value() and then immediatly
* gpio_get_value(), where the gpio_get_value() would return the old value
* until the GPIO clock ticks and the new value gets latched.
*/
extern int gpio_get_value(unsigned gpio);
extern void gpio_set_value(unsigned gpio, int value);
/* powerup default direction is IN */
extern int gpio_direction_input(unsigned gpio);
extern int gpio_direction_output(unsigned gpio, int value);
#include asm-generic/gpio.h>    /* cansleep wrappers */
extern int gpio_request(unsigned gpio, const char *tag);
extern void gpio_free(unsigned gpio);
static inline int gpio_to_irq(unsigned gpio)
{
    return DAVINCI_N_AINTC_IRQ + gpio;
}
static inline int irq_to_gpio(unsigned irq)
{
    return irq - DAVINCI_N_AINTC_IRQ;
}
#endif                /* __DAVINCI_GPIO_H */

这篇关于达芬奇平台DM644X(ARM9, Linux-2.6.10)BSP之gpio.c分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/774824

相关文章

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟 开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚 第一站:海量资源,应有尽有 走进“智听

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

如何解决线上平台抽佣高 线下门店客流少的痛点!

目前,许多传统零售店铺正遭遇客源下降的难题。尽管广告推广能带来一定的客流,但其费用昂贵。鉴于此,众多零售商纷纷选择加入像美团、饿了么和抖音这样的大型在线平台,但这些平台的高佣金率导致了利润的大幅缩水。在这样的市场环境下,商家之间的合作网络逐渐成为一种有效的解决方案,通过资源和客户基础的共享,实现共同的利益增长。 以最近在上海兴起的一个跨行业合作平台为例,该平台融合了环保消费积分系统,在短

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影