HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能!

2024-06-19

Allen 先楫资深 FAE

8年产品研发经验,具有变频器、PLC等工业产品开发经验,也参与过汽车仪表、中控屏等车载产品的研发工作。在产品底层驱动、伺服驱动器、显示仪表等领域有着丰富开发经验。

在嵌入式产品应用开发中,经常需要MCU芯片产生任意的方波信号,从而驱动外设执行相应的操作。比如,驱动模拟量芯片、miniLED屏等。不同于PWM波这种占空比固定的信号,这些驱动信号往往是由等宽的高低电平任意排列的方波。

HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能! (https://ic.work/) 技术资料 第1张


传统的GPIO模拟方波时序,不仅占用CPU资源,而且波形的脉宽较大。在驱动miniLED屏这类外设时,达不到系统的功能要求。先楫半导体的全系列MCU可以将内存中的数据通过DMA来设置GPIO的电平,以TRGM互联管理器和PWM比较器配合使用来设置脉冲宽度,从而产生任意时序的方波信号。该方案基于硬件方式来实现,不会占用CPU处理时间,波形宽度可达到50ns。下面介绍该方案的实现过程,例程基于HPM6360EVK实现。

定义波形数据

定义数组如下所示,其中32位无符号整数可以映射32个GPIO,每个数据位对应一个管脚。数组长度4096对应4096个方波周期。


ATTR_PLACE_AT_NONCACHEABLE_WITH_ALIGNMENT


(

8

)


uint32_t

g_u32LedBufForGpio32

[

4096

]


=


{

0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0

};

初始化GPIO管脚

此处将24个GPIO配置为输出模式。


static


void


zh_led_gpio_config


(


void


)



{



uint32_t

pad_ctl

=


IOC_PAD_PAD_CTL_PE_SET


(

1

)


|


IOC_PAD_PAD_CTL_PS_SET


(

1

);

HPM_IOC

->

PAD

[

IOC_PAD_PC00

].

FUNC_CTL

=

IOC_PC00_FUNC_CTL_GPIO_C_00

;


HPM_IOC

->

PAD

[

IOC_PAD_PC00

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC01

].

FUNC_CTL

=

IOC_PC01_FUNC_CTL_GPIO_C_01

;


HPM_IOC

->

PAD

[

IOC_PAD_PC01

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC02

].

FUNC_CTL

=

IOC_PC02_FUNC_CTL_GPIO_C_02

;


HPM_IOC

->

PAD

[

IOC_PAD_PC02

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC03

].

FUNC_CTL

=

IOC_PC03_FUNC_CTL_GPIO_C_03

;


HPM_IOC

->

PAD

[

IOC_PAD_PC03

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC04

].

FUNC_CTL

=

IOC_PC04_FUNC_CTL_GPIO_C_04

;


HPM_IOC

->

PAD

[

IOC_PAD_PC04

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC05

].

FUNC_CTL

=

IOC_PC05_FUNC_CTL_GPIO_C_05

;


HPM_IOC

->

PAD

[

IOC_PAD_PC05

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC06

].

FUNC_CTL

=

IOC_PC06_FUNC_CTL_GPIO_C_06

;


HPM_IOC

->

PAD

[

IOC_PAD_PC06

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC07

].

FUNC_CTL

=

IOC_PC07_FUNC_CTL_GPIO_C_07

;


HPM_IOC

->

PAD

[

IOC_PAD_PC07

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC08

].

FUNC_CTL

=

IOC_PC08_FUNC_CTL_GPIO_C_08

;


HPM_IOC

->

PAD

[

IOC_PAD_PC08

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC09

].

FUNC_CTL

=

IOC_PC09_FUNC_CTL_GPIO_C_09

;


HPM_IOC

->

PAD

[

IOC_PAD_PC09

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC10

].

FUNC_CTL

=

IOC_PC10_FUNC_CTL_GPIO_C_10

;


HPM_IOC

->

PAD

[

IOC_PAD_PC10

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC11

].

FUNC_CTL

=

IOC_PC11_FUNC_CTL_GPIO_C_11

;


HPM_IOC

->

PAD

[

IOC_PAD_PC11

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC12

].

FUNC_CTL

=

IOC_PC12_FUNC_CTL_GPIO_C_12

;


HPM_IOC

->

PAD

[

IOC_PAD_PC12

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC13

].

FUNC_CTL

=

IOC_PC13_FUNC_CTL_GPIO_C_13

;


HPM_IOC

->

PAD

[

IOC_PAD_PC13

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC14

].

FUNC_CTL

=

IOC_PC14_FUNC_CTL_GPIO_C_14

;


HPM_IOC

->

PAD

[

IOC_PAD_PC14

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC15

].

FUNC_CTL

=

IOC_PC15_FUNC_CTL_GPIO_C_15

;


HPM_IOC

->

PAD

[

IOC_PAD_PC15

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC16

].

FUNC_CTL

=

IOC_PC16_FUNC_CTL_GPIO_C_16

;


HPM_IOC

->

PAD

[

IOC_PAD_PC16

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC17

].

FUNC_CTL

=

IOC_PC17_FUNC_CTL_GPIO_C_17

;


HPM_IOC

->

PAD

[

IOC_PAD_PC17

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC18

].

FUNC_CTL

=

IOC_PC18_FUNC_CTL_GPIO_C_18

;


HPM_IOC

->

PAD

[

IOC_PAD_PC18

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC19

].

FUNC_CTL

=

IOC_PC19_FUNC_CTL_GPIO_C_19

;


HPM_IOC

->

PAD

[

IOC_PAD_PC19

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC20

].

FUNC_CTL

=

IOC_PC20_FUNC_CTL_GPIO_C_20

;


HPM_IOC

->

PAD

[

IOC_PAD_PC20

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC21

].

FUNC_CTL

=

IOC_PC21_FUNC_CTL_GPIO_C_21

;


HPM_IOC

->

PAD

[

IOC_PAD_PC21

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC22

].

FUNC_CTL

=

IOC_PC22_FUNC_CTL_GPIO_C_22

;


HPM_IOC

->

PAD

[

IOC_PAD_PC22

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC23

].

FUNC_CTL

=

IOC_PC23_FUNC_CTL_GPIO_C_23

;


HPM_IOC

->

PAD

[

IOC_PAD_PC23

].

PAD_CTL

=

pad_ctl

;

HPM_IOC

->

PAD

[

IOC_PAD_PC24

].

FUNC_CTL

=

IOC_PC24_FUNC_CTL_GPIO_C_24

;


HPM_IOC

->

PAD

[

IOC_PAD_PC24

].

PAD_CTL

=

pad_ctl

;


for


(


int

i

=

0

;

i

<

24

;

i

++


)


{



gpio_set_pin_output


(

HPM_GPIO0



GPIO_DO_GPIOC



i

);



gpio_write_pin


(

HPM_GPIO0



GPIO_DO_GPIOC



i



0

);



}



}



































配置DMA

在每次输出波形之前都要进行一下DMA的设置,用户通过调整波形数组的数据来设置想要的波形。DMA将数组的数据不断搬运到DO[VALUE]寄存器中,因此要注意GPIO管脚和数组数据的对应关系。例程是输出占空比为50%的PWM波。


static


void


zh_led_dma_config


(


void


)



{



dma_channel_config_t

ch_config

=


{

0

};



unsigned


int

i

=

0

;


for


(

i

=

0

;

i

<

4096

;

i

++


)



{


g_u32LedBufForGpio32

[

i

]


=


(

i

&

1

)


?


(


~

0

)


:


(

0

);



//g_u32LedBufForGpio32[i] = ~0;



//if(i%5 == 0)



//{



// g_u32LedBufForGpio32[i] = ~0;



//}



//else



//{



// g_u32LedBufForGpio32[i] = 0;



//}



}


dma_reset


(

HPM_HDMA

);


intc_m_enable_irq_with_priority


(

BOARD_APP_HDMA_IRQ



1

);


dma_default_channel_config


(

HPM_HDMA




&

ch_config

);

ch_config

.

src_addr

=


(


uint32_t


)


&

g_u32LedBufForGpio32

[

0

];

ch_config

.

dst_addr

=


(


uint32_t


)


&

HPM_GPIO0

->

DO

[

GPIO_DO_GPIOC

].

VALUE

;


ch_config

.

src_width

=

DMA_TRANSFER_WIDTH_WORD

;


// 32位


ch_config

.

dst_width

=

DMA_TRANSFER_WIDTH_WORD

;


// 32位


ch_config

.

src_addr_ctrl

=

DMA_ADDRESS_CONTROL_INCREMENT

;


ch_config

.

dst_addr_ctrl

=

DMA_ADDRESS_CONTROL_FIXED

;


ch_config

.

size_in_byte

=


sizeof


(

g_u32LedBufForGpio32

);


//32 * sizeof(uint32_t);


ch_config

.

dst_mode

=

DMA_HANDSHAKE_MODE_NORMAL

;


ch_config

.

src_burst_size

=

0

;


if


(

status_success

!=


dma_setup_channel


(

HPM_HDMA



0




&

ch_config



false

))



{



printf


(


" dma setup channel failed\n"


);



return


;



}


dmamux_config


(

HPM_DMAMUX



DMAMUX_MUXCFG_HDMA_MUX0



HPM_DMA_SRC_MOT0_0



false

);


trgm_dma_request_config


(

HPM_TRGM0



0



18

);


pwm_enable_dma_request


(

HPM_PWM0




PWM_IRQ_CMP


(

18

));


synt_enable_counter


(

HPM_SYNT



true

);



pwm_start_counter


(

HPM_PWM0

);



dmamux_enable_channel


(

HPM_DMAMUX



DMAMUX_MUXCFG_HDMA_MUX0

);



dma_enable_channel


(

HPM_HDMA



0

);



}






























设置DMA中断,在输出波形结束以后会触发中断响应函数。


void


isr_dma


(


void


)



{



uint32_t

stat

;

stat

=


dma_check_transfer_status


(

HPM_HDMA



0

);


if


(

0

!=


(

stat

&

DMA_CHANNEL_STATUS_TC

))


{



printf


(


"Transfer done!"


);



}



}



SDK_DECLARE_EXT_ISR_M


(

BOARD_APP_HDMA_IRQ



isr_dma

)


配置CLK时钟

以PWM比较器产生需要的波形周期。


/**



* u8Phase from 1~18, 其中设置1 == 设置18, 设置1时 上升沿的边与数据的开始时刻对齐,随着设置的数字增大,上升沿的边向右移动,设置8时,上升沿的边大约在中间。



* */



static


int


zh_led_clk_config


(


uint8_t

u8Phase

)



{



pwm_cmp_config_t

cmp_config_ch0

[

4

]


=


{

0

};



pwm_config_t

pwm_config

=


{

0

};



uint32_t

u32CmpValue

[

4

];


switch


(

u8Phase

)



{



case

1

:


u32CmpValue

[

0

]


=

6

;


u32CmpValue

[

1

]


=

16

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

2

:


u32CmpValue

[

0

]


=

7

;


u32CmpValue

[

1

]


=

18

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

3

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

0

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

8

;



break


;



case

4

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

1

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

9

;



break


;



case

5

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

2

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

10

;



break


;



case

6

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

3

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

11

;



break


;



case

7

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

4

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

12

;



break


;



case

8

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

5

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

13

;



break


;



case

9

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

6

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

14

;



break


;



case

10

:


u32CmpValue

[

0

]


=

18

;


u32CmpValue

[

1

]


=

7

;


u32CmpValue

[

2

]


=

17

;


u32CmpValue

[

3

]


=

15

;



break


;



case

11

:


u32CmpValue

[

0

]


=

18

;


u32CmpValue

[

1

]


=

8

;


u32CmpValue

[

2

]


=

17

;


u32CmpValue

[

3

]


=

16

;



break


;



case

12

:


u32CmpValue

[

0

]


=

17

;


u32CmpValue

[

1

]


=

9

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

13

:


u32CmpValue

[

0

]


=

0

;


u32CmpValue

[

1

]


=

10

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

14

:


u32CmpValue

[

0

]


=

1

;


u32CmpValue

[

1

]


=

11

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

15

:


u32CmpValue

[

0

]


=

2

;


u32CmpValue

[

1

]


=

12

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

16

:


u32CmpValue

[

0

]


=

3

;


u32CmpValue

[

1

]


=

13

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

17

:


u32CmpValue

[

0

]


=

4

;


u32CmpValue

[

1

]


=

14

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



case

18

:


u32CmpValue

[

0

]


=

5

;


u32CmpValue

[

1

]


=

15

;


u32CmpValue

[

2

]


=

18

;


u32CmpValue

[

3

]


=

18

;



break


;



break


;



default


:



break


;



}


pwm_stop_counter


(

HPM_PWM0

);



pwm_set_reload


(

HPM_PWM0



0



zh_led_PWM_FREQ

-

1

);



pwm_set_start_count


(

HPM_PWM0



0



0

);

pwm_config

.

enable_output

=

true

;


pwm_config

.

invert_output

=

false

;


pwm_config

.

update_trigger

=

pwm_shadow_register_update_on_modify

;


pwm_config

.

fault_mode

=

pwm_fault_mode_force_output_highz

;


pwm_config

.

fault_recovery_trigger

=

pwm_fault_recovery_on_fault_clear

;


pwm_config

.

force_source

=

pwm_force_source_software

;


pwm_config

.

dead_zone_in_half_cycle

=

0

;


/*cmp0 cmp1 cmp2 cmp3 for pwm ch0*/


cmp_config_ch0

[

0

].

cmp

=

u32CmpValue

[

0

];


cmp_config_ch0

[

0

].

enable_ex_cmp

=

false

;


cmp_config_ch0

[

0

].

mode

=

pwm_cmp_mode_output_compare

;


cmp_config_ch0

[

0

].

update_trigger

=

pwm_shadow_register_update_on_hw_event

;


cmp_config_ch0

[

0

].

ex_cmp

=

0

;


cmp_config_ch0

[

0

].

half_clock_cmp

=

0

;


cmp_config_ch0

[

0

].

jitter_cmp

=

0

;

cmp_config_ch0

[

1

].

cmp

=

u32CmpValue

[

1

];


cmp_config_ch0

[

1

].

enable_ex_cmp

=

false

;


cmp_config_ch0

[

1

].

mode

=

pwm_cmp_mode_output_compare

;


cmp_config_ch0

[

1

].

update_trigger

=

pwm_shadow_register_update_on_hw_event

;


cmp_config_ch0

[

1

].

ex_cmp

=

0

;


cmp_config_ch0

[

1

].

half_clock_cmp

=

0

;


cmp_config_ch0

[

1

].

jitter_cmp

=

0

;

cmp_config_ch0

[

2

].

cmp

=

u32CmpValue

[

2

];


cmp_config_ch0

[

2

].

enable_ex_cmp

=

false

;


cmp_config_ch0

[

2

].

mode

=

pwm_cmp_mode_output_compare

;


cmp_config_ch0

[

2

].

update_trigger

=

pwm_shadow_register_update_on_hw_event

;


cmp_config_ch0

[

2

].

ex_cmp

=

0

;


cmp_config_ch0

[

2

].

half_clock_cmp

=

0

;


cmp_config_ch0

[

2

].

jitter_cmp

=

0

;

cmp_config_ch0

[

3

].

cmp

=

u32CmpValue

[

3

];


cmp_config_ch0

[

3

].

enable_ex_cmp

=

false

;


cmp_config_ch0

[

3

].

mode

=

pwm_cmp_mode_output_compare

;


cmp_config_ch0

[

3

].

update_trigger

=

pwm_shadow_register_update_on_hw_event

;


cmp_config_ch0

[

3

].

ex_cmp

=

0

;


cmp_config_ch0

[

3

].

half_clock_cmp

=

0

;


cmp_config_ch0

[

3

].

jitter_cmp

=

0

;



if


(

status_success

!=


pwm_setup_waveform


(

HPM_PWM0



1




&

pwm_config



0



cmp_config_ch0



4

))


{



printf


(


"failed to setup waveform for ch0\n"


);



return

status_fail

;



}

cmp_config_ch0

[

0

].

cmp

=

zh_led_PWM_FREQ

-

1

;


cmp_config_ch0

[

0

].

update_trigger

=

pwm_shadow_register_update_on_modify

;



pwm_load_cmp_shadow_on_match


(

HPM_PWM0



4




&

cmp_config_ch0

[

0

]);


pwm_issue_shadow_register_lock_event


(

HPM_PWM0

);


/* enable pwm fault protect */



pwm_fault_source_config_t

config

;



//config.external_fault_active_low = false;


config

.

source_mask

=

PWM_GCR_FAULTI0EN_MASK

;


config

.

fault_recover_at_rising_edge

=

false

;


config

.

fault_output_recovery_trigger

=

0

;



pwm_config_fault_source


(

HPM_PWM0




&

config

);


return

status_success

;



}


























配置TRGM互联管理器

互联管理器TRGM以PWM的周期来触发DMA进行数据搬运。


static


void


zh_led_trgm_config


(


void


)



{



trgm_output_t

stTrgmOutput

;

stTrgmOutput

.

invert

=

false

;


stTrgmOutput

.

type

=

trgm_output_same_as_input

;

stTrgmOutput

.

input

=

44

;



trgm_output_config


(

HPM_TRGM0



14




&

stTrgmOutput

);


pwm_enable_reload_at_synci


(

HPM_PWM0

);


synt_reset_counter


(

HPM_SYNT

);



synt_set_reload


(

HPM_SYNT



zh_led_PWM_FREQ

-

1

);



synt_set_comparator


(

HPM_SYNT



0



zh_led_PWM_FREQ

-

1

);



}








测试程序如下:


int


main


(


void


)



{



unsigned


int

j

=

3

;



board_init


();



zh_led_gpio_config


();



zh_led_clk_config


(

2

);



zh_led_trgm_config


();



zh_led_dma_config


();


printf


(


"start\n"


);


while


(

j

--


)



{



printf


(


"j = %d\n"




j

);


zh_led_dma_config


();



pwm_start_counter


(

HPM_PWM0

);


board_delay_ms


(

500

);



};


printf


(


"stop\n"


);


while


(

1

);


return

0

;



}





串口打印结果如下:

HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能! (https://ic.work/) 技术资料 第2张


测量GPIO的波形:

HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能! (https://ic.work/) 技术资料 第3张
HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能! (https://ic.work/) 技术资料 第4张

因为HDMA访问AHB SRAM速度更快,可以将数据存储到AHB SRAM来提高刷新速度。


__attribute__


((


section


(


".ahb_sram"


)))


uint32_t

g_u32LedBufForGpio32

[

1024

]


=


{

0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0



0xFFFFFFFF



0

};

使用逻辑分析仪测试波形如下:

HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能! (https://ic.work/) 技术资料 第5张

用示波器测量波形如下图所示,信号质量表现优异。

HPMicro波形DMA推GPIO定制指南,简洁高效,吸引你探索更多可能! (https://ic.work/) 技术资料 第6张

文章推荐

相关推荐