diff --git a/bsp/gd32/arm/libraries/gd32_drivers/Kconfig b/bsp/gd32/arm/libraries/gd32_drivers/Kconfig index d96f903fa2d..c20a5a16a6f 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/Kconfig +++ b/bsp/gd32/arm/libraries/gd32_drivers/Kconfig @@ -24,6 +24,19 @@ if BSP_USING_USBD # "ULPI: UTMI+ Low Pin Interface" endif +if BSP_USING_HARD_I2C + choice + prompt "Select I2C Receiving Scheme" + default BSP_USING_RECEIVING_A + + config BSP_USING_RECEIVING_A + bool "master receiving secheme A --- requires that the software be capable of responding quickly to the 12C event." + + config BSP_USING_RECEIVING_B + bool "master receiving secheme B --- don't requires that the software be capable of responding quickly to the 12C event." + endchoice +endif + config BSP_USING_GD_DBG bool default y diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c index 725b306fde5..b4a5d029fec 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_hard_i2c.c @@ -6,6 +6,8 @@ * Change Logs: * Date Author Notes * 2021-12-20 BruceOu the first version + * 2026-01-11 ShiHongchao Fix the I2C master receive mode B software + * flow and add support for mode A */ #include "drv_hard_i2c.h" @@ -112,8 +114,8 @@ static const struct gd32_i2c_bus gd_i2c_config[] = { RCU_I2C0, RCU_GPIOB, RCU_GPIOB, /* periph clock, scl gpio clock, sda gpio clock */ - GPIOB, GPIO_AF_4, GPIO_PIN_6, /* scl port, scl alternate, scl pin */ - GPIOB, GPIO_AF_4, GPIO_PIN_7, /* sda port, sda alternate, sda pin */ + GPIOB, GPIO_AF_4, GPIO_PIN_8, /* scl port, scl alternate, scl pin */ + GPIOB, GPIO_AF_4, GPIO_PIN_9, /* sda port, sda alternate, sda pin */ &i2c0, "hwi2c0", @@ -229,13 +231,52 @@ static void gd32_i2c_gpio_init(const struct gd32_i2c_bus *i2c) static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_uint16_t data_byte) { if (data_byte == 0) return 1; - /* while there is data to be read */ +#ifdef BSP_USING_RECEIVING_A + /* + In single-byte reception, disable ACK because the master needs to send + NACK after receiving the first byte,indicating no more data will be + received, then immediately send the stop condition + */ + if(data_byte == 1) + { + /* disable acknowledge */ + i2c_ack_config(i2c_periph, I2C_ACK_DISABLE); + /* send a stop condition to I2C bus */ + i2c_stop_on_bus(i2c_periph); + } +#endif + + /* while there is data to be read */ while(data_byte) { #if defined (SOC_SERIES_GD32F5xx) || defined (SOC_SERIES_GD32F4xx) if(IS_I2C_LEGACY(i2c_periph)) { +#ifdef BSP_USING_RECEIVING_A + /* + After receiving the second-to-last byte, ACK should be disabled + and STOP should be set, to ensure that NACK is sent after receiving + the last byte and the stop condition is transmitted + */ + if(2 == data_byte) + { + /* wait until BTC bit is set */ + while(!i2c_flag_get(i2c_periph, I2C_FLAG_RBNE)); + /* disable acknowledge */ + i2c_ack_config(i2c_periph, I2C_ACK_DISABLE); + /* send a stop condition to I2C bus */ + i2c_stop_on_bus(i2c_periph); + } +#elif defined(BSP_USING_RECEIVING_B) + /* + For 3-byte reception: Wait for byte transfer completion, then + disable ACK so NACK is automatically sent after receiving the + last byte + For 2-byte reception: Wait for byte transfer completion, then + send stop condition to ensure direct stop after receiving the + last byte instead of sending ACK + */ if(3 == data_byte) { /* wait until BTC bit is set */ @@ -243,14 +284,16 @@ static uint8_t gd32_i2c_read(rt_uint32_t i2c_periph, rt_uint8_t *p_buffer, rt_ui /* disable acknowledge */ i2c_ack_config(i2c_periph, I2C_ACK_DISABLE); } - - if(2 == data_byte) + else if(2 == data_byte) { /* wait until BTC bit is set */ while(!i2c_flag_get(i2c_periph, I2C_FLAG_BTC)); /* send a stop condition to I2C bus */ i2c_stop_on_bus(i2c_periph); } +#else +#error "Please select the receiving scheme." +#endif /* wait until RBNE bit is set */ if(i2c_flag_get(i2c_periph, I2C_FLAG_RBNE)) { @@ -379,11 +422,18 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_ { i2c_stop_on_bus(gd32_i2c->i2c_periph); } - /* enable acknowledge */ + /* enable acknowledge */ i2c_ack_config(gd32_i2c->i2c_periph, I2C_ACK_ENABLE); - /* i2c master sends start signal only when the bus is idle */ + /* i2c master sends start signal only when the bus is idle */ while(i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY)); - /* send the start signal */ +#ifdef BSP_USING_RECEIVING_B + /* */ + if(msg->len == 2) + { + i2c_ackpos_config(gd32_i2c->i2c_periph, I2C_ACKPOS_NEXT); + } +#endif + /* send the start signal */ i2c_start_on_bus(gd32_i2c->i2c_periph); /* i2c master sends START signal successfully */ while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_SBSEND)); @@ -391,14 +441,26 @@ static rt_ssize_t gd32_i2c_master_xfer(struct rt_i2c_bus_device *bus, struct rt_ i2c_master_addressing(gd32_i2c->i2c_periph, msg->addr, I2C_RECEIVER); while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_ADDSEND)); - /* address flag set means i2c slave sends ACK */ +#ifdef BSP_USING_RECEIVING_B + if(msg->len <= 2) + { + i2c_ack_config(gd32_i2c->i2c_periph, I2C_ACK_DISABLE); + } +#endif + /* address flag set means i2c slave sends ACK */ i2c_flag_clear(gd32_i2c->i2c_periph, I2C_FLAG_ADDSEND); +#ifdef BSP_USING_RECEIVING_B + if(msg->len == 1) + { + i2c_stop_on_bus(gd32_i2c->i2c_periph); + } +#endif }else { - /* configure slave address */ + /* configure slave address */ while(i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_I2CBSY)); - //i2c_transfer_byte_number_config(gd32_i2c->i2c_periph, w_total_byte); - /* send a start condition to I2C bus */ + //i2c_transfer_byte_number_config(gd32_i2c->i2c_periph, w_total_byte); + /* send a start condition to I2C bus */ i2c_start_on_bus(gd32_i2c->i2c_periph); while(!i2c_flag_get(gd32_i2c->i2c_periph, I2C_FLAG_SBSEND)); diff --git a/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c b/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c index a95c5d634a4..79efa6de8d7 100644 --- a/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c +++ b/bsp/gd32/arm/libraries/gd32_drivers/drv_pwm.c @@ -6,15 +6,21 @@ * Change Logs: * Date Author Notes * 2023-06-05 zengjianwei first version - * 2025-06-23 Yucai Liu Support for non-complementary PWM output with advanced timers + * 2025-06-23 Yucai Liu Support for non-complementary PWM output with + * advanced timers + * 2025-12-26 shihongchao Optimize the timer clock frequency acquisition + * method; optimize the gd32_pwm structure to make + * it easier to configure; optimize the RCU enable + * logic; optimize GPIO configuration to maintain + * floating input mode when channels are disabled, + * reducing power consumption. */ #include -#include #include #include -#ifdef RT_USING_PWM +#ifdef BSP_USING_PWM /* #define DRV_DEBUG */ #define LOG_TAG "drv.pwm" @@ -24,208 +30,186 @@ #define MIN_PERIOD 3 #define MIN_PULSE 2 -typedef struct -{ - rt_int8_t TimerIndex; /* timer index:0~13 */ - rt_uint32_t Port; /* gpio port:GPIOA/GPIOB/GPIOC/... */ - rt_uint32_t pin; /* gpio pin:GPIO_PIN_0~GPIO_PIN_15 */ - /* timer channel: -2 is ch_1n, -1 is ch_0n, 0 is ch0, 1 is ch1 */ - rt_int16_t channel; - char *name; -} TIMER_PORT_CHANNEL_MAP_S; +typedef struct{ + uint32_t gpio_port; + uint32_t gpio_af; + uint16_t gpio_pin; +}channel_type; struct gd32_pwm { - struct rt_device_pwm pwm_device; - TIMER_PORT_CHANNEL_MAP_S tim_handle; + struct rt_device_pwm pwm_device; /* Inherit PWM device */ + char *name; /* Device name */ + uint32_t timerx; /* Hardware timer dependent on PWM */ + rcu_clock_freq_enum apb_of; /* APB bus to which TIMER belongs */ + channel_type channels[4]; /* PWM channels */ + channel_type nchannels[3]; /* PWM complementary channels, only supported by advanced timers */ }; static struct gd32_pwm gd32_pwm_obj[] = { -#ifdef RT_USING_PWM1 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm1"}}, -#endif - -#ifdef RT_USING_PWM2 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm2"}}, -#endif - -#ifdef RT_USING_PWM3 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm3"}}, +#ifdef BSP_USING_PWM0 + { + .name = "pwm0", + .timerx = TIMER0, + .apb_of = CK_APB2, + .channels = { + {GPIOC, GPIO_AF_1, GPIO_PIN_0}, + {GPIOA, GPIO_AF_1, GPIO_PIN_1}, + {GPIOA, GPIO_AF_1, GPIO_PIN_2}, + {GPIOA, GPIO_AF_1, GPIO_PIN_3}, + }, + .nchannels = { + {GPIOB, GPIO_AF_1, GPIO_PIN_13}, + {GPIOB, GPIO_AF_1, GPIO_PIN_14}, + {GPIOB, GPIO_AF_1, GPIO_PIN_15}, + } + }, #endif -#ifdef RT_USING_PWM4 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm4"}}, +#ifdef BSP_USING_PWM1 + { + .name = "pwm1", + .timerx = TIMER1, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + {GPIOA, GPIO_AF_1, GPIO_PIN_1}, + {GPIOA, GPIO_AF_1, GPIO_PIN_2}, + {GPIOB, GPIO_AF_1, GPIO_PIN_2}, + }, + }, #endif -#ifdef RT_USING_PWM5 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm5"}}, +#ifdef BSP_USING_PWM2 + { + .name = "pwm2", + .timerx = TIMER2, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_6}, + {GPIOA, GPIO_AF_1, GPIO_PIN_7}, + {GPIOB, GPIO_AF_2, GPIO_PIN_0}, + {GPIOB, GPIO_AF_2, GPIO_PIN_1}, + }, + }, #endif -#ifdef RT_USING_PWM6 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm6"}}, +#ifdef BSP_USING_PWM3 + { + .name = "pwm3", + .timerx = TIMER3, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + {GPIOA, GPIO_AF_1, GPIO_PIN_1}, + {GPIOA, GPIO_AF_1, GPIO_PIN_2}, + {GPIOA, GPIO_AF_1, GPIO_PIN_3}, + }, + }, #endif -#ifdef RT_USING_PWM7 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm7"}}, +#ifdef BSP_USING_PWM4 + { + .name = "pwm4", + .timerx = TIMER4, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_2, GPIO_PIN_0}, + {GPIOA, GPIO_AF_2, GPIO_PIN_1}, + {GPIOA, GPIO_AF_1, GPIO_PIN_2}, + {GPIOA, GPIO_AF_1, GPIO_PIN_3}, + }, + }, #endif -#ifdef RT_USING_PWM8 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm8"}}, +#ifdef BSP_USING_PWM7 + { + .name = "pwm7", + .timerx = TIMER7, + .apb_of = CK_APB2, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + {GPIOA, GPIO_AF_1, GPIO_PIN_1}, + {GPIOC, GPIO_AF_1, GPIO_PIN_8}, + {GPIOA, GPIO_AF_1, GPIO_PIN_3}, + }, + .nchannels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_5}, + {GPIOB, GPIO_AF_1, GPIO_PIN_0}, + {GPIOB, GPIO_AF_1, GPIO_PIN_1}, + } + }, #endif -#ifdef RT_USING_PWM9 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm9"}}, +#ifdef BSP_USING_PWM8 + { + .name = "pwm8", + .timerx = TIMER8, + .apb_of = CK_APB2, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_2}, + {GPIOA, GPIO_AF_3, GPIO_PIN_3}, + }, // L1 general-purpose timer is a two-channel timer + }, #endif -#ifdef RT_USING_PWM10 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm10"}}, +#ifdef BSP_USING_PWM9 + { + .name = "pwm9", + .timerx = TIMER9, + .apb_of = CK_APB2, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + }, // L2 general-purpose timer is a single-channel timer + }, #endif -#ifdef RT_USING_PWM11 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm11"}}, +#ifdef BSP_USING_PWM10 + { + .name = "pwm10", + .timerx = TIMER10, + .apb_of = CK_APB2, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + }, // L2 general-purpose timer is a single-channel timer + }, #endif -#ifdef RT_USING_PWM12 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm12"}}, +#ifdef BSP_USING_PWM11 + { + .name = "pwm11", + .timerx = TIMER11, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + {GPIOA, GPIO_AF_1, GPIO_PIN_1}, + }, // L1 general-purpose timer is a two-channel timer + }, #endif -#ifdef RT_USING_PWM13 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm13"}}, +#ifdef BSP_USING_PWM12 + { + .name = "pwm12", + .timerx = TIMER12, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_0}, + }, // L2 general-purpose timer is a single-channel timer + }, #endif -#ifdef RT_USING_PWM14 - {.tim_handle = {3, GPIOB, GPIO_PIN_8, 2, "pwm14"}}, +#ifdef BSP_USING_PWM13 + { + .name = "pwm13", + .timerx = TIMER13, + .apb_of = CK_APB1, + .channels = { + {GPIOA, GPIO_AF_1, GPIO_PIN_7}, + }, // L2 general-purpose timer is a single-channel timer + }, #endif }; -typedef struct -{ - rt_uint32_t Port[7]; - rt_int8_t TimerIndex[14]; -} TIMER_PERIPH_LIST_S; - -static TIMER_PERIPH_LIST_S gd32_timer_periph_list = { - .Port = {0, 0, 0, 0, 0, 0, 0}, - .TimerIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, -}; - -/* - * 将所有用到的 gpio port 和 timer 不重复地列举出来,以方便后面不重复地初始化 - */ -static rt_err_t pwm_find_timer_periph(void) -{ - rt_int16_t i, j, k; - - /* find gpio port of defined table */ - for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) - { - /* find -1 of gd32_periph_list's member of Port */ - for (j = 0; j < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++j) - { - if (0 == gd32_timer_periph_list.Port[j]) - { - break; - } - } - - if (j >= sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0])) - { - LOG_E("Can not find -1 of gd32_periph_list's member of Port!\n"); - break; - } - - /* find the different of Port */ - for (k = 0; k < j; ++k) - { - if (gd32_pwm_obj[i].tim_handle.Port == gd32_timer_periph_list.Port[k]) - { - break; - } - } - - /* if can not find the same Port */ - if (k == j) - { - gd32_timer_periph_list.Port[j] = gd32_pwm_obj[i].tim_handle.Port; - } - } - - /* find timer periph of defined table */ - for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) - { - /* find -1 of gd32_periph_list's member of TimerIndex */ - for (j = 0; j < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++j) - { - if (-1 == gd32_timer_periph_list.TimerIndex[j]) - { - break; - } - } - - if (j >= sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0])) - { - LOG_E("Can not find -1 of gd32_periph_list's member of TimerIndex!\n"); - break; - } - - /* find the different of TimerIndex */ - for (k = 0; k < j; ++k) - { - if (gd32_pwm_obj[i].tim_handle.TimerIndex == gd32_timer_periph_list.TimerIndex[k]) - { - break; - } - } - - /* if can not find the same TimerIndex */ - if (k == j) - { - gd32_timer_periph_list.TimerIndex[j] = gd32_pwm_obj[i].tim_handle.TimerIndex; - } - } - - return RT_EOK; -} - -static rt_uint32_t index_to_timer(rt_int8_t TimerIndex) -{ - switch (TimerIndex) - { - case 0: - return TIMER0; - case 1: - return TIMER1; - case 2: - return TIMER2; - case 3: - return TIMER3; - case 4: - return TIMER4; - case 5: - return TIMER5; - case 6: - return TIMER6; - case 7: - return TIMER7; - case 8: - return TIMER8; - case 9: - return TIMER9; - case 10: - return TIMER10; - case 11: - return TIMER11; - case 12: - return TIMER12; - case 13: - return TIMER13; - - default: - LOG_E("Unsport timer periph!\n"); - } - return TIMER0; -} - static void gpio_clock_enable(rt_uint32_t Port) { switch (Port) @@ -257,54 +241,52 @@ static void gpio_clock_enable(rt_uint32_t Port) } } -static void timer_clock_enable(rt_int8_t TimerIndex) +static void timer_clock_enable(uint32_t timer) { - switch (TimerIndex) + switch (timer) { - case 0: + case TIMER0: rcu_periph_clock_enable(RCU_TIMER0); break; - case 1: + case TIMER1: rcu_periph_clock_enable(RCU_TIMER1); break; - case 2: + case TIMER2: rcu_periph_clock_enable(RCU_TIMER2); break; - case 3: + case TIMER3: rcu_periph_clock_enable(RCU_TIMER3); break; - case 4: + case TIMER4: rcu_periph_clock_enable(RCU_TIMER4); break; - case 5: + case TIMER5: rcu_periph_clock_enable(RCU_TIMER5); break; - case 6: + case TIMER6: rcu_periph_clock_enable(RCU_TIMER6); break; - case 7: + case TIMER7: rcu_periph_clock_enable(RCU_TIMER7); break; -#ifndef GD32F30X_HD - case 8: + case TIMER8: rcu_periph_clock_enable(RCU_TIMER8); break; - case 9: + case TIMER9: rcu_periph_clock_enable(RCU_TIMER9); break; - case 10: + case TIMER10: rcu_periph_clock_enable(RCU_TIMER10); break; - case 11: + case TIMER11: rcu_periph_clock_enable(RCU_TIMER11); break; - case 12: + case TIMER12: rcu_periph_clock_enable(RCU_TIMER12); break; - case 13: + case TIMER13: rcu_periph_clock_enable(RCU_TIMER13); break; -#endif default: LOG_E("Unsport timer periph!\n"); } @@ -314,96 +296,191 @@ static void rcu_config(void) { rt_int16_t i; - for (i = 0; i < sizeof(gd32_timer_periph_list.Port) / sizeof(gd32_timer_periph_list.Port[0]); ++i) + for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) { - if (0 == gd32_timer_periph_list.Port[i]) + /* enable GPIO clock */ + switch (gd32_pwm_obj[i].timerx) { + /* Advanced timer */ + case TIMER0: + case TIMER7: + gpio_clock_enable(gd32_pwm_obj[i].nchannels[0].gpio_port); + gpio_clock_enable(gd32_pwm_obj[i].nchannels[1].gpio_port); + gpio_clock_enable(gd32_pwm_obj[i].nchannels[2].gpio_port); + + /* L0 general-purpose timer */ + case TIMER1: + case TIMER2: + case TIMER3: + case TIMER4: + gpio_clock_enable(gd32_pwm_obj[i].channels[2].gpio_port); + gpio_clock_enable(gd32_pwm_obj[i].channels[3].gpio_port); + + /* L1 general-purpose timer */ + case TIMER8: + case TIMER11: + gpio_clock_enable(gd32_pwm_obj[i].channels[1].gpio_port); + + /* L2 general-purpose timer */ + case TIMER9: + case TIMER10: + case TIMER12: + case TIMER13: + gpio_clock_enable(gd32_pwm_obj[i].channels[0].gpio_port); break; - } - - /* enable GPIO clock */ - gpio_clock_enable(gd32_timer_periph_list.Port[i]); - } - - rcu_periph_clock_enable(RCU_AF); - for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i) - { - if (-1 == gd32_timer_periph_list.TimerIndex[i]) - { + default: + LOG_E("Unsport timer periph at rcu_config!\n"); break; } + } + for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) + { /* enable timer clock */ - timer_clock_enable(gd32_timer_periph_list.TimerIndex[i]); - timer_deinit(index_to_timer(gd32_timer_periph_list.TimerIndex[i])); + timer_clock_enable(gd32_pwm_obj[i].timerx); + timer_deinit(gd32_pwm_obj[i].timerx); } } -static void gpio_config(void) +/** + * @brief Configure PWM output pin to PWM output mode + * @param pwm PWM object + * @param configuration Configuration information passed by PWM driver framework + */ +static void gpio_config_pwmout(const struct gd32_pwm *pwm, + const struct rt_pwm_configuration *configuration) { - rt_int16_t i; - - /* config the GPIO as analog mode */ - for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) + channel_type channel; + uint8_t channel_num = configuration->channel; + if(channel_num == 0) { - gpio_init(gd32_pwm_obj[i].tim_handle.Port, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd32_pwm_obj[i].tim_handle.pin); + LOG_E("PWM channel starts from 1!\n"); + return; } + if(configuration->complementary) + { + if(channel_num > 3) + { + LOG_E("GD32 PWM complementary channel max 3!\n"); + return; + }; + channel = pwm->nchannels[channel_num-1]; + } + else + { + if(channel_num > 4) + { + LOG_E("GD32 PWM complementary channel max 4!\n"); + return; + }; + channel = pwm->channels[channel_num-1]; + } + gpio_mode_set(channel.gpio_port, GPIO_MODE_AF, GPIO_PUPD_NONE, channel.gpio_pin); + gpio_output_options_set(channel.gpio_port, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, channel.gpio_pin); + gpio_af_set(channel.gpio_port, channel.gpio_af, channel.gpio_pin); } -static void timer_init_para(timer_parameter_struct *initpara) +/** + * @brief Configure PWM output pin to PWM floating input mode + * @param pwm PWM object + * @param configuration Configuration information passed by PWM driver framework + */ +static void gpio_config_input(const struct gd32_pwm *pwm, + const struct rt_pwm_configuration *configuration) { - rt_int16_t i; - - for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i) + channel_type channel = {0}; + uint8_t channel_num = configuration->channel; + if(channel_num == 0) { - /* config timer */ - if (-1 != gd32_timer_periph_list.TimerIndex[i]) + return; + } + if(configuration->complementary) + { + if(channel_num > 3) { - timer_init(index_to_timer(gd32_timer_periph_list.TimerIndex[i]), initpara); - } + LOG_E("GD32 PWM complementary channel max 3!\n"); + return; + }; + channel = pwm->nchannels[channel_num-1]; + } + else + { + if(channel_num > 4) + { + LOG_E("GD32 PWM complementary channel max 4!\n"); + return; + }; + channel = pwm->channels[channel_num-1]; } + gpio_mode_set(channel.gpio_port, GPIO_MODE_INPUT, GPIO_PUPD_NONE, channel.gpio_pin); } -static void channel_output_config(timer_oc_parameter_struct *ocpara) +static void channel_output_config(rt_uint32_t timer_periph, timer_oc_parameter_struct *ocpara) { - rt_int16_t i; - rt_uint32_t timer_periph; - - /* config the channel config */ - for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) + switch (timer_periph) { - if (gd32_pwm_obj[i].tim_handle.channel < 0) - { - ocpara->outputstate = TIMER_CCX_DISABLE; - ocpara->outputnstate = TIMER_CCXN_ENABLE; - gd32_pwm_obj[i].tim_handle.channel = -(gd32_pwm_obj[i].tim_handle.channel + 1); - } - timer_periph = index_to_timer(gd32_pwm_obj[i].tim_handle.TimerIndex); - timer_channel_output_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, ocpara); + /* Advanced timer */ + case TIMER0: + case TIMER7: + timer_primary_output_config(timer_periph, ENABLE); + + /* L0 general-purpose timer */ + case TIMER1: + case TIMER2: + case TIMER3: + case TIMER4: + timer_channel_output_config(timer_periph, TIMER_CH_2, ocpara); + timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_2, 7999); + timer_channel_output_mode_config(timer_periph, TIMER_CH_2, TIMER_OC_MODE_PWM0); + timer_channel_output_shadow_config(timer_periph, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE); + /* auto-reload preload shadow reg enable */ + /* timer_auto_reload_shadow_enable(timer_periph); */ + timer_channel_output_state_config(timer_periph, TIMER_CH_2, TIMER_CCX_DISABLE); + timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_2, TIMER_CCXN_DISABLE); - timer_channel_output_pulse_value_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, 7999); - timer_channel_output_mode_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_OC_MODE_PWM0); - timer_channel_output_shadow_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_OC_SHADOW_DISABLE); + timer_channel_output_config(timer_periph, TIMER_CH_3, ocpara); + timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_3, 7999); + timer_channel_output_mode_config(timer_periph, TIMER_CH_3, TIMER_OC_MODE_PWM0); + timer_channel_output_shadow_config(timer_periph, TIMER_CH_3, TIMER_OC_SHADOW_DISABLE); /* auto-reload preload shadow reg enable */ /* timer_auto_reload_shadow_enable(timer_periph); */ - timer_channel_output_state_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_CCX_DISABLE); - timer_channel_complementary_output_state_config(timer_periph, gd32_pwm_obj[i].tim_handle.channel, TIMER_CCXN_DISABLE); - } + timer_channel_output_state_config(timer_periph, TIMER_CH_3, TIMER_CCX_DISABLE); + timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_3, TIMER_CCXN_DISABLE); + + /* L1 general-purpose timer */ + case TIMER8: + case TIMER11: + timer_channel_output_config(timer_periph, TIMER_CH_1, ocpara); + timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_1, 7999); + timer_channel_output_mode_config(timer_periph, TIMER_CH_1, TIMER_OC_MODE_PWM0); + timer_channel_output_shadow_config(timer_periph, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE); + /* auto-reload preload shadow reg enable */ + /* timer_auto_reload_shadow_enable(timer_periph); */ + timer_channel_output_state_config(timer_periph, TIMER_CH_1, TIMER_CCX_DISABLE); + timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_1, TIMER_CCXN_DISABLE); + + /* L2 general-purpose timer */ + case TIMER9: + case TIMER10: + case TIMER12: + case TIMER13: + timer_channel_output_config(timer_periph, TIMER_CH_0, ocpara); + timer_channel_output_pulse_value_config(timer_periph, TIMER_CH_0, 7999); + timer_channel_output_mode_config(timer_periph, TIMER_CH_0, TIMER_OC_MODE_PWM0); + timer_channel_output_shadow_config(timer_periph, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE); + /* auto-reload preload shadow reg enable */ + /* timer_auto_reload_shadow_enable(timer_periph); */ + timer_channel_output_state_config(timer_periph, TIMER_CH_0, TIMER_CCX_DISABLE); + timer_channel_complementary_output_state_config(timer_periph, TIMER_CH_0, TIMER_CCXN_DISABLE); + break; - /* enable timer */ - for (i = 0; i < sizeof(gd32_timer_periph_list.TimerIndex) / sizeof(gd32_timer_periph_list.TimerIndex[0]); ++i) - { - if (-1 != gd32_timer_periph_list.TimerIndex[i]) - { - timer_periph = index_to_timer(gd32_timer_periph_list.TimerIndex[i]); - if (timer_periph == TIMER0 || timer_periph == TIMER7) - { - timer_primary_output_config(timer_periph, ENABLE); - } - timer_enable(timer_periph); - } + default: + LOG_E("Unsport timer periph at channel_output_config!\n"); + break; } + + timer_enable(timer_periph); } static void timer_config(void) @@ -412,58 +489,103 @@ static void timer_config(void) timer_parameter_struct timer_initpara; /* TIMER configuration */ - timer_initpara.prescaler = 119; + timer_initpara.prescaler = 199; timer_initpara.alignedmode = TIMER_COUNTER_EDGE; timer_initpara.counterdirection = TIMER_COUNTER_UP; timer_initpara.period = 15999; timer_initpara.clockdivision = TIMER_CKDIV_DIV1; timer_initpara.repetitioncounter = 0; - timer_init_para(&timer_initpara); + for (size_t i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) + { + timer_init(gd32_pwm_obj[i].timerx, &timer_initpara); + } /* CHX configuration in PWM mode */ - timer_ocintpara.outputstate = TIMER_CCX_ENABLE; + timer_ocintpara.outputstate = TIMER_CCX_DISABLE; timer_ocintpara.outputnstate = TIMER_CCXN_DISABLE; timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH; timer_ocintpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH; timer_ocintpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW; timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW; - channel_output_config(&timer_ocintpara); + /* config the channel config */ + for (size_t i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); ++i) + { + channel_output_config(gd32_pwm_obj[i].timerx, &timer_ocintpara); + } } -static rt_err_t drv_pwm_enable(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration, +static rt_err_t drv_pwm_enable(struct gd32_pwm *pwm, const struct rt_pwm_configuration *configuration, rt_bool_t enable) { + if(channel_num == 0) + { + LOG_E("GD32 PWM channel starts from 1!\n"); + return -RT_EINVAL; + } + if(configuration->complementary) + { + if(channel_num > 3) + { + LOG_E("GD32 PWM complementary channel max 3!\n"); + return -RT_EINVAL; + }; + } + else + { + if(channel_num > 4) + { + LOG_E("GD32 PWM complementary channel max 4!\n"); + return -RT_EINVAL; + }; + } + if (!enable) { - timer_channel_output_state_config(index_to_timer(pstTimerMap->TimerIndex), configuration->channel, - TIMER_CCX_DISABLE); + gpio_config_input(pwm, configuration); + if (configuration->complementary == RT_TRUE) + { + timer_channel_complementary_output_state_config(pwm->timerx, configuration->channel-1, + TIMER_CCXN_DISABLE); + } + else + { + timer_channel_output_state_config(pwm->timerx, configuration->channel-1, + TIMER_CCX_DISABLE); + } } else { + gpio_config_pwmout(pwm, configuration); if (configuration->complementary == RT_TRUE) { - timer_channel_output_state_config( - index_to_timer(pstTimerMap->TimerIndex), configuration->channel - 1, TIMER_CCXN_ENABLE); + timer_channel_complementary_output_state_config(pwm->timerx, configuration->channel-1, + TIMER_CCXN_ENABLE); } else { - timer_channel_output_state_config( - index_to_timer(pstTimerMap->TimerIndex), configuration->channel, TIMER_CCX_ENABLE); + timer_channel_output_state_config(pwm->timerx, configuration->channel-1, + TIMER_CCX_ENABLE); } } return RT_EOK; } -static rt_err_t drv_pwm_get(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration) +static rt_err_t drv_pwm_get(const struct gd32_pwm *pwm, struct rt_pwm_configuration *configuration) { rt_uint64_t tim_clock; rt_uint16_t psc; rt_uint32_t chxcv; + + if(configuration->channel == 0) + { + return -RT_EINVAL; + } - tim_clock = rcu_clock_freq_get(CK_SYS); + rt_uint8_t coef = (RCU_CFG1&RCU_CFG1_TIMERSEL)?4:2; + tim_clock = rcu_clock_freq_get(pwm->apb_of)*coef; - psc = timer_prescaler_read(index_to_timer(pstTimerMap->TimerIndex)); + psc = timer_prescaler_read(pwm->timerx); if (psc == TIMER_CKDIV_DIV2) { tim_clock = tim_clock / 2; @@ -473,21 +595,27 @@ static rt_err_t drv_pwm_get(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm tim_clock = tim_clock / 4; } - chxcv = timer_channel_capture_value_register_read(index_to_timer(pstTimerMap->TimerIndex), configuration->channel); + chxcv = timer_channel_capture_value_register_read(pwm->timerx, configuration->channel-1); /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */ tim_clock /= 1000000UL; - configuration->period = (TIMER_CAR(index_to_timer(pstTimerMap->TimerIndex)) + 1) * (psc + 1) * 1000UL / tim_clock; + configuration->period = (TIMER_CAR(pwm->timerx) + 1) * (psc + 1) * 1000UL / tim_clock; configuration->pulse = (chxcv + 1) * (psc + 1) * 1000UL / tim_clock; return RT_EOK; } -static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm_configuration *configuration) +static rt_err_t drv_pwm_set(struct gd32_pwm *pwm, struct rt_pwm_configuration *configuration) { rt_uint32_t period, pulse; rt_uint64_t tim_clock, psc; - tim_clock = rcu_clock_freq_get(CK_SYS); + rt_uint8_t coef = (RCU_CFG1&RCU_CFG1_TIMERSEL)?4:2; + tim_clock = rcu_clock_freq_get(pwm->apb_of)*coef; + + if(configuration->channel == 0) + { + return -RT_EINVAL; + } /* Convert nanosecond to frequency and duty cycle. 1s = 1 * 1000 * 1000 * 1000 ns */ tim_clock /= 1000000UL; @@ -495,14 +623,14 @@ static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm psc = period / MAX_PERIOD + 1; period = period / psc; - timer_prescaler_config(index_to_timer(pstTimerMap->TimerIndex), psc - 1, TIMER_PSC_RELOAD_NOW); + timer_prescaler_config(pwm->timerx, psc - 1, TIMER_PSC_RELOAD_NOW); if (period < MIN_PERIOD) { period = MIN_PERIOD; } - timer_autoreload_value_config(index_to_timer(pstTimerMap->TimerIndex), period - 1); + timer_autoreload_value_config(pwm->timerx, period - 1); pulse = (unsigned long long)configuration->pulse * tim_clock / psc / 1000ULL; if (pulse < MIN_PULSE) @@ -514,11 +642,11 @@ static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm pulse = period; } - timer_channel_output_pulse_value_config(index_to_timer(pstTimerMap->TimerIndex), configuration->channel, pulse); - timer_counter_value_config(index_to_timer(pstTimerMap->TimerIndex), 0); + timer_channel_output_pulse_value_config(pwm->timerx, configuration->channel-1, pulse); + timer_counter_value_config(pwm->timerx, 0); /* Update frequency value */ - timer_event_software_generate(index_to_timer(pstTimerMap->TimerIndex), TIMER_EVENT_SRC_UPG); + timer_event_software_generate(pwm->timerx, TIMER_EVENT_SRC_UPG); return RT_EOK; } @@ -526,18 +654,18 @@ static rt_err_t drv_pwm_set(TIMER_PORT_CHANNEL_MAP_S *pstTimerMap, struct rt_pwm static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg) { struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg; - TIMER_PORT_CHANNEL_MAP_S *pstTimerMap = (TIMER_PORT_CHANNEL_MAP_S *)device->parent.user_data; + struct gd32_pwm *pwm = rt_container_of(device, struct gd32_pwm, pwm_device); switch (cmd) { case PWM_CMD_ENABLE: - return drv_pwm_enable(pstTimerMap, configuration, RT_TRUE); + return drv_pwm_enable(pwm, configuration, RT_TRUE); case PWM_CMD_DISABLE: - return drv_pwm_enable(pstTimerMap, configuration, RT_FALSE); + return drv_pwm_enable(pwm, configuration, RT_FALSE); case PWM_CMD_SET: - return drv_pwm_set(pstTimerMap, configuration); + return drv_pwm_set(pwm, configuration); case PWM_CMD_GET: - return drv_pwm_get(pstTimerMap, configuration); + return drv_pwm_get(pwm, configuration); default: return -RT_EINVAL; } @@ -547,15 +675,19 @@ static struct rt_pwm_ops drv_ops = {drv_pwm_control}; static rt_err_t gd32_hw_pwm_init(void) { - pwm_find_timer_periph(); rcu_config(); - gpio_config(); timer_config(); + /* + * GPIO is not configured here. When PWM channel is enabled, it will be configured as PWM output. + * When disabled, it will be configured as floating input. + * GPIO defaults to floating input. + */ + return RT_EOK; } -static int gd32_pwm_init(void) +static int rt_hw_pwm_init(void) { int i = 0; int result = RT_EOK; @@ -573,14 +705,14 @@ static int gd32_pwm_init(void) for (i = 0; i < sizeof(gd32_pwm_obj) / sizeof(gd32_pwm_obj[0]); i++) { /* register pwm device */ - if (rt_device_pwm_register(&gd32_pwm_obj[i].pwm_device, gd32_pwm_obj[i].tim_handle.name, &drv_ops, - &gd32_pwm_obj[i].tim_handle)== RT_EOK ) + if (rt_device_pwm_register(&gd32_pwm_obj[i].pwm_device, gd32_pwm_obj[i].name, &drv_ops, + RT_NULL)== RT_EOK ) { - LOG_D("%s register success", gd32_pwm_obj[i].tim_handle.name); + LOG_D("%s register success", gd32_pwm_obj[i].name); } else { - LOG_E("%s register failed", gd32_pwm_obj[i].tim_handle.name); + LOG_E("%s register failed", gd32_pwm_obj[i].name); result = -RT_ERROR; } } @@ -588,6 +720,5 @@ static int gd32_pwm_init(void) __exit: return result; } -INIT_DEVICE_EXPORT(gd32_pwm_init); -#endif /* RT_USING_PWM */ - +INIT_DEVICE_EXPORT(rt_hw_pwm_init); +#endif /* BSP_USING_PWM */