本文介绍如何在HDF PWM框架中开发stm32mp1的pwm外设。
. r) s3 a4 _9 q2 W
& e( A# e' w: e* g3 X3 B+ A1 ?0 Lstm32mp1的大部分外设可以使用st提供的HAL库来开发。hal库是st官网为所有st芯片提供的sdk包,使开发者可以免去操作寄存器的操作,直接使用库函数完成芯片外设的配置。" o4 c$ q: O2 Y6 Q) t; Z: B
: [1 ^& i, t8 t, g/ @$ N综上所述,可以将整个开发步骤分三步走:
0 |1 o3 h- S) D, t. o% f4 g" T4 W, j8 \3 W; o9 q0 U$ g O
1、导入STM32HAL库
9 {! \7 A0 ?/ X- d' o* [0 h. U* Y: Q
2、使能HDF PWM驱动
, S; c4 K* w# q
! W6 v$ j5 e; A3、编写PWM驱动- C( ^5 E$ T% O
! o% Y. i+ m) g! J$ p- |
1、导入stm32mp1 HAL库文件
5 h! v8 O/ S: L# d/ v: O下载HAL库完成后,将HAL库中的以下文件添加到bearpi-micro\device\st\drivers\stm32mp1xx_hal\STM32MP1xx_HAL_Driver的Inc目录中。( G& Q) x0 ~! p* j, D+ d" k' {
stm32mp1xx_hal_tim.h
$ C+ n: \6 m) z, V/ J& Lstm32mp1xx_hal_tim_ex.h
) i, B, O( J7 W w. x) [7 F: I# S/ n( Astm32mp1xx_hal_dma.h
4 K; _' j9 k6 Kstm32mp1xx_hal_dma_ex.h
: o! U# m2 R- F" S, v) E将 以下文件添加到bearpi-micro\device\st\drivers\stm32mp1xx_hal\STM32MP1xx_HAL_Driver的Src目录中:7 c z( q! f0 @8 p
stm32mp1xx_hal_tim.c
# F- D, }6 H5 q% ~+ [% Y" fstm32mp1xx_hal_tim_ex.c
1 L; E G! A4 H6 |: ostm32mp1xx_hal_dma.c. t* f( I5 @! k
stm32mp1xx_hal_dma_ex.c4 D4 ^( ?! x! v; }3 m3 v9 L
7 g6 I2 A$ A) e. p+ l4 V% G- z1 z, l然后将四个源文件加入编译构建体系:编辑bearpi-micro\device\st\drivers\stm32mp1xx_hal\STM32MP1xx_HAL_Driver\BUILD.gn ? ?9 D$ k2 `
' v8 y7 I8 S) |( N$ m
在sources添加如下两项:
2 |5 e& L9 @$ u* z5 ~0 R3 W+ R3 |7 P# H8 a1 p# V; @7 t" O! b: t
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_tim_ex.c",
/ W/ j9 ?. ~% y9 C0 X) G - "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_tim.c",
* r3 m8 |' M: `3 V# Y - "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_dma_ex.c",
" S V- K7 j. P! O5 a, _ - "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_dma.c",
复制代码 7 |% f- y, H$ f
添加后如下所示,这样编译系统就会编译这两个文件:
$ O1 ?6 a$ P A8 t/ `7 M' T$ t. F: G9 E& w
- import("//drivers/adapter/khdf/liteos/hdf.gni")8 v" j9 U3 P2 M. ~5 L# g; W2 ^
- module_name = "hdf_stm32mp1xx_hal", T$ k& C; b9 W1 e2 p
- hdf_driver(module_name) {8 x2 ^( B) q: X) A/ `; s
- sources = [+ g( f& V4 c6 `, l
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_tim_ex.c",
0 h/ |* X$ i7 v2 N - "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_tim.c",+ i% V5 u1 }# Z% x- P* j
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_dma_ex.c",9 f3 t [# S7 b5 }
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_dma.c",
% V& J4 P! N$ S: q+ v7 h) t - "STM32MP1xx_HAL_Driver/Src/system_stm32mp1xx.c",- i6 a2 ~7 _7 u7 |
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal.c",/ A, m' W& ?' F" N g
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_gpio.c",1 k0 ]9 D0 `# v7 ] d- c
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_i2c.c",6 b9 |+ a' J6 K( v+ c
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_exti.c",
# }' O6 o; f9 i I! J7 r - "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_ltdc.c",: v/ {4 c9 U7 u
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_rcc.c",, C: i1 w7 u4 a. x( ^/ M6 N) U
- "STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_rcc_ex.c",
. j% c! Q+ K/ k! y$ s9 c7 |) S) r
+ ~8 E f% f7 _+ Z- ]
7 F3 x5 R1 v8 `* \/ x - include_dirs = [. v, O4 n: g) u0 K/ w2 |
- ".",1 {% y8 Q* @' k7 i
- "//device/st/drivers/stm32mp1xx_hal/STM32MP1xx_HAL_Driver/Inc",: a5 p! S& ^/ o+ l y
- 0 \) B" P& o# h! C
- ]( D$ a$ O/ J, ^# j
- }' m. Q+ } T. n+ d
复制代码
, m- O2 f' P8 x+ [( ~最后,还需要配置一个宏定义,来使能TIM,编辑 STM32MP1xx_HAL_Driver\Inc\stm32mp1xx_hal_conf.h,将 HAL_TIM_MODULE_ENABLED 和HAL_DMA_MODULE_ENABLED使能 ,如下所示:& Q- k' ] t9 I8 U$ q- i# k* c
, ]3 Q2 A7 F1 G
- #define HAL_TIM_MODULE_ENABLED
4 ~3 V4 `, R# P0 D( t1 G: W- u - #define HAL_DMA_MODULE_ENABLED
复制代码 6 S @, j# D# M
到此,我们就能使用stm32mp1xx_hal_tim.h 提供的函数来开发PWM驱动。
) D8 ^' H( ?6 y; ]0 |9 g
( [" r e% j5 W; ?8 R2、使能HDF PWM框架, E' H" b( u# _2 O% H2 b
由于HDF 框架是在内核中的,需要在内核中使能PWM驱动。3 m3 x7 _3 J2 W4 h: c1 k1 _
2 Z( I: ^. H8 _6 U+ {) c. E- h! X编辑 vendor/bearpi/bearpi_hm_micro/kernel_configs/debug_tee.config,将 LOSCFG_DRIVERS_HDF_PLATFORM_PWM设置为y,如下所示:
) q5 \% ^' |' s; B: p/ f" n; F' {2 b3 M# w/ I
- LOSCFG_DRIVERS_HDF_PLATFORM_PWM = y
复制代码 4 C/ Q b: i4 H
开启该宏之后,就会编译HDF 的PWM框架,就能在驱动中使用PWM框架的pwm_core.h和pwm_if.h。
8 ?) D9 o; F* x$ i3 @" _9 L) K9 L+ N1 W
3、编写驱动代码4 M" y- X( a; U L: P6 R
按照官网文档的指示进行编写:PWM框架开发) _5 [7 {' h) ~* |' x" L
- S3 \+ x4 J8 _3 I同样分为三步走。6 M2 @4 n. G; Y
+ d6 o5 C7 `$ W7 s! @( |6 j" U( p
1、配置文件
9 s* m) k1 _, m5 b; {$ n3 L7 R一共需要修改两个hcs文件,分别是:device_info.hcs和pwm_config.hcs
, B7 d1 `+ M2 V6 P# I* S8 q7 s( C$ A ~6 Y+ n9 E2 e
首先 编辑st\bearpi_hm_micro\liteos_a\hdf_config\device_info\device_info.hcs增加pwm节点,如下所示:# B5 T7 q- \* G% Y0 M5 H( g H7 U
4 _9 V. F V- L9 a% i5 C, x+ \该节点应该是在 platform :: host节点下创建。其中policy=1表示只对内核发布驱动服务,moduleName必须为HDF_PLATFORM_PWM,serviceName必须以HDF_PLATFORM_PWM_开头,后面的数字用来区别不同的pwm外设。( V$ V3 R( j) L& y; K
; j6 T2 v+ _6 W3 O
这里我设置为2是因为我使用TIM2作为PWM的源。- device_pwm :: device{
4 X* m9 Q& U8 v - device0 :: deviceNode{
+ ?6 n, m, b I& y - policy = 1;
3 N) M: K* o4 n$ T0 i - priority = 12;' s. v! n# G0 U# ^! ?1 R
- permission = 0777;
6 s" \0 }& V/ U, m7 ?9 F - moduleName = "HDF_PLATFORM_PWM";% F1 x+ z+ \$ c$ h) F
- serviceName = "HDF_PLATFORM_PWM_2";& ^8 ^4 Z* f" N' H, s3 d3 z Z
- deviceMatchAttr = "st_stm32mp157_pwm_2";
: b; G4 \$ c, Y) V! A7 p - }% R" Q$ R4 E. s7 R# z# \- n2 C4 k
- }
复制代码
, _7 _' c( [9 C第二个配置文件就是自己创建的,在\bearpi_hm_micro\liteos_a\hdf_config\录下创建pwm目录,在目录中创建 pwm_config.hcs,并在其中添加以下内容:9 W( O# u2 t6 J7 B( s8 C# R
& I5 F. l. g& ~" `7 t
其中PWM的计数频率是1MHZ,在代码中写死,可以修改;physics_register表示TIM的寄存器基地址,根据STM32MP1参考手册可知TIM2的寄存器地址是x40000000寄存器地址范围是0x70。
" l' d3 i+ D3 w0 |* U( T) i$ i% X6 ~9 _
下面的配置表示:使用TIM2 Channel 1作为PWM输出,周期是1ms,占空比是50%" O& p# Q$ j; v5 u- O
/ v( P5 J% w, ~( x* d
- root {
6 m1 F* T8 H# b% B - platform {
1 Z, T/ x7 V( i% I, v# T - pwm_config {7 e: V ]- T! u* e/ \$ Q
- //1mhz
/ D: `) \* Z* V o4 j - template pwm_device {! e5 ?6 F+ C; S- e7 Q- z4 h
- Period = 1000; //1000*0.1us=100us=10khz) Q0 k% W( ^0 P Y1 w. r: V. r, P
- Pulse = 500; //5009 @, H* S) s) p+ x% `; h
- Polarity = 0; //high& ]1 `- A+ u ?$ u. v
- IdleState = 0; //reset
7 p! L4 F# Y. T, i - channel = 0; //channel_1 PA5
* v( Y3 k; w. n - match_attr = "";
5 h" N- ?% X; | v; \9 g - num = 0;
: l1 U9 r9 u, F1 t! p - physics_register = 0X40000000;
0 t2 V) `* s- @; l( i" N - register_size = 0X70;
B2 i, t! H8 R9 w2 `# I/ ? - }
/ B8 w6 d8 s- S. w* O* p
5 K0 f& t2 {$ G- device_0X40000000 :: pwm_device {" \5 m' @0 a! X; t3 s
- match_attr = "st_stm32mp157_pwm_2";, a8 p7 K' {9 v/ \2 c
- num = 2;
% `7 o% f/ ~& e - physics_register = 0X40000000;+ j' M; Q1 |- s$ S T8 f0 J# i; |
- }& V* W _: B& i" h0 H; Q# g& N
- }& M: O/ Q% L+ m+ A W
- }# ?/ r: i- X A3 r0 ?5 b
- }
复制代码 # E0 h9 \' K- k6 s1 {0 U& c5 D6 B
将上诉两个文件编辑完成后,在t\bearpi_hm_micro\liteos_a\hdf_config\hdf.hcs添加一行:0 G$ }1 E6 S+ \. m6 o
Y+ [+ N6 b! @3 f7 s- #include "pwm/pwm_config.hcs"
复制代码 ; V1 f; e* |" j, n9 B/ A7 h
2、编写驱动% l5 _; X1 r1 i! \, r1 E3 J7 R0 D1 D
在earpi-micro\device\st\drivers录下新建pwm目录,并创建驱动源文件stm32mp1_pwm.c,以及BUILD.gn
1 F, V& W- w4 i
; {; m U: {8 ~- [" E首先定义驱动入口对象:
* X: B L. T6 C! S# n( I( X; o/ s& d& r1 n- Z
- // 定义驱动入口的对象,必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量
0 p( c6 J1 I1 W9 v+ U& m - struct HdfDriverEntry g_pwmDriverEntry = {
/ c* j# v9 S# G - .moduleVersion = 1,* b2 M9 ]# Z2 _1 x% l: X6 q# E
- .moduleName = "HDF_PLATFORM_PWM", //必须与device_info.hcs中的字段一样,用于与驱动设备资源匹配
1 V% a3 G& Z- e B& i \ - .Bind = HdfPwmDriverBind,
2 l# |* @1 _' W }+ n' M - .Init = HdfPwmDriverInit,! R. v/ h8 ]( ?& O, w& N ~1 i
- .Release = HdfPwnDriverRelease,
7 a; |: z' O: Q3 v1 o - };
P" J+ M5 v2 v( f - + n6 Q) M" D2 i0 u6 p
- // 调用HDF_INIT将驱动入口注册到HDF框架中! [/ o, C3 a v4 C" U( q
- HDF_INIT(g_pwmDriverEntry);
复制代码 ( J7 a$ i/ g5 k/ ^7 C
以及自定义一个PWM对象:% H& a4 X+ M2 N# j
2 ]$ p7 k. O \, }
- //私有pwm结构体! i$ t" p8 @4 Z5 L( F& T/ ^
- struct StmPwm {3 @" ^; u' i; |7 `
- TIM_HandleTypeDef htim; //HAL TIM必须
/ a3 I& T5 i% M6 x5 u - struct PwmDev dev; //HDF PWM核心层必须
4 X9 |5 Y, V/ l* ~) B$ ^ - TIM_OC_InitTypeDef sConfig; //HAL PWM必须 & E" U0 R, C2 v9 Y4 X: q+ d3 x' T& O
- uint32_t physics_register; //TIM基地址
1 D+ ~1 ]4 W: i9 f - uint32_t register_size; //地址范围
' L* H8 c. @ X& z - uint32_t channel; //pwm channel6 O# e$ z" ^8 j$ n
- };
复制代码
( I- i/ m$ U. d+ g( J9 U初始化函数逻辑很简单,就是使用 struct StmPwm 对象中的成员去调用各个库的初始化函数。
+ a3 G" C4 e5 |( [& C0 l5 h" O! \0 ]2 u5 \. i) V3 X0 J: j* u
首先引入PWM所需的头文件:0 ~" m/ J3 i5 ~/ f
, W+ j; p; ^' ~/ `4 D! S3 f. V
- //stm hal库
. Y2 B9 l( I( D0 Y: [ - #include "stm32mp1xx_hal.h". X2 B/ z3 Q7 g1 |% q
- #include "stm32mp1xx_hal_tim.h"" _3 ]5 y$ X: R5 E* L6 F) s
- //hdf pwm1 G0 p+ s9 a0 X, `5 q& Q
- #include "pwm_core.h"
& X% y* c6 i3 ^( Y! k) a - #include "pwm_if.h"
复制代码 3 z& H$ d# u3 O, B5 o
驱动初始化函数:首先读取上文所配置的信息到StmPwm对象中,然后将PwmDev添加到HDF PWM框架中,这样就能使用HDF PWM框架的功能;最后调用STM32MP1 HAL库函数,初始化TIM2的PWM模式,设置对应的GPIO复用功能,开始输出PWM。
5 U: n6 }/ F( s# h3 Y: x' ^0 T1 a& _7 t; G6 D% h/ }: c, e. ?/ R
- // 驱动自身业务初始的接口(设置IO口为输出) HDF框架在加载驱动的时候,会将私有配置信息保存在HdfDeviceObject 中的property里面9 U( P+ ^7 }5 f4 Y
- int32_t HdfPwmDriverInit(struct HdfDeviceObject *device)2 s6 ~4 F: h0 K+ H1 t% f4 ^
- {1 q c+ U. p$ v2 V* ?
- dprintf("%s enter\r\n",__func__);
* w% c) @) y6 J$ d - + M: s2 O3 R* F# n; ~6 o
- RCC_ClkInitTypeDef clkconfig;! ^; u5 T$ g4 ~2 _
- uint32_t pFLatency;
6 b2 |% x+ Z5 U* ]" e( n - uint32_t uwTimclock;
1 n9 C1 O: N4 W9 o( s - ! Q0 c0 O$ p! K/ |2 x2 y
- sp = (struct StmPwm *)OsalMemCalloc(sizeof(*sp));
" [. x& H- p- ]" \" q5 r! Z - . v/ U+ D, A# D3 Q% F" K/ ^) [
- //读取配置文件
! c8 d# I* }* W7 i - readHcs(device);
5 Q" }: X$ H5 E) r- S/ H - " { U+ J: w) U) F0 b
- //获取时钟频率
$ k6 h2 h, p7 }1 J1 t, T - HAL_RCC_GetClockConfig(&clkconfig, &pFLatency); # h1 v9 t3 W4 T
- __HAL_RCC_TIM2_CLK_ENABLE();
; W' j* }- C8 l" e7 |0 t( r: e" Q - uwTimclock = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_TIMG1);
# O1 y( k7 `4 K7 @/ B4 j: _ - //dprintf(" uwTimclock = %d\r\n",uwTimclock);
( h( H# B0 x7 V8 P - //配置TIM的计数频率为10MHZ
# \1 N9 A, C$ a. o) G5 c1 Y. P - sp->htim.Init.Prescaler = (uint32_t) ((uwTimclock / 10000000U) - 1U); 6 r. x3 g# p9 d; H; E
- sp->htim.Init.CounterMode = TIM_COUNTERMODE_UP;
+ s& Z0 N; ^8 r5 Q- J - sp->htim.Init.ClockDivision = 0U;, M V7 {* Y( v4 Y- G- h+ {
- sp->htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;% i2 q5 W; z( ?6 i
- sp->sConfig.OCMode = TIM_OCMODE_PWM1;
% d; j3 f6 E+ i% M' T - sp->sConfig.OCFastMode = TIM_OCFAST_DISABLE;
2 q3 }& h. s- r4 R+ R - 3 B. }: _. M" p& Y% g! w% \1 ~7 Y
- sp->dev.method = &g_pwmOps;
* I& t/ F& W+ e$ o6 E/ P% @ - sp->dev.cfg.duty = sp->sConfig.Pulse;
6 Y& N5 }$ E9 k5 Q1 A+ Q; L6 ? - sp->dev.cfg.period = sp->htim.Init.Period; 6 f* V( q5 |2 S0 _& {
- sp->dev.cfg.polarity = sp->sConfig.OCPolarity; ( e C/ a5 b9 C2 U! O( |
- sp->dev.cfg.status = PWM_ENABLE_STATUS;
, q9 }0 l3 y- _+ ?7 E0 { - sp->dev.cfg.number = 10000; 2 t3 _' N7 D& F+ x
* i1 y: g- j7 A" y4 ~- sp->dev.busy = false;
1 d, `# ~9 e; C - ! P! t; z) P+ ~6 p/ d
- //添加到核心层
7 E7 I0 U4 Y" I - if (PwmDeviceAdd(device, &(sp->dev)) != HDF_SUCCESS) {
- e4 n" J+ e" s: Y- Z# X
: g: ^- o% c" q3 G, M- return HDF_FAILURE;
# s2 Z! M! o' k - }
7 D Z* I- ]* \# S6 E9 b - //对TIM2寄存器基地址进行映射
, ?. O' I `+ r1 ]5 L - sp->htim.Instance = (TIM_TypeDef *)OsalIoRemap(sp->physics_register, sp->register_size);6 ]+ t7 W4 R- r
- if (sp->htim.Instance == NULL) {- w) D" ^" m3 K' A0 C& h, {- C
- dprintf("error OsalIoRemap for htim \r\n");
8 C7 Y3 \6 H- _, o, U" Z* C$ A - return -1;
1 n& M% E7 K u# g* `* u - }8 Q; A7 `3 `3 c( }4 C9 ?. }, ^
- //初始化PWM寄存器) n" ?* D: i( L- `
- if (HAL_TIM_PWM_Init(&sp->htim) == HAL_OK)5 `5 o( P8 b) f- L5 l8 k% W
- {6 b L# X6 V$ ^& ^0 U4 ~/ V {8 f
- dprintf("pwm init ok config channel \r\n");$ U% P- W' p5 B& _: L6 v
* Q7 L) ~( ?& ]1 C- p2 x- Z/ l- HAL_TIM_PWM_ConfigChannel(&sp->htim, &sp->sConfig, sp->channel);& E7 k, P& r0 W: m8 t" M, M* m
- //初始化gpio
4 o' _& J& u6 ]; c* M" ]/ F+ C. u - HAL_TIM_MspPostInit(&sp->htim);
H7 Q. y: ?/ |6 F! ], Y+ e- B# g - //开始输出PWM7 v' l i& ]) U; N$ T1 o) S3 b
- HAL_TIM_PWM_Start(&sp->htim,sp->channel);! W+ K* x$ c, C# F& v& R7 c
- }else{
* p: M) X& k( e - return -1; V' P3 o8 O$ t- S! K7 F1 K
- }
! _8 U. o) @. {7 B
# I4 o' X0 A/ F- S- 1 S y) H; J7 l$ D
- return HDF_SUCCESS;( W+ d5 F( I+ S+ f; f, }/ ]
- }
复制代码 - V& G0 h7 F' Q* V- K- G& @
读取上诉配置文件到StmPwm对象:
2 u! u. z1 g0 V- H0 T: O+ h5 V( v, b: C& z
- //读取配置文件 pa5 tim2 channel 1
j! B% l( A) E* C) N) t - int readHcs(struct HdfDeviceObject *obj)
# K, E2 Q/ Y" X5 S( S: ?7 W* G - {; f$ b; F: i- J; |9 e, B
- dprintf("%s enter\r\n",__func__);2 r' P& b, d+ k O
- % ~2 @) G) x" _. H
! s4 l, D/ D, j, n' Z$ X0 r- struct DeviceResourceIface *iface = NULL;. V5 n0 X4 T" e$ }7 m
- 6 d' p/ |4 G3 z N
- iface = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);6 F. E; P5 s2 m3 \6 O7 o
- if (iface == NULL || iface->GetUint32 == NULL) {& [, J* b# Z* M( d* A0 h5 u
- HDF_LOGE("%s: face is invalid", __func__);
) Q3 [1 P( h# x/ \) ~3 y3 [ - return HDF_FAILURE;# p+ j* }6 f' N5 ^
- }5 W- p& T+ H+ B) h
- if (iface->GetUint32(obj->property, "Period", &sp->htim.Init.Period, 0) != HDF_SUCCESS) {
( g" d& `8 ]9 Q, Q( |7 P1 c - HDF_LOGE("%s: read num fail", __func__); r# Y0 ?& y, }' E8 g! }
- return HDF_FAILURE;% k1 m! W/ f' F5 _: U! }
- }/ @ y, p" f2 K9 A" f( G+ J
- if (iface->GetUint32(obj->property, "Pulse", &sp->sConfig.Pulse, 0) != HDF_SUCCESS) {; I6 G8 B K7 F- `( ]
- HDF_LOGE("%s: read num fail", __func__);
& k5 c4 x4 F0 O# C9 z9 M - return HDF_FAILURE;
9 i/ j' w o6 x3 l; v" v2 p - }8 b; I, F1 f3 |& v* t6 g
- if (iface->GetUint32(obj->property, "physics_register", &sp->physics_register, 0) != HDF_SUCCESS) {! C2 k+ j; J- r
- HDF_LOGE("%s: read num fail", __func__);
, `% ?% Q/ ?0 Y2 \ - return HDF_FAILURE;3 M1 _1 m4 a7 ^& t/ m/ m' C
- }
6 k+ {; ^" G( I+ I" V$ G - if (iface->GetUint32(obj->property, "register_size", &sp->register_size, 0) != HDF_SUCCESS) {1 ^/ y. W0 h/ A5 D- y5 a7 f
- HDF_LOGE("%s: read num fail", __func__);# F0 {/ `/ x; \* Q3 K4 N& G; x
- return HDF_FAILURE;
/ K5 M5 r; a; l0 x& T; x7 G' I2 O - }* | n$ m' Q/ V$ v$ ^: g
- if (iface->GetUint32(obj->property, "channel", &sp->channel, 0) != HDF_SUCCESS) {. I* O/ H- h! G7 r& |0 G2 [' c
- HDF_LOGE("%s: read num fail", __func__);
, M; g2 o5 r0 C - return HDF_FAILURE;
" \3 y/ B: ?8 r0 b' r0 c6 ` - }" k7 w( d# V' p, w0 T" _. C( l4 D
- if (iface->GetUint32(obj->property, "Polarity", &sp->sConfig.OCPolarity , 0) != HDF_SUCCESS) {
% Y" O7 A: u* W; X - HDF_LOGE("%s: read num fail", __func__);/ Z' C6 _# I& _/ U. V% D
- return HDF_FAILURE;$ x# b# D# f" n3 l5 G7 F
- }( h2 J: R) \2 c% M. [
- if (iface->GetUint32(obj->property, "IdleState", &sp->sConfig.OCIdleState , 0) != HDF_SUCCESS) {6 {1 e( T4 a W/ D1 X7 X& q
- HDF_LOGE("%s: read num fail", __func__);2 W6 b+ d* V$ z8 }8 D, J. a6 C
- return HDF_FAILURE;( ^0 {' a" _" A1 y* b% b
- }
5 o, L# z* ^) {# u3 y - if (iface->GetUint32(obj->property, "IdleState", &sp->dev.num , 0) != HDF_SUCCESS) {9 `1 K# O! i/ g
- HDF_LOGE("%s: read num fail", __func__);
& o8 U) H( o+ p' [& H! B - return HDF_FAILURE;
! c: A# w* s" I9 F5 y - }9 ]- N+ t/ Y& _! u! ^( O
- , Y3 K& O9 |7 L# I
- return 0;( y/ g! `9 P, C6 f
- }
" @! D5 b% J4 Y! O
复制代码
* [- S- g: l1 F/ OGPIOA_5 复用为PWM模式
, n6 s: B4 a: y& N3 b+ V& d$ ?# L6 T, N$ H& h
- //初始化gpio口
( r: l: {' G4 @2 ~ - void HAL_TIM_MspPostInit(TIM_HandleTypeDef* htim)
! f; M; o6 b/ ?# |6 j7 h - {- s1 s" U0 j4 l! S
- __HAL_RCC_GPIOA_CLK_ENABLE();0 a$ n' s" W5 f9 ^, l. F2 W
- dprintf("%s enter\r\n",__func__); e7 E7 c7 H- I# a- o+ b- S5 @
- GPIO_InitTypeDef GPIO_InitStruct;) S- t: C( y) u9 j. y
0 P0 F! p* h; L- //gpioa addr
) K2 h( S/ \6 q8 h j" S - unsigned char *gpioa= (unsigned char *)OsalIoRemap(GPIOA_PHYADDR, GPIOA_SIZE);
2 D/ ^/ I1 \$ _4 d5 E - /**TIM2 GPIO Configuration
2 L6 j% @+ q/ c" j1 H$ j7 h - PA5 ------> TIM2_CH1 7 z" Z4 }7 J) h& k) L+ t$ B8 E- U( g
- */
, V1 j1 u9 v7 }4 X - GPIO_InitStruct.Pin = GPIO_PIN_5;8 }8 t0 Q9 Z1 V7 R5 f4 u7 ?
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;! ~$ Q! ~4 s! l& c# L d
- GPIO_InitStruct.Pull = GPIO_NOPULL; f* |$ l5 n+ z) Q% y" F/ d
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
% x8 Y) O5 {+ m* G- U0 X8 f% w9 O. N# ~ - GPIO_InitStruct.Alternate = GPIO_AF1_TIM2;
, {; y Y& E' B J* |+ N - HAL_GPIO_Init((GPIO_TypeDef *)gpioa, &GPIO_InitStruct);
; ?, U" b, v% z) o0 N - % c' Y1 {7 r7 P# P
- }
复制代码 - ?& Q6 O ?: u& `! h+ E
到此初始化任务就基本完成,那么HDF PWM框架如何来使用我们的PWM驱动呢?这就要提到 PwmMethod,通过设置PwmMethod的函数,提供接口给PWM框架,主要是提供StmPwmSetConfig。6 [, J5 b) B# d1 b/ U
7 `2 r* d) G: I {( ?0 O L1 G4 l' D! i, |
- struct PwmMethod g_pwmOps = {6 y* o0 [5 {& [5 ]: I4 K" V2 G% L/ y
- .setConfig = StmPwmSetConfig,
- o# N3 W; d+ Q) @1 _3 y - };
复制代码 5 Q: a4 F" ]" i. \# N
HDF_PWM框架是通过pwm_if.h文件给内核提供PWM功能的,我们来看pwm_if.h给内核提供了什么。$ ]5 J& J0 F( w% F: D) X, r
+ @6 M5 Q/ c+ O1 h* q- B, x- //获取pwm句柄
+ I; n( }7 m, i( m4 _5 o p - DevHandle PwmOpen(uint32_t num);! T* R9 O2 [' g/ i J7 Y+ j
- void PwmClose(DevHandle handle);( ^8 s2 i0 x1 c! B- p# \* O
- //设置pwm周期6 J& g& H3 S% W! p7 Q! P; I- A
- int32_t PwmSetPeriod(DevHandle handle, uint32_t period);) \. r& [, l' o5 t5 c4 b! F2 C
- //设置占空比& M0 }- [* g! K3 z" v8 e0 f# V7 F. C
- int32_t PwmSetDuty(DevHandle handle, uint32_t duty);) A4 x* d" j8 n1 ?" H$ {7 G
- //设置极性
6 ~# i+ z7 K; ~* e( Q/ ^ - int32_t PwmSetPolarity(DevHandle handle, uint8_t polarity);. _0 i9 a$ v+ A a4 v# r
- //使能pwm
8 E0 V$ t- [! p" f - int32_t PwmEnable(DevHandle handle);
" w( [- L- F9 |- z% z - int32_t PwmDisable(DevHandle handle);
3 n6 A3 r0 y& L' o' O - //设置pwm8 S- o) M( O, w8 L! g- @$ e# I
- int32_t PwmSetConfig(DevHandle handle, struct PwmConfig *config);
9 j" D5 k5 t+ l% u% @5 | - int32_t PwmGetConfig(DevHandle handle, struct PwmConfig *config);
复制代码
7 e( S# }% L9 {! M! H/ `% }, f可以看到PWM框架提供给内核设置PWM参数的接口,这些接口最终会调用我们编写的驱动,那么我们的驱动应该如何实现上述的功能?答案就在:StmPwmSetConfig,所有的接口最终都会调用PwmSetConfig()而间接调用我们编写的StmPwmSetConfig()。9 N) W6 g8 a% p% [: P9 G U
7 Q/ r& }* s* _- C& S7 S: V
在StmPwmSetConfig()中会对config参数进行检查,然后根据config参数去操作PWM外设,具体而言就是调用HAL库的PWM函数去实现:
! U0 f- t8 o/ c$ u6 V0 J" s
6 P# q$ `, F$ F' L, F- //给pwm框架注册的函数! } W& J/ Q' K. ?
- struct PwmMethod g_pwmOps = {6 Q/ u5 c; C3 O, r
- .setConfig = StmPwmSetConfig,
@$ k' e2 X) j3 R1 }8 l - };" ?5 w0 _% R$ Q: O7 ?# J
- //设置pwm
+ }! V5 ]4 Q, s% p" b+ k8 Z5 G - static int32_t StmPwmSetConfig(struct PwmDev *pwm, struct PwmConfig *config)
6 c; C: X3 U D7 T4 E5 m - {! x& w- V' X) `+ [0 X
5 G# K/ \, O0 Y# y- if (pwm->cfg.polarity != config->polarity ) {/ Z0 Q/ ~3 m) U. u7 U1 E
- HDF_LOGE("%s: not support set pwm polarity", __func__);
3 l/ ?+ q% u' {& I$ r( L. ` - return HDF_ERR_NOT_SUPPORT;! i; k* l% {' t: j9 t/ q" }5 w
- }- a8 R' P; v- P1 h$ `4 d. v
7 V5 E) g# ?7 j' A- if (config->status == PWM_DISABLE_STATUS) {
) X8 M3 P+ t! Q* y+ M" ?& Y - # [4 K! [! X& J Y% I
- StmPwmDisable();- ]9 {7 t% ^" O4 T% ]& g
- return HDF_SUCCESS;" }/ T) P; u/ r- [$ r7 e, d+ e
- }
, \3 e9 e( b8 Q t( _ [3 q$ f
% Q2 b% G% Y* X3 n+ ]* z: F- y- if (config->polarity != PWM_NORMAL_POLARITY && config->polarity != PWM_INVERTED_POLARITY) {
F( Z" x& `0 t# t - HDF_LOGE("%s: polarity %u is invalid", __func__, config->polarity);
: H+ z' s) w/ d5 V$ R+ b7 V - return HDF_ERR_INVALID_PARAM;+ m) z5 N$ ^2 z9 E4 k& s. T
- }
, ?. Z: ~; U0 G1 I: X
8 Z5 z* M. n3 D1 n4 c* V0 r; {- if (config->period < 0) {
) u9 M" T8 b/ ] ]. Y2 M% Y - HDF_LOGE("%s: period %u is not support, min period %u", __func__, config->period, 0);( F# m; L. q" W- f6 }+ A
- return HDF_ERR_INVALID_PARAM;- S. `4 q3 E7 i \ ?# R' l
- }% o6 K$ I N& C! ^5 L5 a* e& X
- if (config->duty < 1 || config->duty > config->period) {
. E$ \6 T- `- b - HDF_LOGE("%s: duty %u is not support, min dutyCycle 1 max dutyCycle %u",- N* g# R1 y: `+ H H+ p* y4 s# C
- __func__, config->duty, config->period);
/ W" D; Z) G9 C - return HDF_ERR_INVALID_PARAM;) l4 y) P( s" u. W" _! v
- }
3 L0 R3 x4 `' q - //暂停pwm,更新配置6 L4 B n$ F L9 x9 [; n" F; x
- StmPwmDisable();
* S8 z" r+ M: Z# @. w
0 ]! ~ M& f3 \* O- if (pwm->cfg.polarity != config->polarity) {
% `5 _$ ?* \% G/ H - StmPwmSetPolarity( config->polarity);
# z2 B1 ?! x a4 ]6 j - }4 }% q2 ?; N& i! y0 E; \4 W
- StmPwmSetPeriod( config->period);
2 T4 |. r2 S. x/ L - StmPwmSetDuty( config->duty);
' P5 T% D# r$ {3 A, f* r - //继续输出+ @0 q; q9 ~9 `, Y& D6 a( L
- - h/ h& n! M2 W% s$ O8 j
- if (config->number == 0) {" p1 z* @4 T+ b3 M9 e
- StmPwmAlwaysOutput();
S5 z( m" r* g - } else {$ V' P Q. K* B
- StmPwmOutputNumberSquareWaves( config->number);
4 G2 n, j/ E) {+ J; E. T0 X" ] - }
# g! V7 b% U$ L# p% C2 Z# G+ f2 V - return HDF_SUCCESS;& T3 o8 c- D6 S2 v) L
- }) G% y* k0 S/ {& ~: j
复制代码 ( c- P$ S( L. p$ J
HAL库实现配置PWM的方法:! R. g: w- o5 J. C, v
8 G& {- u) |$ _* |
- static inline void StmPwmDisable()
, L. e' T1 ^5 J& x8 w! a% j7 j - {5 v+ v, ^6 @9 N! r* F" P
- HAL_TIM_PWM_Stop(&sp->htim, sp->channel);
- i* S" j0 T! c4 M1 _) w: R+ R - }
) x! P$ I9 F0 C. M - 4 ^& P3 [. _+ Y* M
- static inline void StmPwmSetPeriod(uint32_t us)
0 ?% R# F3 L% _ - {
* z+ t2 h4 e1 y' N6 ]& M - sp->htim.Init.Period = us;
8 m n# H' e! V* Q* N% c( R/ K5 ? - TIM_Base_SetConfig(sp->htim.Instance, &sp->htim.Init);: F$ \. A& H e/ I6 M
- }; G1 X# u8 I2 k
- static inline void StmPwmSetDuty(uint32_t us)
1 q5 X* _2 F% s) ^# Q8 f( ]' n - {
* `/ i) f% y) O0 q - sp->sConfig.Pulse = us;
. x' O: R2 ]& M5 w5 C2 P( m# w - HAL_TIM_PWM_ConfigChannel(&sp->htim, &sp->sConfig, sp->channel);
/ o2 W* P- ^ D; P3 \ - }
% F. ~) E+ Q9 Z* \* k* s/ a - static inline void StmPwmSetPolarity(uint32_t polarity)) H* r3 }; ~! |0 w" c
- {
, i0 I p% L' G2 h# W - sp->sConfig.OCPolarity = polarity;8 l9 p% G4 x/ T4 }; B5 P
- HAL_TIM_PWM_ConfigChannel(&sp->htim, &sp->sConfig, sp->channel); 8 B; }( Z [8 u: l: J7 p
- }. ^1 A" b9 C7 }" L4 ?' }' t
- static inline void StmPwmAlwaysOutput()0 f& [5 B+ D# C/ T9 o% P
- {
9 V9 T T9 c1 p9 R - HAL_TIM_PWM_Start(&sp->htim, sp->channel);
! k! i ]( {: V6 D% d4 n - }% o8 G- L! l: p. e- ?7 m
- 6 D2 |$ B9 R- H" v4 {7 ?" N
- static inline void StmPwmOutputNumberSquareWaves(uint32_t num)
$ G- a; L' U* `3 f, C7 u. S4 G - {! c! D- @- t" T( s
7 X* p; }0 _0 A% A+ }- HAL_TIM_PWM_Start(&sp->htim, sp->channel);
. C/ d% A! Q% p: L - }
复制代码 1 _/ z4 Y- I% v4 l8 V6 [ `) Z9 o- I
3、编写构建脚本$ U0 i8 O' ^ B# U6 M |
最后要将我们编写好的驱动文件加入到编译构建系统中:
9 p7 H+ G" D D& K. Z& X
( E* ]- j( e4 V编辑BUILD.gn:
5 T! H5 q+ b$ B% T% _0 L7 ] l6 H( ~9 i+ i
- import("//drivers/adapter/khdf/liteos/hdf.gni")8 n3 ~1 ^) m4 U
- module_switch = defined(LOSCFG_DRIVERS_HDF_PLATFORM_PWM)
, H( }: x2 X9 b9 R - hdf_driver("hdf_pwm") {
+ l+ U& Q2 ^9 w7 L+ G- V - sources = [" R8 W$ y& M9 |2 E3 B
- "stm32mp1_pwm.c",5 T; A G0 z% E- n! ~/ D$ I1 G
- ]4 ?3 f; s& \5 ~2 [+ F o. ^
- include_dirs = [' H' B5 v( h3 P! T
- "." ,
0 o) J. ~8 e9 N7 a# N) M - "//device/st/drivers/stm32mp1xx_hal/STM32MP1xx_HAL_Driver/Inc",
. k, B( e+ k1 ] - ]' _# W) r2 z; M4 e9 L6 d
- }
复制代码
+ `$ x3 C% x2 q并修改device/st/drivers/BUILD.gn,在dep添加pwm:: A$ G$ ]3 F; e
# F* @/ @ o5 D7 _( V }1 g+ |- group("drivers") {* r9 t8 C. z# A" {+ n2 t$ c
- deps = [
1 J$ f" v3 A0 o+ Y. ~7 f" |; \ - "pwm",( f3 {# A8 @7 W5 _" X
- "uart",2 I6 n7 d- [- W3 m* T2 Q4 t- Y1 _
- "iwdg",
- B' X0 D6 \3 {$ o, b; \8 }! h - "i2c",
: d1 z6 b% H7 K" ?6 }% S - "gpio",
- f- ^- T. r/ V' G - "led",
: l& \- z% L; G- o6 M8 m5 ]. V - "button",
0 U( Y9 C P6 a2 h! ^+ n - "sample",
7 E0 b% m* A- @2 j s1 I5 ` - "mem",! X: B# J g2 z4 j; q
- "stm32mp1xx_hal",5 s9 y9 D/ |6 P. t
- "wifi/driver/hi3881", h# h7 K; G1 u! F& T$ |
- "wifi/driver:hdf_vendor_wifi",
5 k" t- x3 F0 E; }0 r _ - ]
7 [6 N' f! _; }3 x - }
复制代码
& c$ l: ]$ q; M. @7 e4、效果& }* C3 A9 U% f- F
完成PWM驱动的编写后,就可以使用bearpi-micro\drivers\framework\include\platform\pwm_if.h里的函数来控制PWM波。如图是使用逻辑分析测量到的GPIOA_5引脚上的PWM信号:$ C: @3 \- A8 A$ u' @6 D$ \6 h1 T
0 Y8 b+ @9 O/ l/ k计数频率10MHZ,计数值1000,那么PWM的频率就是10MHZ/1000=10KHZ:
, o: A* o! a- v" M. l; |6 [+ o1 f+ b% a, r( Q" ]0 U
+ c" i; U# O/ o5 _8 t' F* m9 h$ V
/ k( d* U9 ]; f$ f( w8 d6 r
————————————————, z4 F" j3 g, K: t
版权声明:killer-p( U8 ^# S u" F
% y- Q0 L! S2 y5 l" f6 e( A! m' F7 z
x4 _0 m2 ~# P+ r6 S% _, R. f |