正点原子嵌入式linux驱动开发——Linux PWM驱动

news2024/9/28 13:16:16

PWM是很常用到功能,可以通过PWM来控制电机速度,也可以使用PWM来控制LCD的背光亮度。本章就来学习一下如何在Linux下进行PWM驱动开发。

PWM驱动解析

不在介绍PWM是什么了,直接进入使用。

给LCD的背光引脚输入一个PWM信号,这样就可以通过调整占空比的方式来调整LCD背光亮度了。提高占空比就会提高背光亮度,降低占空比就会降低背光亮度,重点就在于PWM信号的产生和占空比的控制。

设备树下PWM控制器节点

定时器节点

STM32MP157有很多路PWM,这些PWM都是由定时器产生的:

  • TIM1/TIM8:这2个是16位高级定时器,主要用于电机控制。这两个定时器支持PWM型号,每个定时器支持4通道PWM信号。
  • TIM2/TIM3/TIM4/TIM5:这4个是通用定时器,TIM3/TIM4是16位定时器,TIM2/TIM5是32位定时器。这4个定时器也支持PWM输出,每个定时器支持4通道PWM信号。
  • TIM12/TIM13/TIM14:这3个都是16 位的通用定时器,TIM12支持2通道的PWM信号,TIM13/TIM14这两个定时器每个只支持1个通道的PWM信号。
  • TIM15/TIM16/TIM17:这3个也都是16位的通用定时器,TIM15支持2通道的PWM信号,TIM16/TIM17每个定时器支持1通道的PWM信号。

可以看出,STM32MP157的PWM通道非常多,不同的PWM通道功能也不同,可以
根据实际情况选择合适的PWM通道。本节使用PA10这个引脚来实现PWM功能,注意!PA10这个引脚被用作USB的ID引脚,如果所使用的开发板使用了PA10作为USB OTG的ID引脚,那么在做本实验的时候开发板的USB OTG接口不能连接到电脑上!正点原子STM32MP157开发板的USB接口采用TypeC接口,因此没有用到PA10作为ID引脚

打开STM32MP157的数据手册,可以看到PA10可以作为TIM1的通道3

TIM1简介

这里其实可以去看裸机开发的笔记,对TIM1这个高级定时器的介绍,我这边只关注Linux驱动部分。

TIM1设备节点

接下来看一下TIM1的设备树,STM32定时器设备树绑定信息文档为:Documentation/devicetree/bindings/mfd/stm32-timers.txt,简单总结一下定时器节点信息。

1、必须的参数

  • compatible:必须是“st,stm32-timers”。
  • reg:定时器控制器物理寄存器基地址,对于TIM1来说,这个地址为0x44000000,这个可以在STM32MP157的数据手册上找到。
  • clock-names:时钟源名字,设置为“int”。
  • clocks:时钟源。

2、可选的参数

  • resets:复位句柄,用来复位定时器控制器,可以参考文档reset/st,stm32-rcc.txt。
  • dmas:DMA通道,最多7通道的DMA。
  • dma-names:DMA名字列表,必须和“dmas”属性匹配,可选的名字有“ch1”、“ch2”、“ch3”、“ch4”、“up”、 “trig”、“com”。

3、可选的子节点

STM32定时器有多种功能,比如计时、PWM、计数器等,不同的功能需要用不同的子节点来表示,可选子节点有三种,分别对应不同的功能:

  • pwm:pwm子节点描述定时器的PWM功能,关于PWM的详细信息请参考绑定文档pwm/pwm-stm32.txt。
  • timer: timer子节点描述定时器的定时功能,定时相关信息请参考绑定文档iio/timer/stm32-timer-trigger.txt。
  • counter: counter子节点描述定时器的计数功能,相关信息请参考绑定文档counter/stm32-timer-cnt.txt。

了解完定时器的绑定文档以后,来看一下STM32MP157实际的定时器节点,打开stm32mp151.dtsi,找到名为“timers1”的设备节点,这个就是TIM1定时器节点,内容如下:
TIM1节点
第19-23行,TIM1的pwm功能子节点,这个是本小节重点关注的。

PWM设备子节点

通过上面对定时器绑定文档的讲解,知道PWM作为定时器的子节点,这里就来看一下PWM子节点绑定文档:Documentation/devicetree/bindings/pwm/pwm-stm32.txt,简单总结一下PWM子节点属性信息:

  • compatible:必须为“st,stm32-pwm”。
  • pinctrl-names:设置为“default”,也可以添加“sleep”,这样当进入低功耗的时候PWM引脚引入sleep模式。
  • pinctrl-n:PWM引脚pinctrl句柄,用来指定PWM信号输出引脚。
  • #pwm-cells:应该设置为3。

STM32MP157的PWM节点的compatible属性为“st,stm32-pwm”,可以在Linux内核源码中搜索这个字符串找到PWM驱动文件,这个文件为:drivers/pwm/pwm-stm32.c

PWM子系统

Linux内核提供了PWM子系统框架,编写PWM驱动的时候一定要符合这个框架。PWM子系统的核心是pwm_chip结构体,定义在文件include/linux/pwm.h中,定义如下:
pwm_chip结构体
第292行,pwm_ops结构体就是PWM外设的各种操作函数集合,编写PWM外设驱动的时候需要开发人员实现。pwm_ops结构体也定义在pwm.h头文件中,定义如下:
pwm_ops结构体
pwm_ops中的这些函数不一定全部实现,但是配置PWM的函数必须实现,比如apply或者config。第264行的apply函数是最新的PWM配置函数,通过此函数来配置PWM的周期以及占空比,老的内核里面会使用第271行的config函数来配置PWM。其中第271-276行的config、set_polarity、enable和disable都是老版本内核所使用的函数。

PWM子系统驱动的核心初始化pwm_chip结构体,然后向内核注册初始化完成以后的pwm_chip。这里就要用到pwmchip_add函数,此函数定义在drivers/pwm/core.c文件中,函数原型如下:

int pwmchip_add(struct pwm_chip *chip)

函数参数和返回值含义如下:

  • chip:要向内核注册的pwm_chip。
  • 返回值:0,成功;负数,失败。

卸载PWM驱动的时候需要将前面注册的pwm_chip从内核移除掉,这里要用到
pwmchip_remove函数
,函数原型如下:

int pwmchip_remove(struct pwm_chip *chip) 

函数参数和返回值含义如下:

  • chip:要移除的pwm_chip。
  • 返回值:0,成功;负数,失败。

PWM驱动源码解析

简单分析一下Linux内核自带的STM32MP157 PWM驱动,驱动文件是pwm-stm32.c这个文件。打开这个文件,可以看到,这是一个标准的平台设备驱动文件,有如下所示:
STM32MP157 PWM平台驱动
第2行,当设备树PWM节点的compatible属性值为“st,stm32-pwm”的话就会匹配此驱动。

第14行,当设备树节点和驱动匹配以后stm32_pwm_probe函数就会执行。

在看stm32_pwm_probe函数之前先来看下stm32_pwm结构体,这个结构体是ST官方创建的STM32 PWM结构体,这个结构体会贯穿整个PWM驱动。stm32_pwm结构体定义在pwm-stm32.c文件中,结构体内容如下:
stm32_pwm结构体
重点看一下第2行,这是一个pwm_chip结构体成员变量chip,前面说了PWM子系统的核心就是pwm_chip。

stm32_pwm_probe函数如下(有缩减):

示例代码39.1.3.3 stm32_pwm_probe函数 
608 static int stm32_pwm_probe(struct platform_device *pdev) 
609 { 
610     struct device *dev = &pdev->dev; 
611     struct device_node *np = dev->of_node; 
612     struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); 
613     struct stm32_pwm *priv; 
614     int ret; 
615 
616     priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 
617     if (!priv) 
618         return -ENOMEM; 
619 
620     mutex_init(&priv->lock); 
621     priv->regmap = ddata->regmap; 
622     priv->clk = ddata->clk; 
623     priv->max_arr = ddata->max_arr; 
624     priv->chip.of_xlate = of_pwm_xlate_with_flags; 
625     priv->chip.of_pwm_n_cells = 3; 
626
627     if (!priv->regmap || !priv->clk) 
628         return -EINVAL; 
629 
630     ret = stm32_pwm_probe_breakinputs(priv, np); 
631     if (ret) 
632         return ret; 
633 
634     stm32_pwm_detect_complementary(priv); 
635 
636     priv->chip.base = -1; 
637     priv->chip.dev = dev; 
638     priv->chip.ops = &stm32pwm_ops; 
639     priv->chip.npwm = stm32_pwm_detect_channels(priv); 
640 
641     ret = pwmchip_add(&priv->chip); 
642     if (ret < 0) 
643         return ret; 
644 
645     platform_set_drvdata(pdev, priv); 
646 
647     return 0; 
648

第616行,priv是一个stm32_pwm类型的结构体指针变量,这里为其申请内存stm32_pwm结构体有个重要的成员变量chip,chip是pwm_chip类型的。所以这一行就引出了PWM子系统核心部件pwm_chip,稍后的重点就是初始化chip。

第621-625行,初始化priv的各个成员变量,第624和625还初始化了pwm_chip的of_xlate和of_pwm_n_cells这两个成员变量。

第630行,调用stm32_pwm_probe_breakinputs函数来读取“st,breakinput”属性,设置break输入,本章例程用不到。

第634行,调用stm32_pwm_detect_complementary函数来检测是否使能TIM1的互补输出功能。

第636-639行,重点,初始化pwm_chip的各个成员变量,第638行设置pwm_chip的ops函数为stm32pwm_ops,stm32pwm_ops里面包含了PWM的具体操作,稍后重点分析。第639行设置pwm_chip的npwm,也就是设置当前打开多少路PWM。这里直接使用stm32_pwm_detect_channels函数来读取TIM1的CCER寄存器,CCER寄存器的CC1E(bit0)、CC2E(bit4)、CC3E(bit8)和CC4E(bit12)这4个位用于开启TIM1的4通道PWM,如果为1就表示对应的PWM通道打开。所以stm32_pwm_detect_channels函数就会直接读取这4个位来判断对应的PWM通道是否打开。

重点来看一下stm32pwm_ops,定义如下:
stm32pwm_ops操作集合
第487行stm32_pwm_apply_locked就是最终的PWM设置函数,在应用中设置的PWM频率和占空比最终就是由stm32_pwm_apply_locked函数来完成的,此函数会最终操作STM32相关的寄存器。

stm32_pwm_apply_locked函数源码如下:
stm32_pwm_apply_locked函数
第478行,加互斥锁,防止竞争的产生。一次只有一个应用可以设置PWM。

第479行,调用stm32_pwm_apply函数来设置 PWM

stm32_pwm_apply函数内容如下:
stm32_pwm_apply函数
第453行,在设置PWM之前,先调用stm32_pwm_disable函数关闭PWM。

第458行,调用stm32_pwm_set_polarity函数设置指定PWM通道的极性。

第460行,调用stm32_pwm_config来设置PWM的频率以及占空比。

第465行,PWM设置完成以后调用stm32_pwm_enable函数使能PWM。

stm32_pwm_config函数内容如下:

示例代码39.1.3.7 stm32_pwm_config函数 
322 static int stm32_pwm_config(struct stm32_pwm *priv, int ch, 
323                             int duty_ns, int period_ns) 
324 { 
325     unsigned long long prd, div, dty; 
326     unsigned int prescaler = 0; 
327     u32 ccmr, mask, shift; 
328 
329     /* Period and prescaler values depends on clock rate */ 
330     div = (unsigned long long)clk_get_rate(priv->clk) * period_ns; 
331 
332     do_div(div, NSEC_PER_SEC); 
333     prd = div; 
334 
335     while (div > priv->max_arr) { 
336         prescaler++; 
337         div = prd; 
338         do_div(div, prescaler + 1); 
339     } 
340 
341     prd = div; 
342 
343     if (prescaler > MAX_TIM_PSC) 
344         return -EINVAL; 
345 
346     /* 
347      * All channels share the same prescaler and counter so when two
348      * channels are active at the same time we can't change them 
349      */ 
350     if (active_channels(priv) & ~(1 << ch * 4)) { 
351         u32 psc, arr; 
352 
353         regmap_read(priv->regmap, TIM_PSC, &psc); 
354         regmap_read(priv->regmap, TIM_ARR, &arr); 
355 
356         if ((psc != prescaler) || (arr != prd - 1)) 
357             return -EBUSY; 
358     } 
359 
360     regmap_write(priv->regmap, TIM_PSC, prescaler); 
361     regmap_write(priv->regmap, TIM_ARR, prd - 1); 
362     regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE); 
363 
364     /* Calculate the duty cycles */ 
365     dty = prd * duty_ns; 
366     do_div(dty, period_ns); 
367 
368     write_ccrx(priv, ch, dty); 
369 
370     /* Configure output mode */ 
371     shift = (ch & 0x1) * CCMR_CHANNEL_SHIFT; 
372     ccmr = (TIM_CCMR_PE | TIM_CCMR_M1) << shift; 
373     mask = CCMR_CHANNEL_MASK << shift; 
374 
375     if (ch < 2) 
376         regmap_update_bits(priv->regmap, TIM_CCMR1, mask, ccmr); 
377     else 
378         regmap_update_bits(priv->regmap, TIM_CCMR2, mask, ccmr); 
379 
380     regmap_update_bits(priv->regmap, TIM_BDTR, TIM_BDTR_MOE, TIM_BDTR_MOE); 
381 
382     return 0; 
383 }

PWM的设置主要就是两方面:频率和占空比。第330~362行都是设置PWM频率的,函数参数period_ns为周期值,也就是PWM的频率。TIM的PSC寄存器用来设置定时器分频值,当TIM时钟源确定以后,设置PSC分频值即可得到TIM最终的时钟频率。TIM的ARR寄存器是自动加载寄存器,将TIM设置为向下计数器,定时器开启以后每个时钟周期,计数器减一,直到计数器减为0。这个时候再将ARR里面的值加载到计数器里面,计数器就会重新开始倒计时,如此一直重复。因此PSC和ARR这两个寄存器就决定了PWM的周期值要注意!由于一个定时器有4通道的 PWM,而这4路PWM只能设置成同一个周期,如果想要多路周期不同的PWM信号,那就要使用多个不同的TIM!

第365-380行,设置PWM的占空比,参数duty_ns表示占空比。一个定时器下的4路PWM可以设置不同的占空比,相当于一个定时器下的4路PWM信号,周期是一样的,但是占空比可以不同。占空比的设计原理比较简单,前面我们已经知道当定时器时钟频率确定以后(PSC分频值不变),ARR寄存器里面的值就决定了PWM周期,这个数值就叫比较值,改变比较值就可以改变PWM的占空比。STM32MP157一个定时器有4路PWM通道,每个通道都有个用来存放比较值的寄存器,因此一共有4个寄存器CCR1-CCR4,这4个寄存器就叫做比较寄存器。所以第365、366行就是根据参数duty_ns算出对应的CCRx(x=1~4)寄存器对应的值,然后在368行通过write_ccrx函数将相应的值写入到对应的
CCRx寄存器里面。

第371-378行是设置PWM输出模式,通道1和通道2使用CCMR1寄存器,通道3和通道4使用CCMR2寄存器。最后的380行设置BDTR寄存器,这个寄存器是break和死区控制相关的,本章用不到。

至此,STM32MP157的PWM驱动就分析完了。

PWM驱动编写

修改设备树

PWM驱动就不需要再编写了,ST已经写好了,前面也已经详细的分析过这个驱动源码了。在实际使用的时候只需要修改设备树即可(这个可以类比裸机开发用HAL库),STM32MP157开发板上的JP1排针引出了 PA10这个引脚,如下图所示:
PA10引脚
PA10可以作为TIM1的通道3的PWM输出引脚,所以需要在设备树里面添加PA10的引脚信息以及TIM1通道3的PWM信息

添加PA10引脚信息

打开stm32mp15-pinctrl.dtsi文件,在iomuxc节点下添加GPIO1_IO04的引脚信息,如下所示:

示例代码39.2.1.1 TIM1 PWM引脚信息 
1  pwm1_pins_a: pwm1-0 { 
2      pins { 
3          pinmux = <STM32_PINMUX('E', 9, AF1)>, /* TIM1_CH1 */ 
4                   <STM32_PINMUX('E', 11, AF1)>, /* TIM1_CH2 */ 
5                   <STM32_PINMUX('E', 14, AF1)>; /* TIM1_CH4 */ 
6          bias-pull-down; 
7          drive-push-pull; 
8          slew-rate = <0>; 
9      }; 
10 }; 
11 
12 pwm1_sleep_pins_a: pwm1-sleep-0 { 
13     pins { 
14         pinmux = <STM32_PINMUX('E', 9, ANALOG)>, /* TIM1_CH1 */ 
15                  <STM32_PINMUX('E', 11, ANALOG)>, /* TIM1_CH2 */ 
16                  <STM32_PINMUX('E', 14, ANALOG)>; /* TIM1_CH4 */ 
17     }; 
18 };

可以看出ST官方已经设置好了TIM1的CH1、CH2和CH4这三个通道的引脚配置,但是这里只需要CH3,因此将示例代码29.2.1.1改成如下所示:

示例代码39.2.1.2 PA10引脚配置 
1  pwm1_pins_a: pwm1-0 { 
2      pins { 
3          pinmux = <STM32_PINMUX('A', 10, AF1)>; /* TIM1_CH3 */ 
4          bias-pull-down; 
5          drive-push-pull; 
6          slew-rate = <0>; 
7      }; 
8  }; 
9 
10 pwm1_sleep_pins_a: pwm1-sleep-0 { 
11     pins { 
12         pinmux = <STM32_PINMUX('A', 10, ANALOG)>; /* TIM1_CH3 */ 
13     }; 
14 };

示例代码39.2.1.2中仅仅将PA10复用为TIM1的CH3,一定要根据自己所使用的板子硬件来配置引脚。

向timer1节点追加信息

stm32mp151.dtsi文件中已经有了“timers1”节点,但是这个节点默认是disable的,而且还不能直接使用。需要在stm32mp157d-atk.dts文件中向timers1节点追加一些内容,在stm32mp157d-atk.dts文件中加入如下所示内容:

示例代码39.2.1.3 向timers1添加的内容 
1  &timers1 { 
2      status = "okay"; 
3      /* spare all DMA channels since they are not needed for PWM output */ 
4      /delete-property/dmas; 
5      /delete-property/dma-names; 
6      pwm1: pwm { 
7          pinctrl-0 = <&pwm1_pins_a>; 
8          pinctrl-1 = <&pwm1_sleep_pins_a>; 
9          pinctrl-names = "default", "sleep"; 
10         #pwm-cells = <2>; 
11         status = "okay"; 
12     }; 
13 };

第 4、5行,关闭DMA功能,因为PWM输出不需要DMA。

第7行,pinctrl-0属性指定TIM1的CH3所使用的输出引脚对应的pinctrl节点,这里设置
为示例代码39.2.1.2中的pwm1_pins_a。

屏蔽其他复用的IO

检查一下设备树中有没有其他外设用到PA10,如果有的话需要屏蔽掉!注意,不能只屏蔽掉PA10的 pinctrl配置信息,也要搜索一下“gpioa 10”,看看有没有哪里用到,用到的话也要屏蔽掉

设备树修改完成后重新编译设备树,然后使用新的设备树启动系统。

使能PWM驱动

ST官方的Linux内核已经默认使能了PWM驱动,所以不需要修改,但是为了学习,还是需要知道怎么使能。打开Linux内核配置界面,按照如下路径找到配置项:

-> Device Drivers
-> Pulse-Width Modulation (PWM) Support
-> <*> STMicroelectronics STM32 PWM //选中

配置如下图所示:
PWM配置项

PWM驱动测试

确定TIM1对应的pwmchipX问价

使用新的设备树启动系统,然后将开发板上的PA10引脚连接到示波器上,通过示波器来查看PWM波形图。可以直接在用户层来配置PWM,进入目录/sys/class/pwm中,如下图所示:
TIM1对应的PWM
注意!上图中有个pwmchip0,但是并不知道这个pwmchip0是否为TIM1对应的文件。可以通过查看pwmchip0对应的地址是否和TIM1定时器寄存器起始地址是否一致来确定其是否属于TIM1。进入到pwmchip0目录下,会打印出其路径:
pwmchip0路径名称
从上图可以看出pwmchip0对应的定时器寄存器起始地址为0X44000000,根据示例代码39.1.1.1中的timers1节点,可以知道TIM1这个定时器的寄存器起始地址就是0X44000000。因此,pwmchip0就是TIM1对应的文件。

为什么要用这么复杂的方式来确定定时器对应的pwmchip文件呢?因为当STM32MP157开启多个定时器的PWM功能以后,其pwmchip文件就会变!

调出pwmchip0的pwm2子目录

pwmchip0是整个TIM1的总目录,而TIM1有4路PWM,每路都可以独立打开或关闭。CH1-CH4对应的编号为0~3,因此打开TIM1的CH3输入如下命令:

echo 2 > /sys/class/pwm/pwmchip0/export

上述命令中2就是TIM1_CH3,如果要打开TIM1的CH1,那就是0。执行完成会在pwmchip0目录下生成一个名为“pwm2”的子目录,如下图所示:
新生成的pwm2子目录

设置PWM频率

注意,这里设置的是周期值,单位为ns,比如20KHz频率的周期就是50000ns,输入如下命令:

echo 50000 > /sys/class/pwm/pwmchip0/pwm2/period

设置PWM的占空比

这里不能直接设置占空比,而是设置的一个周期的ON时间,也就是高电平时间,比如20KHz频率下20%占空比的ON时间就是10000,输入如下命令:

echo 10000 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle

使能TIM1 CH3

一定要先设置频率和波特率,最后在开启定时器,否则会提示参数错误!输入如下命令使能TIM1的通道3这路PWM:

echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable

设置完成使用示波器查看波形是否正确,正确的话如下图所示:
PWM波形图
从上图可以看出,此时PWM频率为20KHz,占空比为20%,与设置的一致。如果要修改频率或者占空比的话一定要注意这两者时间值,比如20KHz频率的周期值为50000ns,那么在调整占空比的时候ON时间就不能设置大于50000,否则就会提示参数无效。

极性反转

前面也可以修改PWM的极性,上面设置的PWM占空比为20%,只需要修改极性就可以将占空比变为80%。向/pwmchip0/pwm2/polarity文件写入“inversed”即可反转极性,命令如下:

echo "inversed" > /sys/class/pwm/pwmchip0/pwm2/polarity

极性反转以后占空比就变为了80%,如果要恢复回原来的极性,向/pwmchip0/pwm2/polarity文件写入“normal”即可,命令如下:

echo "normal" > /sys/class/pwm/pwmchip0/pwm2/polarity

总结

Linux内核直接修改PWM,只需要在pinctrl和设备树中添加相应的内容即可。

在stm32mp15-pinctrl.dtsi中,在iomuxc节点下,找到本次实验中使用的TIM1的复用,就是pwm1_pins_a和pwm1_sleep_pins_a节点,添加电气属性内容。

在stm32mp157d-atk.dts中,向&timer1追加内容,把status置为“okay”使能,然后把pinctrl加入即可。

最后就可以通过在/sys/class/pwm里面对应的pwmchip文件中,在./export通过“echo”命令打开对应的PWM输出,在./period设置频率和在./duty_cycle设置占空比;最后在./enable中“echo 1”打开PWM输出。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1130292.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Node编写更新用户信息接口

目录 前言 定义路由和处理函数 验证表单数据 实现更新用户基本信息的功能 前言 继前面几篇文章&#xff0c;本文介绍如何编写更新用户信息接口 定义路由和处理函数 路由 // 更新用户信息接口 router.post(/userinfo, userinfo_handler.updateUserinfo) 处理函数 // 导…

Netty实战-实现自己的通讯框架

通信框架功能设计 功能描述 通信框架承载了业务内部各模块之间的消息交互和服务调用&#xff0c;它的主要功能如下&#xff1a; 基于 Netty 的 NIO 通信框架&#xff0c;提供高性能的异步通信能力&#xff1b;提供消息的编解码框架&#xff0c;可以实现 POJO 的序列化和反序…

Kmeans算法实现目标客户聚类分析

文章目录 一、Kmeans简介二、数据集描述三、实现方法一、Kmeans简介 Kmeans是聚类算法中较为简单的一种,简单但实用,有如下优势和缺点: 优势 算法简单,便于使用(算法仅需要考虑一个分类数量K即可) 适合常规数据集(最好是线性可分的数据集) 适合 不适合 缺点 K值难以确…

【COMP329 LEC4 Locomotion and Kinematics】

Only for the Test 1 which include 4.2 4.3 4.4 Locomotion and Kinematics 运动和运动学 (4.2) Part 2: Wheeled Motion 1. Wheeled Robots a. 省略控制双腿需要的计算复杂度 b. 只限于easy terrain &#xff08;地形&#xff09; c. 不平坦uneven 不规则irregular 的地形需要…

STM32-程序占用内存大小计算

STM32中程序占用内存容量 Keil MDK下Code, RO-data,RW-data,ZI-data这几个段: Code存储程序代码。 RO-data存储const常量和指令。 RW-data存储初始化值不为0的全局变量。 ZI-data存储未初始化的全局变量或初始化值为0的全局变量。 占用的FlashCode RO Data RW Data; 运行消…

Go语言用Resty库编写的音频爬虫代码

以下是一个使用Resty库的Go语言下载器程序&#xff0c;用于从facebook下载音频。此程序使用了duoip/get_proxy的代码。 package mainimport ("fmt""github.com/john-nguyen09/resty""io/ioutil""net/http" )func main() {// 设置爬虫i…

互联网金融 个人身份识别技术要求

文章目录 术语缩略语个人身份识别技术框架框架与各组部分的作用个人身份识别实现的主要功能 个人身份识别凭据技术要求概述记忆凭据类静态口令生成要求使用要求设备要求及安全要求 预设问题回答生成要求使用要求 OPT令牌生成要求使用要求安全要求 数字证书无硬介质证书生成要求…

kvm webvirtcloud 如何添加直通物理机的 USB 启动U盘

第一步&#xff1a;查看USB设备ID 在物理机上输入 lsusb 命令 rootubuntu:/media/usb1# lsusb Bus 002 Device 002: ID 0781:5581 SanDisk Corp. Ultra Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub Bus 001 Device 004: ID 0424:2514 Microchip Technolo…

【力扣】x (-x) 与 x (x - 1)

最近刷了很多位运算的题&#xff0c;从一开始的死记硬背x & (-x) 与 x & (x - 1)的含义&#xff0c;到现在彻底弄懂&#xff0c;花了很多时间理解。 前提知识&#xff1a; 内存中的计算都是按照补码进行计算的。正数的原反补相同&#xff0c;负数的补码等于原码取反加…

FOC系列(一)----DRV8301芯片的学习

一、 写在前面 从今年四五月份一直就想玩个无刷直流电机&#xff08;BLDC&#xff09;&#xff0c;但是碍于一直没时间。其实很早就做出来了测试板的控制板&#xff0c;可以当做开发板使用&#xff0c;考虑到成本问题&#xff0c;最后选用STM32F103CBT6芯片&#xff0c;下面是很…

边缘计算发生了什么?

边缘计算(Edge computing)成为一种革命性工具&#xff0c;可以满足日益增长的实时数据处理需求。通过在网络边缘&#xff08;更靠近数据生成位置&#xff09;进行数据处理&#xff0c;边缘计算可显着减少延迟和带宽使用。 这是我们多年来一直被告知的故事&#xff0c;但随着生…

听GPT 讲Rust源代码--library/std(2)

File: rust/library/std/src/sys_common/wtf8.rs 在Rust源代码中&#xff0c;rust/library/std/src/sys_common/wtf8.rs这个文件的作用是实现了UTF-8编码和宽字符编码之间的转换&#xff0c;以及提供了一些处理和操作UTF-8编码的工具函数。 下面对这几个结构体进行一一介绍&…

封装一个vue3 Toast组件,支持组件和api调用

先来看一段代码 components/toast/index.vue <template><div v-if"isShow" class"toast">{{msg}}</div> </template><script setup> import { ref, watch } from vue const props defineProps({show: {type: Boolean,def…

“/usr/bin/env: ‘python’: No such file or directory“:Linux中python口令无效,python3有效

文章目录 1. 问题的发现2. /usr/bin 目录里跟python有关的链接2.1 使用ll查看文件的链接2.2 分析python口令不能使用的原因 3 参考文章《linux 升级默认python 环境为python3》4 修改命令为python 1. 问题的发现 我在安装scons时&#xff0c;发现python口令不能直接用&#xf…

Unity之ShaderGraph如何实现水波纹效果

前言 今天我们来实现一个水波纹的效果 如下图所示: 主要节点 Normalize :返回输入 In 的标准化值。输出矢量与输入 In 具有相同的方向,但长度为 1。 Length:返回输入 In 的长度。这也称为大小 (magnitude)。矢量的长度是使用毕达哥拉斯定理 (Pythagorean Theorum) 计算…

javaEE -7(网络原理初识 --- 7000字)

一&#xff1a;网络初识 计算机的独立模式是指多台计算机在网络中相互独立运行&#xff0c;彼此之间不共享资源或信息。在早期&#xff0c;计算机主要采用独立模式&#xff0c;每台计算机都拥有自己的操作系统、应用程序和数据&#xff0c;它们之间没有直接的连接或通信。 在…

笔记/日记应用 memos

memos &#xff0c;一款很惊艳的笔记应用&#xff0c;UI很漂亮&#xff0c;交互体验也很好&#xff0c;还有其他的小伙伴基于memos开发了不同平台的客户端。 图源-Gihub页 可以说这个是私人笔记系统的天花板&#xff0c;推荐给大家。

从 Python 程序中运行 PowerShell 脚本

Windows PowerShell 由数十个内置 cmdlet 组成&#xff0c;它们提供了丰富的功能集。 其中一些功能是独一无二的&#xff0c;只能通过 PowerShell 获得&#xff1b; 因此&#xff0c;如果我们能够在 Python 等其他编程语言中使用 PowerShell 脚本&#xff0c;那将非常有用。 本…

【分布式】: 幂等性和实现方式

【分布式】: 幂等性和实现方式 幂等&#xff08;idempotent、idempotence&#xff09;是一个数学与计算机学概念&#xff0c; 常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数&#xff0c;或幂等方法&#xff0c;是…

图像处理中底层、高层特征、上下文信息理解

1.图像的语义信息: 图像的语义分为视觉层、对象层和概念层。 视觉层即通常所理解的底层&#xff0c;即颜色、纹理和形状等等&#xff0c;这些特征都被称为底层特征语义&#xff1b; 对象层即中间层&#xff0c;通常包含了属性特征等&#xff0c;就是某一对象在某一时刻的状态&a…