全志F1C100S声卡驱动探究

article/2025/9/22 4:01:25

如果有问题,请加QQ群 891339868 进行交流

        最近刚上手全志的F1C100S这个片子,听一下音乐,本来想着挺简单,内核配置一下驱动就OK,谁知道上来就一闷棍,主线内核不支持内置声卡,经过多方面的研究,终于把这个声卡驱动搞定,今天记录一下。

首先来看一下f1c100s的内部声卡的框图:

从图上可以看得出来,模拟部分是4输入,2输出,一个输入混音器,两个输出混音器,还有与之相对应的开关,搞清楚这些东西很重要,因为这里面的每一个组件或者开关,在声卡驱动里面都有对应的组件描述。

硬件结构有一定的了解以后,再来看一下和该声卡有关联的寄存器:

上图中就是和f1c100s的内部codec相关的所有的寄存器,在这里先不做详细的介绍,下面用到后再说。

和其他声卡驱动一样,f1c100s的声卡驱动也是给予ASOC框架做的,也是分为platform部分,codec部分,machine部分,他们又分别包含codec驱动和DAI驱动,我的理解codec驱动是提供资源,DAI驱动是规定怎么传输和使用这些资源

一、先来看一下codec部分:

首先看一这两个比较重要的数据结构:

struct suniv_codec_quirks {const struct regmap_config *regmap_config;const struct snd_soc_codec_driver *codec;struct snd_soc_card * (*create_card)(struct device *dev);struct reg_field reg_adc_fifoc;	/* used for regmap_field */unsigned int reg_dac_txdata;	/* TX FIFO offset for DMA config */unsigned int reg_adc_rxdata;	/* RX FIFO offset for DMA config */bool has_reset;u32 dma_max_burst;
};static const struct suniv_codec_quirks suniv_codec_quirks = {.regmap_config	= &suniv_codec_regmap_config,.codec		= &suniv_codec_codec,.create_card	= suniv_codec_create_card,.reg_adc_fifoc	= REG_FIELD(SUNIV_CODEC_ADC_FIFOC, 0, 31),.reg_dac_txdata	= SUNIV_CODEC_DAC_TXDATA,.reg_adc_rxdata	= SUNIV_CODEC_ADC_RXDATA,.has_reset = true,//.dma_max_burst = SUNIV_DMA_MAX_BURST,//
};

第一个定义的结构体是和f1c100s的codec相关的数据的一个集合,第二个是对其进行数据填充。

(一)、struct regmap_config:寄存器地址的相关配置

static const struct regmap_config suniv_codec_regmap_config = {.reg_bits	= 32,.reg_stride	= 4,.val_bits	= 32,.max_register	= SUNIV_CODEC_ADC_DAP_ORT_REG,
};

1、reg_bits:寄存器地址的长度,32位的,所以配置为32

2、reg_stride:寄存器的跨度,32位的,所以配置位8

3、val_bits:寄存器值的长度,32位的,所以配置位32

4、max_register:寄存器最大的偏移地址,可以配置为和声卡相关的最大的寄存器地址

相对来说,上面这些配置比较简单也比较格式化,到这里就介绍完了。

(二)、struct snd_soc_codec_driver:驱动的相关配置

static const struct snd_soc_codec_driver suniv_codec_codec = {.component_driver = {.controls		= suniv_codec_controls,//音频控件.num_controls		= ARRAY_SIZE(suniv_codec_controls),.dapm_widgets		= suniv_codec_codec_dapm_widgets,//dapm控件.num_dapm_widgets	= ARRAY_SIZE(suniv_codec_codec_dapm_widgets),.dapm_routes		= suniv_codec_codec_dapm_routes,//dapm路由.num_dapm_routes	= ARRAY_SIZE(suniv_codec_codec_dapm_routes),},
};

在这里面主要配置和DAPM(动态电源管理)组件相关的东西:

1、struct snd_kcontrol_new *controls:声卡的音频控件,其实就是各个通路上的通断开关和音量调节开关之类的东西,下面来看一下该声卡的具体配置:

static const struct snd_kcontrol_new suniv_codec_controls[] = {SOC_SINGLE_TLV("Headphone Playback Volume", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_HP_VOL, 0x3f, 1,suniv_codec_hp_volume_scale),SOC_SINGLE_TLV("DAC Playback Volume", SUNIV_CODEC_DAC_DPC,SUNIV_CODEC_DAC_DPC_DVOL, 0x3f, 1,suniv_codec_dvol_scale),SOC_DOUBLE("Headphone Playback Switch", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_LHPPAMUTE_MUTE,SUNIV_CODEC_DAC_ACTL_RHPPAMUTE_MUTE, 1, 0),SOC_SINGLE("MIC AMPEN Switch", SUNIV_CODEC_ADC_ACTL,SUNIV_CODEC_ADC_ACTL_MIC_AMPEN, 1, 0),
};

在这里其实是定义了四个控制开关,这几个开关在系统启动后,可以使用amixer 工具进行设置和查看的,在配置时,使用了SOC_SINGLE_TLV和SOC_DOUBLE这两个宏,具体这两个宏怎么定义的,可以具体百度,区别就是第一个是一次设置一个寄存器,第二个是一次设置两个寄存器。

2、struct snd_soc_dapm_widget *dapm_widgets:动态电源控制控件,其实就是对应硬件框图里面的混音器、输入、输出之类的东西,下面来看一下 具体的配置:

static const struct snd_soc_dapm_widget suniv_codec_codec_dapm_widgets[] = {/* Microphone inputs */SND_SOC_DAPM_INPUT("MIC"),/* Mic input path */SND_SOC_DAPM_PGA("Mic Amplifier", SUNIV_CODEC_ADC_ACTL,SUNIV_CODEC_ADC_ACTL_MIC_AMPEN, 0, NULL, 0),/* Line In */SND_SOC_DAPM_INPUT("LINEIN"),/* FM In */SND_SOC_DAPM_INPUT("FMINR"),SND_SOC_DAPM_INPUT("FMINL"),/* Digital parts of the ADCs */SND_SOC_DAPM_SUPPLY("ADC Enable", SUNIV_CODEC_ADC_FIFOC,SUNIV_CODEC_ADC_FIFOC_EN_AD, 0, NULL, 0),/* Digital parts of the DACs */SND_SOC_DAPM_SUPPLY("DAC Enable", SUNIV_CODEC_DAC_DPC,SUNIV_CODEC_DAC_DPC_EN_DA, 0, NULL, 0),/* Analog parts of the ADCs */SND_SOC_DAPM_ADC("ADC", "Codec Capture", SUNIV_CODEC_ADC_ACTL,SUNIV_CODEC_ADC_ACTL_ADC_EN, 0),/* Analog parts of the DACs */SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_DACAENL, 0),SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_DACAENR, 0),/* Mixers */SOC_MIXER_ARRAY("Left Mixer", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_LMIXEN, 0,suniv_codec_out_mixer_controls),SOC_MIXER_ARRAY("Right Mixer", SUNIV_CODEC_DAC_ACTL, SUNIV_CODEC_DAC_ACTL_RMIXEN, 0,suniv_codec_out_mixer_controls),SOC_MIXER_ARRAY("ADC Mixer", SUNIV_CODEC_ADC_ACTL,SND_SOC_NOPM, 0,suniv_codec_adc_mixer_controls),/* Headphone outpu path*/SND_SOC_DAPM_MUX("Headphone Source Playback Route",SND_SOC_NOPM, 0, 0, suniv_codec_hp_src),SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_HPPAEN, 0, NULL, 0),SND_SOC_DAPM_SUPPLY("HPCOM Protection", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_HPCOMPEN, 0, NULL, 0),SND_SOC_DAPM_REG(snd_soc_dapm_supply, "HPCOM", SUNIV_CODEC_DAC_ACTL,SUNIV_CODEC_DAC_ACTL_HPCOM_FC, 0x3, 0x3, 0),SND_SOC_DAPM_OUTPUT("HP"),
};

从上面的代码可以看出来,这里面是根据codec的内部硬件框图对各个接口进行抽象定义,也是根据内核中的ASOC框架提供的各种宏,对相关的寄存器进行初始化。

3、struct snd_soc_dapm_route *dapm_routes:动态路由控制,其实就是配置codec各个线路怎么连接,信号怎么走,具体的配置如下:

static const struct snd_soc_dapm_route suniv_codec_codec_dapm_routes[] = {/* ADC Routes */{ "ADC", NULL, "ADC Enable" },/* Microphone Routes */{ "Mic Amplifier", NULL, "MIC" },/* DAC Routes */{ "Left DAC", NULL, "DAC Enable" },{ "Right DAC", NULL, "DAC Enable" },/* Left Mixer Routes */{ "Left Mixer", "Left DAC To Mixer Switch", "Left DAC" },{ "Left Mixer", "Right DAC To Mixer Switch", "Right DAC" },{ "Left Mixer", "MICIN To Out Mixer Switch", "Mic Amplifier" },{ "Left Mixer", "LINEIN To Out Mixer Switch", "LINEIN" },{ "Left Mixer", "FMINL To Out Mixer Switch", "FMINL" },/* Right Mixer Routes */{ "Right Mixer", "Left DAC To Mixer Switch", "Left DAC" },{ "Right Mixer", "Right DAC To Mixer Switch", "Right DAC" },{ "Right Mixer", "MICIN To Out Mixer Switch", "Mic Amplifier" },{ "Right Mixer", "LINEIN To Out Mixer Switch", "LINEIN" },{ "Right Mixer", "FMINL To Out Mixer Switch", "FMINL" },/* ADC Mixer Routes */{"ADC Mixer", "MIC To ADC Mixer Switch" ,"Mic Amplifier" },{"ADC Mixer", "LINEIN To ADC Mixer Switch" ,"LINEIN" },{"ADC Mixer", "FMINR To ADC Mixer Switch" ,"FMINR" },{"ADC Mixer", "FMINL To ADC Mixer Switch" ,"FMINL" },{"ADC Mixer", "LEFT Out To ADC Mixer Switch" ,"Left Mixer" },{"ADC Mixer", "RIGHT Out To ADC Mixer Switch" ,"Right Mixer" },{ "ADC" , NULL ,"ADC Mixer" },/* Headphone Routes */{ "Headphone Source Playback Route", "DAC", "Left DAC" },{ "Headphone Source Playback Route", "DAC", "Right DAC" },{ "Headphone Source Playback Route", "Mixer", "Left Mixer" },{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },{ "Headphone Amp", NULL, "Headphone Source Playback Route" },{ "HP", NULL, "Headphone Amp" },{ "HPCOM", NULL, "HPCOM Protection" },
};

上面的结构体数组中,每一个成员代表一个路由线路,里面都是包含三个字符串,第一个是“目的组件”,第三个是“源组件”,中间的那个是连接目的和源的控件,如果是NULL,说明目的和源直连,这里面的控件和组件,都是与上面定义的相对应的。

说到这里,和codec  driver相关的数据结构基本上就介绍完了。

(三)、struct snd_soc_card * (*create_card)(struct device *dev) ,这个接口是在machine层创建一个声卡设备,需要结合codec部分和platform部分的数据,这个最后再做介绍

(四)、剩下的几个字段是和DMA寄存器相关的描述

struct reg_field reg_adc_fifoc; /* used for regmap_field */

unsigned int reg_dac_txdata; /* TX FIFO offset for DMA config */

unsigned int reg_adc_rxdata; /* RX FIFO offset for DMA config */

bool has_reset;

u32 dma_max_burst;

下面再来说一下codec部分的DAI驱动,主要是看一下这个数据结构:

static struct snd_soc_dai_driver suniv_codec_dai = {.name	= "Codec",.ops	= &suniv_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相关的内容,因为在这个ASOC的驱动框架中,大部分工作都是在codec部分来做,而且现在使用的是f1c100s的内置声卡,所以platform部分的配置相对较少。

(一)、先看下组件driver部分,看下这个数据:

static const struct snd_soc_component_driver suniv_codec_component = {.name = "suniv-codec",
};

因为这是一个内置声卡,codec部分的内部寄存器配置已经包含了platform部分的寄存器配置,所以不需要再做其他的配置

(二)、再看一下DAI的driver部分,看下这个数据:

static struct snd_soc_dai_driver dummy_cpu_dai = {.name	= "suniv-codec-cpu-dai",.probe	= suniv_codec_dai_probe,.playback = {.stream_name	= "Playback",.channels_min	= 1,.channels_max	= 2,.rates		= SUNIV_CODEC_RATES,.formats	= SUNIV_CODEC_FORMATS,.sig_bits	= 24,},.capture = {.stream_name	= "Capture",.channels_min	= 1,.channels_max	= 2,.rates 		= SUNIV_CODEC_RATES,.formats 	= SUNIV_CODEC_FORMATS,.sig_bits	= 24,},
};

和codec部分的DAI配置如出一辙。

到这里codec和platform部分基本上已经配置完了,下面需要重点研究一下驱动的probe函数

三、f1c100s的声卡驱动的注册函数,详细的代码如下:

static int suniv_codec_probe(struct platform_device *pdev)
{struct snd_soc_card *card;struct suniv_codec *scodec;const struct suniv_codec_quirks *quirks;struct resource *res;void __iomem *base;int ret;scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);if (!scodec)return -ENOMEM;scodec->dev = &pdev->dev;res = platform_get_resource(pdev, IORESOURCE_MEM, 0);base = devm_ioremap_resource(&pdev->dev, res);if (IS_ERR(base)) {dev_err(&pdev->dev, "Failed to map the registers\n");return PTR_ERR(base);}quirks = of_device_get_match_data(&pdev->dev);if (quirks == NULL) {dev_err(&pdev->dev, "Failed to determine the quirks to use\n");return -ENODEV;}scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,quirks->regmap_config);if (IS_ERR(scodec->regmap)) {dev_err(&pdev->dev, "Failed to create our regmap\n");return PTR_ERR(scodec->regmap);}/* Get the clocks from the DT */scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");if (IS_ERR(scodec->clk_apb)) {dev_err(&pdev->dev, "Failed to get the APB clock\n");return PTR_ERR(scodec->clk_apb);}scodec->clk_module = devm_clk_get(&pdev->dev, "codec");if (IS_ERR(scodec->clk_module)) {dev_err(&pdev->dev, "Failed to get the module clock\n");return PTR_ERR(scodec->clk_module);}if (quirks->has_reset) {scodec->rst = devm_reset_control_get_exclusive(&pdev->dev,NULL);if (IS_ERR(scodec->rst)) {dev_err(&pdev->dev, "Failed to get reset control\n");return PTR_ERR(scodec->rst);}}scodec->gpio_pa = devm_gpiod_get_optional(&pdev->dev, "allwinner,pa",GPIOD_OUT_LOW);if (IS_ERR(scodec->gpio_pa)) {ret = PTR_ERR(scodec->gpio_pa);if (ret != -EPROBE_DEFER)dev_err(&pdev->dev, "Failed to get pa gpio: %d\n", ret);return ret;}/* reg_field setup */scodec->reg_adc_fifoc = devm_regmap_field_alloc(&pdev->dev,scodec->regmap,quirks->reg_adc_fifoc);if (IS_ERR(scodec->reg_adc_fifoc)) {ret = PTR_ERR(scodec->reg_adc_fifoc);dev_err(&pdev->dev, "Failed to create regmap fields: %d\n",ret);return ret;}/* Enable the bus clock */if (clk_prepare_enable(scodec->clk_apb)) {dev_err(&pdev->dev, "Failed to enable the APB clock\n");return -EINVAL;}/* Deassert the reset control */if (scodec->rst) {ret = reset_control_deassert(scodec->rst);if (ret) {dev_err(&pdev->dev,"Failed to deassert the reset control\n");goto err_clk_disable;}}/* DMA configuration for TX FIFO */scodec->playback_dma_data.addr = res->start + quirks->reg_dac_txdata;scodec->playback_dma_data.maxburst = quirks->dma_max_burst;;scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;/* DMA configuration for RX FIFO */scodec->capture_dma_data.addr = res->start + quirks->reg_adc_rxdata;scodec->capture_dma_data.maxburst = quirks->dma_max_burst;;scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;ret = snd_soc_register_codec(&pdev->dev, quirks->codec,&suniv_codec_dai, 1);if (ret) {dev_err(&pdev->dev, "Failed to register our codec\n");goto err_assert_reset;}ret = devm_snd_soc_register_component(&pdev->dev,&suniv_codec_component,&dummy_cpu_dai, 1);if (ret) {dev_err(&pdev->dev, "Failed to register our DAI\n");goto err_unregister_codec;}ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);if (ret) {dev_err(&pdev->dev, "Failed to register against DMAEngine\n");goto err_unregister_codec;}card = quirks->create_card(&pdev->dev);if (IS_ERR(card)) {ret = PTR_ERR(card);dev_err(&pdev->dev, "Failed to create our card\n");goto err_unregister_codec;}snd_soc_card_set_drvdata(card, scodec);ret = snd_soc_register_card(card);if (ret) {dev_err(&pdev->dev, "Failed to register our card\n");goto err_unregister_codec;}return 0;err_unregister_codec:snd_soc_unregister_codec(&pdev->dev);
err_assert_reset:if (scodec->rst)reset_control_assert(scodec->rst);
err_clk_disable:clk_disable_unprepare(scodec->clk_apb);return ret;
}

这个注册函数主要分为以下几个功能:

1、为驱动从内存中获取资源

2、为驱动从设备树中获取相关的硬件配置参数

3、根据获取的资源分别对platform和codec部分进行注册

4、在machine层注册一个声卡

下面具体分析一下这个注册函数,主要有以下函数:

//获取内存
scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL)
//获取硬件的资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0)
//将获取的codec的物理地址做一下映射,获得虚拟地址的基地址
base = devm_ioremap_resource(&pdev->dev, res)
//获取特定平台的数据
quirks = of_device_get_match_data(&pdev->dev)
//从设备树获取总线时钟
scodec->clk_apb = devm_clk_get(&pdev->dev, "apb")
//从设备树获取模块时钟
scodec->clk_module = devm_clk_get(&pdev->dev, "codec")
//注册codec部分
ret = snd_soc_register_codec(&pdev->dev, quirks->codec, &suniv_codec_dai, 1);
//注册platform部分
ret = devm_snd_soc_register_component(&pdev->dev, &suniv_codec_component, &dummy_cpu_dai, 1);
//注册DMA
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0)
//注册声卡
snd_soc_card_set_drvdata(card, scodec);//over!

到这里基本上就可以了

将驱动编译成模块,插入到系统,就可以愉快的使用声卡了

先听首歌,看看效果


http://chatgpt.dhexx.cn/article/qiNN4DxX.shtml

相关文章

【f1c200s/f1c100s】全志f1c200s开发板设计(含原理图和PCB)

f1c200s开发板设计 芯片特征开发板包含资源 开发板如图所示,模仿树莓派取了一个mango-pi的名字,haha~。 硬件主要参考了荔枝派nano和正点原子开发板。经过测试,全部功能正常。 芯片特征 全志F1C200s与F1C100s的区别为F1C200s含有64M SIP RA…

全志F1C100s使用记录:资料索引与基础说明

文章目录 前言资料来源u-boot & linux基础特性启动过程FEL模式电路设计 前言 最早接触到F1C100s是前两年的一个文章,里面使用F1C100s制作了一张可以运行Linux的名片,就是下图这个。下图上实际电路部分就占用角落一点点面积,当时觉得蛮有…

MySql 内连接与外连接 作用和 区别

内连接:指连接结果仅包含符合连接条件的行,参与连接的两个表都应该符合连接条件。 外连接:连接结果不仅包含符合连接条件的行同时也包含自身不符合条件的行。包括左外连接、右外连接和全外连接。 左外连接:左边表数据行全部保留…

php左内连接,内连接和外连接的区别是什么

内连接和外连接的区别:1、连接结果不同,内连接的连接结果仅包含符合连接条件的行,而外连接的连接结果包含了符合连接条件的行;2、注意事项不同,内连接需要注意区分在嵌套查询时使用的any以及all的区别,外连…

Mysql内连接和外连接的区别

内连接 简单来说:就是根据交集的部分来链接到一条完整的数据,以此来完成查询 以两张表相同的部分来链接上两张表的数据,而不是说,内连接查询的就是交集的部分,那样查询出来的不就是表的id信息了或者仅仅是关联的信息 …

mysql内连接和左连接的区别_MySQL连接查询 内连接和外连接的区别

展开全部 内连接:指连接结果仅包含符合连接条件的行,参与连接的两个表都应该符合连接条件。 外连62616964757a686964616fe78988e69d8331333363393638接:连接结果不仅包含符合连接条件的行同时也包含自身不符合条件的行。包括左外连接、右外连…

mysql内外三种连接,mysql内连接和外连接的区别

以下面两张表为例来看一下内连接与外连接的区别: 一、内连接( 最常用 ) 定义:仅将两个表中满足连接条件的行组合起来作为结果集。 关键词:INNER JOINselect * from employees e inner join department d on e.employee_id d.department_id …

Sql 中内连接、外连接、全连接、交叉连接的区别

外连接(out join) 外连接分为外左连接(left outer join)和外右连接(right outer join) 注释:left outer join 与 left join 等价, 一般写成left join right outer join 与 right join等价,一般写成right join 左连接&#xf…

谈谈你对计算机的发展,结合您目前对计算机的认识,请您谈谈对计算机发展趋势的看法。...

计算机从出现至今,经历了机器语言、程序语言、简单操作系统和Linux、Macos、BSD、Windows等现代操作系统四代,运行速度也得到了极大的提升,第四代计算机的运算速度已经达到几十亿次每秒。 计算机也由原来的仅供军事科研使用发展到人人拥有&am…

对计算机科学与技术专业课程的认识,计算机科学与技术专业课程

计算机科学与技术专业课程计算机科学与技术专业课程 二、课程简介 TOP 1.数字逻辑电路: “数字逻辑”是计算机专业本科生的一门主要课程,具有自身的理论体系和很强的实践性。它是计算机组成原理的主要先导课程之一,是计算机应用专业关于计算机系统结构方面的主干课程之一。…

计算机网络技术的专业认识,计算机网络技术课程学习后的自我认识

计算机网络技术课程学习后的自我认识 随着计算机、多媒体、现代通讯网络为代表的信息技术的迅猛发展,信息技术已经渗透到了教育领域,在教育领域中引起了一场深刻的变化。小编是YJBYS小编整理的计算机网络技术课程学习后的自我认识,欢迎阅读 信…

计算机专业认识和规划,计算机科学与技术专业认识与规划

计算机科学与技术专业认识与规划 专业认识与规划专业认识与规划对于这个专业的学生,它要求学生的英语水平,数学水平很高。最主要的是,它的专业课程很多,很复杂,很累人,它需要你付出很多的努力,…

对计算机网络的认识论文800,对计算机的认识论文

为毕业生写对计算机的认识论文提供对计算机的认识论文范文参考,涵盖硕士、大学本科毕业论文范文和职称论文范文,包括论文选题、开题报告、文献综述、任务书、参考文献等,是优秀免费对计算机的认识论文网站。 对计算机软件属性和保护方式的认识 摘要&…

JS取整,取余

1、取整 // 丢弃小数部分,保留整数部分 parseInt(5/2)  // 2 2.向上取整 // 向上取整,有小数就整数部分加1 Math.ceil(5/2)  // 3 3.向下取整 // 向下取整,丢弃小数部分 Math.floor(5/2)  // 2 4四舍五入 // 四舍五入 Math.round(5/2)  // 3 5 取余 // 取余 6…

JS中的求余和求模

在JS中有两个操作符,求余和求模,它们本质上并没有区别,但仍有一些差异,今天就给大家区分一下它们 求余 符号:% 写法:a % b 求余:a % b ,表示为a rem b,相当于 a - n * …

js数据取整,取余,保留小数,数据脱敏等的一些处理方式

一个经典的解决四舍五入问题后js保留两位小数的方法: 1.四舍五入保留2位小数(若第二位小数为0,则保留一位小数) function keepTwoDecimal(num) {var result parseFloat(num);if (isNaN(result)) {alert(传递参数错误&#xff0…

js中小数计算时精度问题

js中的number为双精度浮点型,计算时需要先进行进制转换,将十进制换成二进制,而进制转换时,小数就有可能出现精度问题了,原因如下 整数转二进制:除二取余法(没有问题) 4 除以 2 取余…

JavaScript数学运算(取整,取余和取模)

来源 | https://www.fly63.com 这篇文章主要介绍js取整、取余和取模的实现方法和它们之间的区别,以及Math对象的常用方法函数介绍,有需要的朋友可以参考下。 取整 在项目开发过程中,有时要对小数取整操作,JavaScript也提供了4种方…

JavaScript数字取整、取余总结

一、取整: 取整 //丢弃小数部分,保留整数部分 parseInt(85.5)//85向上取整 //向上取整,有余数或者小数,整数位1 Math.ceil(7/3) //3 Math.ceil(85.6) //86 Math.ceil(72.1) //73向下取整 //向下取整,有余数或者小数…

QuickSort 拿下!

剑指 Offer 45. 把数组排成最小的数   输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 由于这一题的解题思路是:首先将数组转换为字符数组,然后根据规则对其进行排序&#xff…