/* * @File name : driver_gpio.c * @Author : Bluetrum IOT Team * @Date : 2023-02-13 * @Description : This file provides functions to manage the most functionalities * of the GPIO peripheral. * * Copyright (c) by Bluetrum, All Rights Reserved. */ #include "driver_gpio.h" const gpio_edge_cap_typedef separate_wakeup_io[WAKEUP_IDX_MAX] = { /*edge is useless from PA7 to PB4 */ [WAKEUP_IDX_PA7] = {GPIO_EDGE_RISING, GPIOA_REG, GPIO_PIN_7}, [WAKEUP_IDX_PB1] = {GPIO_EDGE_RISING, GPIOB_REG, GPIO_PIN_1}, [WAKEUP_IDX_PB2] = {GPIO_EDGE_RISING, GPIOB_REG, GPIO_PIN_2}, [WAKEUP_IDX_PB3] = {GPIO_EDGE_RISING, GPIOB_REG, GPIO_PIN_3}, [WAKEUP_IDX_PB4] = {GPIO_EDGE_RISING, GPIOB_REG, GPIO_PIN_4}, /*WK0 no used in this array*/ [WAKEUP_IDX_WK0] = {GPIO_EDGE_RISING, NULL, 0}, [WAKEUP_IDX_PORT_FALL] = {GPIO_EDGE_FALLING, NULL, 0}, [WAKEUP_IDX_PORT_RISE] = {GPIO_EDGE_RISING, NULL, 0}, }; /** * @brief Initializes the gpiox peripheral according to the specified * parameters in the gpio_init_struct. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_init_struct: pointer to a gpio_init_typedef structure that * contains the configuration information for the specified GPIO peripheral. * @retval None */ void gpio_init(gpio_typedef* gpiox, gpio_init_typedef* gpio_init_struct) { u32 reg; uint32_t pin_idx = 0, pin_bit_offset = 0x00; uint8_t i = 0x00; /*--- Configure the port pins ---*/ for (pin_idx = 0; pin_idx < 16; pin_idx++) { pin_bit_offset = ((uint32_t)0x01) << pin_idx; if (gpio_init_struct->gpio_pin & pin_bit_offset) { /*--- Input or Output Configuration ---*/ reg = gpiox->dir; reg &= ~pin_bit_offset; reg |= ((uint32_t)(gpio_init_struct->gpio_dir) << pin_idx); gpiox->dir = reg; /*--- Digital or Analog Configuration ---*/ reg = gpiox->de; reg &= ~pin_bit_offset; reg |= ((uint32_t)(gpio_init_struct->gpio_mode) << pin_idx); gpiox->de = reg; /*--- Function Mapping Enable Configuration ---*/ reg = gpiox->fen; reg &= ~pin_bit_offset; reg |= ((uint32_t)(gpio_init_struct->gpio_fen) << pin_idx); gpiox->fen = reg; /*--- GPIO Direction Select Configguratiopn ---*/ reg = gpiox->fdir; reg &= ~pin_bit_offset; reg |= ((uint32_t)(gpio_init_struct->gpio_fdir) << pin_idx); gpiox->fdir = reg; /*--- Pull Up or Pull Down Configuration and clear driving cfg ---*/ if (gpio_init_struct->gpio_dir == GPIO_DIR_INPUT) { gpiox->drv &= ~pin_bit_offset; for (i = 0; i < 6; i++) { reg = gpiox->pupd[i]; reg &= ~pin_bit_offset; if ((i + 1) == gpio_init_struct->gpio_pupd) { reg |= pin_bit_offset; } gpiox->pupd[i] = reg; } /*--- Output driving Configuration and clear pupu cfg ---*/ } else if (gpio_init_struct->gpio_dir == GPIO_DIR_OUTPUT) { for (i = 0; i < 6; i++) { gpiox->pupd[i] &= ~pin_bit_offset; } if (gpio_init_struct->gpio_mode == GPIO_MODE_DIGITAL) { if (GPIO_SUPPORT_STRONG_CURRENT(gpio_init_struct->gpio_drv, gpiox, pin_idx)) { reg = gpiox->drv; reg |= ((uint32_t)(gpio_init_struct->gpio_drv)); gpiox->drv = reg; } else { reg = gpiox->drv; reg &= ~pin_bit_offset; reg |= ((uint32_t)(gpio_init_struct->gpio_drv) << pin_idx); gpiox->drv = reg; } } } } } } /** * @brief De-initialize the specified GPIO peripheral. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bits to be written. This parameter * can be any combination of GPIO_PIN_x where x can be (0..15). * @retval None */ uint32_t gpio_deinit(gpio_typedef *gpiox, uint16_t gpio_pin) { u32 reg_original_sta; if (gpiox == GPIOB_REG) { gpio_pin &= 0x3ff; } reg_original_sta = gpiox->de; gpiox->de &= ~gpio_pin; return reg_original_sta; } /** * @brief Sets the selected data port bits. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bits to be written. This parameter * can be any combination of GPIO_PIN_x where x can be (0..15). * @retval None */ AT(.com_periph.gpio.set) void gpio_set_bits(gpio_typedef* gpiox, uint16_t gpio_pin) { gpiox->io_set = gpio_pin; } /** * @brief Clears the selected data port bits. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bits to be written. * This parameter can be any combination of GPIO_PIN_x where x can be (0..15). * @retval None */ AT(.com_periph.gpio.reset) void gpio_reset_bits(gpio_typedef* gpiox, uint16_t gpio_pin) { gpiox->io_clr = gpio_pin; } /** * @brief Writes the specified GPIO output data port. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param port_val: specifies the port bits to be written. This parameter * can be any combination of GPIO_PIN_x where x can be (0..15). * @retval None */ AT(.com_periph.gpio.write) void gpio_write_data(gpio_typedef *gpiox, uint16_t port_val) { gpiox->data = port_val; } /** * @brief Toggles the selected output data port bits. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bits to be written. This parameter * can be any combination of GPIO_PIN_x where x can be (0..15). * @retval None */ AT(.com_periph.gpio.toggle) void gpio_toggle_bits(gpio_typedef *gpiox, uint16_t gpio_pin) { gpiox->data ^= gpio_pin; } /** * @brief Reads the specified GPIO input data port. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @retval GPIO output data port value. */ AT(.com_periph.gpio.read_data) uint16_t gpio_read_data(gpio_typedef* gpiox) { return ((uint16_t)(gpiox->data)); } /** * @brief Reads the specified input port pin. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bit to read. * This parameter can be gpio_pin where x can be (0..15). * @retval The input port pin value. */ AT(.com_periph.gpio.read_bit) uint8_t gpio_read_bit(gpio_typedef* gpiox, uint16_t gpio_pin) { uint8_t bit_status; if ((gpiox->data & gpio_pin) != BIT_RESET) { bit_status = (uint8_t)SET; } else { bit_status = (uint8_t)RESET; } return bit_status; } AT(.com_periph.gpio.set_input) void gpio_set_input(gpio_typedef* gpiox, uint16_t gpio_pin) { gpiox->dir |= gpio_pin; } AT(.com_periph.gpio.set_out) void gpio_set_out(gpio_typedef* gpiox, uint16_t gpio_pin) { gpiox->dir &= ~gpio_pin; } /** * @brief Configure GPIO function mapping. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bit to read. * This parameter can be gpio_pin where x can be (0..15). * @param func_idx: Peripheral index that need to config mapping. The value of this param * see enumeration "GPIO_CROSSBAR_PERIPHERAL_DEF" in the file "driver_gpio.h". * @retval None */ void gpio_func_mapping_config(gpio_typedef* gpiox, uint16_t gpio_pin, GPIO_CROSSBAR_PERIPHERAL_DEF func_idx) { u8 i; u8 pin_idx; uint8_t register_idx, register_offset; u8 gpio_crossbar_idx_PA0 = 1; u8 gpio_crossbar_idx_PB0 = 17; /*--- Calculate the pin_dix for function mapping ---*/ for (i = 0; i < 16; i++) { if ((1 << i) == gpio_pin) { break; } } if (gpiox == GPIOA_REG) { pin_idx = gpio_crossbar_idx_PA0 + i; } else { pin_idx = gpio_crossbar_idx_PB0 + i; } /*--- Check parameter validity ---*/ if (func_idx >= GPIO_CROSSBAR_PERIPHERAL_MAX_IDX) { return; } if ((gpiox == GPIOB_REG) && (gpio_pin > GPIO_PIN_9)) { return; } /*--- function mapping config ---*/ if (func_idx > GPIO_CROSSBAR_PERIPHERAL_INPUT_BASE) { register_idx = (func_idx - GPIO_CROSSBAR_PERIPHERAL_INPUT_BASE - 1) / 4; register_offset = (func_idx - GPIO_CROSSBAR_PERIPHERAL_INPUT_BASE - 1) % 4 * 8; /* Do nothing if the current peripheral is already mapped to the corresponding IO */ if (((FUNCMAP->func_input_map[register_idx] >> register_offset) & 0x1f) == pin_idx) { return; } FUNCMAP->func_input_map[register_idx] |= (uint32_t)(0xff << register_offset); FUNCMAP->func_input_map[register_idx] |= (uint32_t)(pin_idx << register_offset); } else if (func_idx < GPIO_CROSSBAR_PERIPHERAL_INPUT_BASE) { register_idx = (pin_idx - 1) / 4; register_offset = (pin_idx - 1) % 4 * 8; /* Do nothing if the current peripheral is already mapped to the corresponding IO */ if (((FUNCMAP->func_output_map[register_idx] >> register_offset) & 0x1f) == func_idx) { return; } FUNCMAP->func_output_map[register_idx] |= (uint32_t)(0xff << register_offset); FUNCMAP->func_output_map[register_idx] |= (uint32_t)(func_idx << register_offset); } } /** * @brief Clear GPIO function mapping config. * @param gpiox: where x can be (A.B) to select the GPIO peripheral. * @param gpio_pin: specifies the port bit to read. * This parameter can be gpio_pin where x can be (0..15). * @retval None */ void gpio_func_mapping_clear(gpio_typedef* gpiox, uint16_t gpio_pin) { uint8_t register_idx, register_offset; uint8_t i, j; u8 pin_idx; u8 gpio_crossbar_idx_PA0 = 1; u8 gpio_crossbar_idx_PB0 = 17; /* Calculate the pin_dix for function mapping */ for (i = 0; i < 16; i++) { if ((1 << i) == gpio_pin) { break; } } if (gpiox == GPIOA_REG) { pin_idx = gpio_crossbar_idx_PA0 + i; } else { pin_idx = gpio_crossbar_idx_PB0 + i; } /* Clear output mapping */ register_idx = (pin_idx - 1) / 4; register_offset = (pin_idx - 1) % 4 * 8; FUNCMAP->func_output_map[register_idx] |= (uint32_t)(0xff << register_offset); for (i = 0; i < 7; i++) { for (j = 0; j < 32; j += 8) if (((FUNCMAP->func_input_map[i] >> j) & 0x1f) == pin_idx) { FUNCMAP->func_input_map[i] |= (uint32_t)(0xff << j); } } } /** * @brief WK0 IO level detect config. * @param pull_up_en: Whether WK0 internal pull up enable. * @param pull_down_en: Whether WK0 internal pull down enable. * @param input_en: Whether WK0 input enable. * @retval None */ void wko_io_config(FUNCTIONAL_STATE pull_up_en, FUNCTIONAL_STATE pull_down_en, FUNCTIONAL_STATE input_en) { if (pull_up_en) { RTCCON1 |= (uint32_t)(1 << 4); } else { RTCCON1 &= ~(uint32_t)(1 << 4); } if (pull_down_en) { RTCCON1 |= (uint32_t)(1 << 1); RTCCON11 |= (uint32_t)(1 << 4); // Sure this bit is enable, and recommends it is enabled normally. } else { RTCCON1 &= ~(uint32_t)(1 << 1); } if (input_en) { RTCCON1 |= (uint32_t)(1 << 0); } else { RTCCON1 &= ~(uint32_t)(1 << 0); } } /** * @brief Read WK0 level. * @retval The state of interrupt_type (SET or RESET). */ AT(.com_periph.wk0.read_bit) FLAG_STATE wko_io_read_bit(void) { if (RTCCON & (1 << 19)) { return SET; } else { return RESET; } } void wk0_edge_capture_config(GPIO_EDGE_SEL edge) { clk_gate0_cmd(CLK_GATE0_LP, CLK_EN); if(edge == GPIO_EDGE_FALLING){ wko_io_config(1, 0, 1); WKUPEDG |= BIT(5); }else{ wko_io_config(0, 1, 1); WKUPEDG &= ~BIT(5); } WKUPCON |= BIT(5); WKUPCPND = 0xff << 16; //clear wakeup pending } uint32_t gpio_edge_capture_idx_get(gpio_edge_cap_typedef* config) { uint32_t ret_idx = WAKEUP_IDX_MAX; /*we first considered wakeup source is general IO */ for (uint8_t i = WAKEUP_IDX_PORT_FALL; i < WAKEUP_IDX_MAX; i++) { if (config->edge == separate_wakeup_io[i].edge) { ret_idx = i; break; } } /*check if wakeup source is separate IO*/ for (uint8_t i = WAKEUP_IDX_PA7; i < WAKEUP_IDX_WK0; i++) { if ((config->gpiox == separate_wakeup_io[i].gpiox) && (config->gpio_pin == separate_wakeup_io[i].gpio_pin)) { ret_idx = i; break; } } return ret_idx; } void gpio_edge_capture_config(gpio_edge_cap_typedef* config) { clk_gate0_cmd(CLK_GATE0_LP, CLK_EN); gpio_init_typedef gpio_config; uint32_t gpio_pin = config->gpio_pin; if(config->gpiox == GPIOB_REG){ gpio_pin = ((uint32_t)config->gpio_pin) << 16; } gpio_config.gpio_pin = config->gpio_pin; gpio_config.gpio_dir = GPIO_DIR_INPUT; gpio_config.gpio_fen = GPIO_FEN_GPIO; gpio_config.gpio_fdir = GPIO_FDIR_SELF; gpio_config.gpio_mode = GPIO_MODE_DIGITAL; gpio_config.gpio_pupd = config->gpio_pupd; gpio_init(config->gpiox, &gpio_config); uint32_t wakeup_idx = gpio_edge_capture_idx_get(config); if(config->edge == GPIO_EDGE_RISING){ PORTINTEDG &= ~gpio_pin; PORTINTEN |= gpio_pin; WKUPEDG &= ~BIT(wakeup_idx); WKUPCON |= BIT(wakeup_idx); }else{ PORTINTEDG |= gpio_pin; PORTINTEN |= gpio_pin; WKUPEDG |= BIT(wakeup_idx); WKUPCON |= BIT(wakeup_idx); } WKUPCPND = 0xff << 16; } AT(.com_periph.gpio.pending_clr) void gpio_edge_pending_clear(void) { WKUPCPND = 0xff << 16; } AT(.com_periph.gpio.pending_is) bool gpio_is_edge_pending(void) { return ((WKUPEDG>>16) & 0xff) ? true : false; } void gpio_edge_pic_config(isr_t isr, int pr) { sys_irq_init(IRQn_PORT, pr, isr); WKUPCPND = 0xff << 16; WKUPCON |= BIT(16); } void gpio_edge_pic_disable(void) { PICEN &= ~BIT(IRQn_PORT); WKUPCON &= ~BIT(16); }