全志V3s音频设备驱动

2023-11-11 20:30
文章标签 驱动 音频设备 全志 v3s

本文主要是介绍全志V3s音频设备驱动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

ALSA

  • alsa子系统初始化
    linux-5.1.0\sound\core\sound.c
// 定义固定的alsa主设备号
#define CONFIG_SND_MAJOR	116	/* standard configuration */
static int major = CONFIG_SND_MAJOR;  
snd_major = major;
// 使用定义的主设备号注册字符设备,此时没有创建设备文件,统一使用snd_fops文件操作接口
if (register_chrdev(major, "alsa", &snd_fops)) 
// 其中snd_ops
static const struct file_operations snd_fops ={.owner =	THIS_MODULE,.open =		snd_open,.llseek =	noop_llseek,
};
// snd_open只是设备文件操作的中转,次设备号对应的文件操作接口放在snd_minors数组,而没有注册到设备文件
static int snd_open(struct inode *inode, struct file *file)// 获取次设备号unsigned int minor = iminor(inode);// 根据次设备号,从数组找到设备的真正fops结构体mptr = snd_minors[minor];replace_fops(file, new_fops);// snd_minors数组的设置
snd_ctl_dev_register(struct snd_device *device)、snd_pcm_dev_register(struct snd_device *device) 调用snd_register_device(int type, struct snd_card *card, int dev,	const struct file_operations *f_ops,	void *private_data, struct device *device)preg->f_ops = f_ops; // 传进来的file_operations结构体// 该函数通过snd_minors数组查找未使用的此设备号。minor = snd_find_free_minor(type, card, dev);snd_minors[minor] = preg;// 该函数时为例mdev或udev能够自动创建设备文件err = device_add(device);// 前一步需要的设备文件的名字,在调用snd_register_device前就设置好了
linux-5.1.0\sound\core\control.c
snd_ctl_create(struct snd_card *card)static struct snd_device_ops ops = {.dev_free = snd_ctl_dev_free,.dev_register =	snd_ctl_dev_register,.dev_disconnect = snd_ctl_dev_disconnect,};dev_set_name(&card->ctl_dev, "controlC%d", card->number);linux-5.1.0\sound\core\pcm.c	
_snd_pcm_new(struct snd_card *card, const char *id, int device, int playback_count, int capture_count, bool internal, struct snd_pcm **rpcm)	static struct snd_device_ops ops = {.dev_free = snd_pcm_dev_free,.dev_register =	snd_pcm_dev_register,.dev_disconnect = snd_pcm_dev_disconnect,};snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device, stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');

ASOC

1. machine:匹配platform端与codec端
linux-5.1.0\sound\soc\soc-core.c
linux-5.1.0\include\sound\soc.h
struct snd_soc_card *card;/* CPU <--> Codec DAI links  */struct snd_soc_dai_link *dai_link;  /* predefined links only */// dai_link常用属性,v3s内置声卡为例,dai_link核心功能是用于platform端与codec端的匹配link->name		= "cdc";link->stream_name	= "CDC PCM";link->codec_dai_name	= "Codec";link->cpu_dai_name	= dev_name(dev);link->codec_name	= dev_name(dev);link->platform_name	= dev_name(dev);link->dai_fmt		= SND_SOC_DAIFMT_I2S;// 一个声卡可以有多个link*num_links = 1;// dai_link交给snd_soc_cardcard->dai_link = link;  // sun4i_codec_create_link(dev, &card->num_links);card->dev		= dev;card->name		= "sun4i-codec";card->dapm_widgets	= sun4i_codec_card_dapm_widgets;card->num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_card_dapm_widgets);card->dapm_routes	= sun4i_codec_card_dapm_routes;card->num_dapm_routes	= ARRAY_SIZE(sun4i_codec_card_dapm_routes);
ret = snd_soc_register_card(card);

snd_soc_register_card注册snd_soc_card的过程分析

snd_soc_register_card(struct snd_soc_card *card)// 遍历要注册到这个card的所有link,soc_init_dai_link(card, link);// 绑定platform和codec,并调用二者的probe函数等snd_soc_bind_card(card);// 绑定和注册声卡cardsnd_soc_instantiate_card(card);// 1soc_bind_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)// 判断当前要注册的card的rtd中是否已绑定dai_link,&(card)->rtd_list->dai_linksoc_is_dai_link_boundif (rtd->dai_link == dai_link)   return true;// 创建rtd,/* SoC machine DAI configuration, glues a codec and cpu DAI together */rtd = soc_new_pcm_runtime(card, dai_link);// 找出cpu dairtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);// 找出codec dai,并交给rtdcodec_dais[i] = snd_soc_find_dai(codecs);   rtd->codec_dai = codec_dais[0];// 新的rtd放到当前准备注册的声卡soc_add_pcm_runtime(card, rtd);		// 2 两端的绑定工作完成,创建声卡结构体。	/* card bind complete so register a sound card */ret = snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, card->owner, 0, &card->snd_card);// 3 创建card结构体随带的controlsif (card->dapm_widgets)snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,  card->num_dapm_widgets);// 4 匹配component drive,例子中是sun4i_codec_component、quirks->codec,并调用component driver的probe函数。/* probe all components used by DAI links on this card */ret = soc_probe_link_components(card, rtd, order);// 5 匹配dai driver,并调用dai driver的probe函数。/* probe all DAI links on this card */ret = soc_probe_link_dais(card, rtd, order);// 6 正式注册声卡ret = snd_card_register(card->snd_card);

其中snd_soc_register_card的soc_init_dai_link

	// 如果snd_soc_dai_link中的platform为空,对snd_soc_dai_link中的platforms进行初始化填充snd_soc_init_platform(card, link);if (!platform)platform->name		  = dai_link->platform_name;platform->of_node	  = dai_link->platform_of_node;// 对于遍历到的当前link,如果三者1个不为空,则填充填充一个codec = codecs[0]snd_soc_init_multicodecif (dai_link->codec_name || dai_link->codec_of_node || dai_link->codec_dai_name) dai_link->codecs[0].name = dai_link->codec_name;dai_link->codecs[0].of_node = dai_link->codec_of_node;dai_link->codecs[0].dai_name = dai_link->codec_dai_name;// Codec 的name和of_node 二者只能且必须设置一个/*  Codec must be specified by 1 of name or OF node,  not both or neither. */if (!!codec->name ==  !!codec->of_node) dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n", link->name);/* 必须设置, Codec DAI name must be specified */if (!codec->dai_name)  dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n", link->name);/*  Platform may be specified by either name or OF node, but* can be left unspecified, and a dummy platform will be used. */if (link->platforms->name && link->platforms->of_node)  dev_err(card->dev, "ASoC: Both platform name/of_node are set for %s\n", link->name);/*  CPU device may be specified by either name or OF node, but* can be left unspecified, and will be matched based on DAI name alone.. */if (link->cpu_name && link->cpu_of_node) dev_err(card->dev, "ASoC: Neither/both cpu name/of_node are set for %s\n", link->name);/*  At least one of CPU DAI name or CPU device name/node must be specified  */if (!link->cpu_dai_name &&  !(link->cpu_name || link->cpu_of_node)) dev_err(card->dev, "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", link->name);
2. platform,host端的控制接口

platform与codec的注册都使用同一个函数devm_snd_soc_register_component

linux-5.1.0\sound\soc\soc-devres.c
// 其中devm是一种资源管理的方式,不用考虑资源释放,内核会内部做好资源回收。
devm_snd_soc_register_component(&pdev->dev, &sun4i_codec_component, &dummy_cpu_dai, 1);snd_soc_register_componentsnd_soc_add_componentsnd_soc_register_dais(component, dai_drv, num_dai);/* Create a DAI and add it to the component's DAI list */static struct snd_soc_dai *soc_add_dai// dai是当前新建的,component是传进来的,把dai挂到当前注册的component上。list_add_tail(&dai->list, &component->dai_list);snd_soc_component_add(component);// 其中component_list是全局变量:static LIST_HEAD(component_list);// 把当前component注册到全局链表list_add(&component->list, &component_list);

dma的通道注册

// 设备树中获取声卡控制器的地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
// 获取声卡数字数据的输入输出寄存器地址偏移量
#define SUN4I_CODEC_DAC_TXDATA			(0x0c)
#define SUN4I_CODEC_ADC_RXDATA			(0x24)
.reg_dac_txdata	= SUN4I_CODEC_DAC_TXDATA,
.reg_adc_rxdata	= SUN6I_CODEC_ADC_RXDATA,
// 绑定声卡数据寄存器到DMA的内存地址上。
/* DMA configuration for TX FIFO */
scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;
/* DMA configuration for RX FIFO */
scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;
// 把设置的两个DMA数据结构snd_dmaengine_dai_dma_data绑定到cpu dai
static struct snd_soc_dai_driver dummy_cpu_daisun4i_codec_dai_probesnd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data, &scodec->capture_dma_data);dai->playback_dma_data = playback;dai->capture_dma_data = capture;devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);snd_dmaengine_pcm_registerdmaengine_pcm_request_chan_of // 申请DMA通道// 指定要使用的DMA的名字,与设备树的DMA对应。static const char * const dmaengine_pcm_dma_channel_names[] = {[SNDRV_PCM_STREAM_PLAYBACK] = "tx",[SNDRV_PCM_STREAM_CAPTURE] = "rx",};dma_request_slave_channel_reason(dev, name);dma_request_chan(struct device *dev, const char *name)// 通过设备树的of_node获取名字相同的dma硬件信息。of_dma_request_slave_channel(dev->of_node, name);// 在设备树中的DMA硬件信息
dmas = <&dma 15>, <&dma 15>;
dma-names = "rx", "tx";
3. codec,device端的控制接口

和platform一样,使用同一接口注册component,即platform和codec在machine眼中都是component

devm_snd_soc_register_component(&pdev->dev, quirks->codec, &sun4i_codec_dai, 1);
4. 从设备驱动开发角度总结:asoc在实际应用需要注意的要点
  • machine的snd_soc_dai_link中,用于匹配的name要设置,具体看soc_init_dai_link
    link->codec_dai_name = “iCodec”;
    link->cpu_dai_name = dev_name(dev);
    link->codec_name = dev_name(dev);
    link->platform_name = dev_name(dev);
  • 对于内置控制器的音频驱动,platform端的component driver没什么内容,内容集中在codec端的component driver做控制
static const struct snd_soc_component_driver sun4i_codec_component = {.name = "sun4i-codec",
};
static const struct snd_soc_component_driver sun4i_codec_codec = {.controls		= sun4i_codec_controls,.num_controls		= ARRAY_SIZE(sun4i_codec_controls),.dapm_widgets		= sun4i_codec_codec_dapm_widgets,.num_dapm_widgets	= ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),.dapm_routes		= sun4i_codec_codec_dapm_routes,.num_dapm_routes	= ARRAY_SIZE(sun4i_codec_codec_dapm_routes),.idle_bias_on		= 1,.use_pmdown_time	= 1,.endianness		= 1,.non_legacy_dai_naming	= 1,
};
  • platform端和codec端的dai driver控制接口基本一致,只是参数能力有所区别。
static struct snd_soc_dai_driver sun4i_codec_dai = {.name	= "Codec",.ops	= &sun4i_codec_dai_ops,.playback = {.stream_name	= "Codec Playback",.channels_min	= 1,.channels_max	= 2,.rate_min	= 8000,.rate_max	= 192000,.rates		= SNDRV_PCM_RATE_CONTINUOUS,.formats	= SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S32_LE,.sig_bits	= 24,},.capture = {.stream_name	= "Codec Capture",.channels_min	= 1,.channels_max	= 2,.rate_min	= 8000,.rate_max	= 48000,.rates		= SNDRV_PCM_RATE_CONTINUOUS,.formats	= SNDRV_PCM_FMTBIT_S16_LE |SNDRV_PCM_FMTBIT_S32_LE,.sig_bits	= 24,},
};
  • platform与codec,两端的重要职能
platform							<------>						codec
cpu dai:cpu端音频数据传输的参数								codec dai:codec端的音频数据传输的参数
component driver:多为空或者DMA的控制接口						component driver:对codec功能的控制接口集,controls、widgets、routes
DMA:cpu dai与内存的数据搬运桥梁		

IIS音频数据传输协议,实质是IIS控制器驱动

若是内置声卡控制器,不用音频数据传输协议,直接读写相应寄存器
s3c24xx-iis 驱动分析
  • machine中没有dma的代码,只有用于匹配的snd_soc_dai_link
linux-5.1.0\sound\soc\samsung\s3c24xx_uda134x.cstatic struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {.name = "UDA134X",.stream_name = "UDA134X",.codec_name = "uda134x-codec",.codec_dai_name = "uda134x-hifi",.cpu_dai_name = "s3c24xx-iis",.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBS_CFS,.ops = &s3c24xx_uda134x_ops,.platform_name	= "s3c24xx-iis",    // dma附在了s3c24xx-iis中,所以该处匹配名称没有实际的作用。
};struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
ret = devm_snd_soc_register_card(&pdev->dev, card);
  • 源码路径
    linux-5.1.0\sound\soc\samsung\s3c24xx-i2s.c

  • s3c24xx-i2s驱动的platform_device

linux-5.1.0\arch\arm\plat-samsung\devs.c
struct platform_device s3c_device_iis = {.name		= "s3c24xx-iis",.id		= -1,.num_resources	= ARRAY_SIZE(s3c_iis_resource),.resource	= s3c_iis_resource,.dev		= {.dma_mask		= &samsung_device_dma_mask,.coherent_dma_mask	= DMA_BIT_MASK(32),}
};
  • platform端的的snd_soc_component_driver为空
static const struct snd_soc_component_driver s3c24xx_i2s_component = {.name		= "s3c24xx-i2s",
};
  • dma使用附在了s3c24xx-i2s中
s3c24xx_iis_dev_probes3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
// dma绑定到了platform的dai driver中
static struct snd_soc_dai_driver s3c24xx_i2s_dai.probe = s3c24xx_i2s_probe,snd_soc_dai_init_dma_data(dai, &s3c24xx_i2s_pcm_stereo_out, &s3c24xx_i2s_pcm_stereo_in);
  • i2s的操作接口在snd_soc_dai_driver中
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {.ops = &s3c24xx_i2s_dai_ops,
};
s3c24xx_i2s_dai_opss3c24xx_i2s_triggers3c24xx_snd_rxctrl(1);// iis的实际寄存器操作iisfcon = readl(s3c24xx_i2s.regs + S3C2410_IISFCON);iiscon  = readl(s3c24xx_i2s.regs + S3C2410_IISCON);iismod  = readl(s3c24xx_i2s.regs + S3C2410_IISMOD);			
  • 其中s3c24xx_i2s.regs在platform_driver的probe函数中取platform_device的寄存器资源
s3c24xx_iis_dev_proberes = platform_get_resource(pdev, IORESOURCE_MEM, 0);s3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res);
sun4i-i2s 驱动分析

linux-5.1.0\sound\soc\sunxi\sun4i-i2s.c

声卡控制之kcontrol

对于普通的snd_kcontrol:
snd_soc_add_controls : snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表

static const struct snd_soc_component_driver soc_component_dev_wm8960 = {.probe			= wm8960_probe,snd_soc_add_component_controls(component, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls));static const struct snd_kcontrol_new wm8960_snd_controls[] = {SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, 0, 63, 0, inpga_tlv),SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 6, 1, 0),}

声卡控制之DAPM(动态音频电源管理)

  • 系列文章:
    Asoc dapm(一) - kcontrol

  • kcontrol
    dapm的kcontrol是通过widget注册的,即没有进行显式dapm的kcontrol注册

DAPM的kcontrol注册过程
a.1 snd_soc_dapm_new_controls  // 把widget放入card->widgets链表
b.2 在注册machine驱动时, 导致如下调用: soc_probe_dai_link > soc_post_component_init > snd_soc_dapm_new_widgets
即snd_soc_dapm_new_controls先注册了widget,但是widget中带的kcontrols要等machine驱动注册时才寻找添加snd_soc_dapm_new_widgets:对于每一个widget, 设置它的power_check函数(用来判断该widget是否应该上电)  对于mixer widget, 取出其中的snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表对于mux widget,它只有一个snd_kcontrol_new, 构造出snd_kcontrol, 放入card->controls链表
  • widget
static const struct snd_soc_component_driver soc_component_dev_wm8960 = {.probe			= wm8960_probe,wm8960_add_widgets(component);snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets,  ARRAY_SIZE(wm8960_dapm_widgets));static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {SND_SOC_DAPM_INPUT("LINPUT1"),SND_SOC_DAPM_INPUT("RINPUT1"),//带kcontrol的widgetSND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, wm8960_lin, ARRAY_SIZE(wm8960_lin)),其中的kcontrolstatic const struct snd_kcontrol_new wm8960_lin[] = {SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),};}
  • route和path
    通过route的信息构建path,并将新建的path放入声卡card的paths(complete path)中,但kcontrol此时还没设置
static const struct snd_soc_component_driver soc_component_dev_wm8960 = {.probe			= wm8960_probe,wm8960_add_widgets(component);snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));// 其中根据audio_paths的的sink和source找到对应的widget,进而创建pathstatic const struct snd_soc_dapm_route audio_paths[] = {{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },}// 找sink source对应的widget,构造path;kcontrol暂时为NULLsnd_soc_dapm_add_route(dapm, route); list_for_each_entry(w, &dapm->card->widgets, list)ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control, route->connected);// 设置connect  /* connect static paths */if (control == NULL) {path->connect = 1;} else {// 根据source和sink的type类型设置connect,此处id其实是控件类型typeswitch (wsource->id) {}switch (wsink->id) {}}// 新建的path放入到声卡card的paths中,即构成了complete pathlist_add(&path->list, &dapm->card->paths);

设置path的kcontrol

soc_probe_dai_link > soc_post_component_init > snd_soc_dapm_new_widgetssnd_soc_dapm_new_widgets:对于每一个widget, 设置它的power_check函数(用来判断该widget是否应该上电)  对于mixer widget, 取出其中的snd_kcontrol_new构造出snd_kcontrol, 放入card->controls链表对于mux widget,它只有一个snd_kcontrol_new, 构造出snd_kcontrol, 放入card->controls链表
  • complete path
    在这里插入图片描述

这篇关于全志V3s音频设备驱动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

驱动(RK3588S)第七课时:单节点设备树

目录 需求一、设备树的概念1、设备树的后缀名:2、设备树的语法格式3、设备树的属性(重要)4、设备树格式举例 二、设备树所用函数1、如何在内核层种获取设备树节点:2、从设备树上获取 gpio 口的属性3、获取节点上的属性只针对于字符串属性的4、函数读取 np 结点中的 propname 属性的值,并将读取到的 u32 类型的值保存在 out_value 指向的内存中,函数的返回值表示读取到的

驱动安装注册表指令

HKCR: HKEY_CLASSES_ROOT HKCU: HKEY_CURRENT_USER HKLM: HKEY_LOCAL_MACHINE HKU: HEKY_USER HER: 相对根键

UMDF驱动安装

VS2013 + WDF8.1,UMDF驱动选择User Mode Driver,不要选User Mode Driver 2.0,否则Win7安装有问题,如图 另外,在驱动安装时不要忘记WUDFUpdate_<主版本号><次版本号>.dll文件,具体文件名在INF中查找。此文件可在WDF的安装目录中找到。注意:在WDF的安装目录中会有3个WUDFUpdate_xxx.dll文件,x86,x6

电脑驱动分类

电脑驱动程序(驱动程序)是操作系统与硬件设备之间的桥梁,用于使操作系统能够识别并与硬件设备进行通信。以下是常见的驱动分类: 1. 设备驱动程序 显示驱动程序:控制显卡和显示器的显示功能,负责图形渲染和屏幕显示。 示例:NVIDIA、AMD 显示驱动程序。打印机驱动程序:允许操作系统与打印机通信,控制打印任务。 示例:HP、Canon 打印机驱动程序。声卡驱动程序:管理音频输入和输出,与声卡硬件

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击,

windows10 卸载网络驱动以及重新安装

右键桌面此电脑的图标,点击管理,设备管理器—网络适配器,找到下图中的驱动(不同的系统或者显卡会导致网卡驱动名称与下图不一样,多为Realtek开头),右键选择卸载设备,然后重启电脑,系统会自动重新安装驱动 新电脑首次安装驱动: 根据主板厂家,比如华硕,进入华硕官网,点击服务支持,点击下载中心,选择型号,点击右侧驱动程序和工具软件,选择windows版本,下载相应的驱动,下载完之后在对应文件中找

笔记整理—内核!启动!—kernel部分(1)驱动与内核的关系

首先,恭喜完成了uboot部分的内容整理,其次补充一点,uboot第一部分和第二部分的工作不是一定的,在不同的版本中,可能这个初始化早一点,那个的又放在了第二部分,版本不同,造成的工作顺序不同,但终归是要完成基本内容初始化并传参给kernel的。         那么至于驱动与内核的关系,用一张图来说明最适合不过:         驱动位于OS层的中下层与硬件相接。驱动是内

读源码笔记--文件过滤驱动FileSpy第1篇 -- DriverEntry

今天只读FileSpy的DriverEntry,位于源文件:filespy.c。 // // 全局变量. // ULONG gFileSpyDebugLevel = DEFAULT_FILESPY_DEBUG_LEVEL; #if WINVER >= 0x0501 ULONG gFileSpyAttachMode = FILESPY_ATTACH_ALL_VOLUMES; #else ULON

Circuit Design 三极管驱动蜂鸣器电路 及 蜂鸣器两端电压正确但是不响的解决方案

利用三极管进行电流放大的蜂鸣器驱动电路图: (百度图片找的) 我用有源蜂鸣器实现的这个电路,但是蜂鸣器不响。 details: 1. VCC =5V 蜂鸣器两端的直接电压约为4.5V, 但是蜂鸣器不响。 2. 将蜂鸣器直接接在4.5V的电源两端,蜂鸣器响。(说明蜂鸣器是好的) 3. 测了三极管各个管脚的电压, 和理论上的是一致的。 情况很奇怪,换了好几个三极管结果都是一样的,