El temporizador SysTick es común a todos los procesadores Cortex-M y se define en los archivos CMSIS-Core. Este puede utilizarse para funciones de retardo, aplicándose tanto a chips STM32 como GD32 o AT32, siempre que sean compatibles con Cortex-M3/M4.
El siguiente código está basado en el chip GD32F303RCT6 con una frecuencia principle de 120 MHz.
Ejemplo 1: Retardos por microsegundos y milisgeundos
/**
* @brief Retardo en microsegundos
* @param xus Duración del retardo (0 a 233015)
*/
void Delay_us(uint32_t xus) {
SysTick->LOAD = 120 * xus;
SysTick->VAL = 0;
SysTick->CTRL = 0x00000005;
while (!(SysTick->CTRL & 0x00010000));
SysTick->CTRL = 0x00000004;
}
/**
* @brief Retardo en milisegundos
* @param xms Duración del retardo (0 a 4294967295)
*/
void Delay_ms(uint32_t xms) {
while (xms--) {
Delay_us(1000);
}
}
Ejemplo 2: Inicialización dinámica según frecuencia del sistema
static uint16_t g_fac_us = 0;
void delay_init(uint16_t sysclk) {
SysTick->CTRL = 0;
SysTick->CTRL &= ~(1 << 2);
g_fac_us = sysclk / 8;
}
void delay_us(uint32_t nus) {
SysTick->LOAD = nus * g_fac_us;
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
do {
uint32_t temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16)));
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
SysTick->VAL = 0;
}
void delay_ms(uint16_t nms) {
uint32_t repeat = nms / 1000;
uint32_t remain = nms % 1000;
while (repeat--) {
delay_us(1000 * 1000);
}
if (remain) {
delay_us(remain * 1000);
}
}
GPIO
Configuración y uso
La configuración de GPIO es similar a la de STM32.
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOD);
gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOD, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
Escritura y lectura de pines:
gpio_bit_write(GPIOA, GPIO_PIN_8, SET);
uint8_t value = gpio_input_bit_get(GPIOA, GPIO_PIN_13);
// Alternar estado del pin
void gpio_togglepin(uint32_t gpio_periph, uint32_t pin) {
uint32_t octl = GPIO_OCTL(gpio_periph);
GPIO_BOP(gpio_periph) = ((octl & pin) << 16u) | (~octl & pin);
}
ADC
Inicialización
void Adc_Init(void) {
rcu_periph_clock_enable(RCU_GPIOC);
gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
rcu_periph_clock_enable(RCU_ADC0);
adc_deinit(ADC0);
rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV6);
adc_mode_config(ADC_MODE_FREE);
adc_special_function_config(ADC0, ADC_SCAN_MODE, DISABLE);
adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE);
adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);
adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 1);
adc_enable(ADC0);
adc_calibration_enable(ADC0);
}
Obtener valor ADC
uint16_t Get_Adc(uint8_t ch) {
adc_regular_channel_config(ADC0, 0, ch, ADC_SAMPLETIME_239POINT5);
adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);
while (SET != adc_flag_get(ADC0, ADC_FLAG_EOC));
return adc_regular_data_read(ADC0);
}
uint16_t Get_Adc_Average(uint8_t ch, uint8_t times) {
uint32_t temp_val = 0;
for (uint8_t t = 0; t < times; t++) {
temp_val += Get_Adc(ch);
delay_ms(5);
}
return temp_val / times;
}
DAC
Configuración
void Dac_Init(void) {
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_DAC);
gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
dac_deinit();
dac_output_buffer_disable(DAC0);
dac_trigger_disable(DAC0);
dac_wave_mode_config(DAC0, DAC_WAVE_DISABLE);
dac_dma_disable(DAC0);
dac_enable(DAC0);
dac_data_set(DAC0, DAC_ALIGN_12B_R, 0);
}
void Dac_Set_Vol(uint16_t vol) {
double temp = vol;
temp /= 1000;
temp = temp * 4096 / 3.3;
if (temp >= 4096) temp = 4095;
dac_data_set(DAC0, DAC_ALIGN_12B_R, temp);
}
UART
Configuración básica
void usart_init(uint32_t bound) {
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(RCU_USART0);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
usart_deinit(USART0);
usart_baudrate_set(USART0, bound);
usart_stop_bit_set(USART0, USART_STB_1BIT);
usart_word_length_set(USART0, USART_WL_8BIT);
usart_parity_config(USART0, USART_PM_NONE);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_interrupt_enable(USART0, USART_INT_RBNE);
nvic_irq_enable(USART0_IRQn, 3, 3);
usart_enable(USART0);
}
PWM
Frecuencia y ciclo de trabajo
Para generar una señal PWM de 1000 Hz con un ciclo de trabajo del 50%:
- PSC = 1200 - 1 → PSC + 1 = 1200
- ARR = 100 - 1 → ARR + 1 = 100
- f = fsystem / [(PSC+1)*(ARR+1)] = 1000 Hz
void TIM0_PWM_Init(uint16_t arr, uint16_t psc) {
timer_oc_parameter_struct timer_ocinitpara;
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_TIMER0);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
timer_deinit(TIMER0);
timer_initpara.prescaler = psc;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = arr;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER0, &timer_initpara);
timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocinitpara);
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, arr / 2);
timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM0);
timer_primary_output_config(TIMER0, ENABLE);
timer_enable(TIMER0);
}
void TIM_SetTIM0Compare1(uint32_t compare) {
timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, compare);
}
Medición de PWM por captura de entrada
Utilizando el canal CH0 del TIMER1 para medir frecuencia y ciclo de trabajo.
void TIM1_CH0_Cap_Init(uint16_t arr, uint16_t psc) {
timer_ic_parameter_struct timer_icinitpara;
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_TIMER1);
gpio_init(GPIOA, GPIO_MODE_IPD, GPIO_OSPEED_MAX, GPIO_PIN_0);
timer_deinit(TIMER1);
timer_initpara.prescaler = psc;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = arr;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0;
timer_init(TIMER1, &timer_initpara);
timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING;
timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;
timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV1;
timer_icinitpara.icfilter = 0x0;
timer_input_capture_config(TIMER1, TIMER_CH_0, &timer_icinitpara);
timer_auto_reload_shadow_enable(TIMER1);
timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_CH0);
timer_interrupt_enable(TIMER1, TIMER_INT_CH0);
nvic_irq_enable(TIMER1_IRQn, 2, 0);
timer_enable(TIMER1);
}
Timers
Interrupción del Timer2
void TIM2_Int_Init(uint16_t arr, uint16_t psc) {
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER2);
timer_deinit(TIMER2);
timer_struct_para_init(&timer_initpara);
timer_initpara.prescaler = psc;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = arr;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_init(TIMER2, &timer_initpara);
timer_interrupt_enable(TIMER2, TIMER_INT_UP);
nvic_irq_enable(TIMER2_IRQn, 1, 3);
timer_enable(TIMER2);
}
void TIMER2_IRQHandler(void) {
if (timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP) == SET) {
LED1_TOGGLE();
timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
}
}
WDT (Watchdog)
Independent WDT
void FWDGT_Init(uint8_t prer, uint16_t rlr) {
fwdgt_write_enable();
fwdgt_config(rlr, prer);
fwdgt_counter_reload();
fwdgt_enable();
}
void FWDGT_Feed(void) {
fwdgt_counter_reload();
}
Window WDT
void WWDGT_Init(uint8_t tr, uint8_t wr, uint32_t fprer) {
rcu_periph_clock_enable(RCU_WWDGT);
wwdgt_config(tr, wr, fprer);
wwdgt_enable();
wwdgt_flag_clear();
wwdgt_interrupt_enable();
nvic_irq_enable(WWDGT_IRQn, 2, 3);
}
void WWDGT_IRQHandler(void) {
wwdgt_counter_update(127);
wwdgt_flag_clear();
LED1_TOGGLE();
}
RTC
Configuración y manejo de tiempo real
uint8_t RTC_Init(void) {
rcu_periph_clock_enable(RCU_PMU);
rcu_periph_clock_enable(RCU_BKPI);
pmu_backup_write_enable();
uint16_t bkpflag = bkp_read_data(BKP_DATA_0);
if (bkpflag != 0X6060) {
bkp_deinit();
rcu_osci_on(RCU_LXTAL);
if (rcu_osci_stab_wait(RCU_LXTAL) == ERROR) {
rcu_osci_on(RCU_IRC40K);
rcu_rtc_clock_config(RCU_RTCSRC_IRC40K);
bkp_write_data(BKP_DATA_0, 0x6061);
} else {
rcu_rtc_clock_config(RCU_RTCSRC_LXTAL);
bkp_write_data(BKP_DATA_0, 0x6060);
}
rcu_periph_clock_enable(RCU_RTC);
rtc_lwoff_wait();
rtc_register_sync_wait();
rtc_interrupt_enable(RTC_INT_SECOND);
rtc_prescaler_set(32768 - 1);
rtc_lwoff_wait();
if (bkpflag != 0X6061) {
RTC_Set_Time(2023, 8, 28, 23, 58, 55);
}
}
nvic_irq_enable(RTC_IRQn, 0, 0);
RTC_Get_Time();
return 0;
}
DMA
Configuración de DMA0 para transmisión UART
void DMA_Config(uint32_t mar, uint32_t par) {
dma_parameter_struct dma_init_struct;
rcu_periph_clock_enable(RCU_DMA0);
dma_deinit(DMA0, DMA_CH3);
dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
dma_init_struct.memory_addr = mar;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = 0;
dma_init_struct.periph_addr = par;
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
dma_init_struct.priority = DMA_PRIORITY_MEDIUM;
dma_init(DMA0, DMA_CH3, &dma_init_struct);
dma_circulation_disable(DMA0, DMA_CH3);
dma_memory_to_memory_disable(DMA0, DMA_CH3);
}
void MYDMA_Enable(uint16_t num) {
dma_channel_disable(DMA0, DMA_CH3);
while (DMA_CHCTL(DMA0, DMA_CH3) & DMA_CHXCTL_CHEN);
dma_transfer_number_config(DMA0, DMA_CH3, num);
dma_channel_enable(DMA0, DMA_CH3);
}
IIC
Implementación de I2C
void IIC_Init(void) {
rcu_periph_clock_enable(RCU_GPIO_IIC);
gpio_init(GPIO_SCL, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_SCL);
gpio_init(GPIO_SDA, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_PIN_SDA);
IIC_Stop();
}
void IIC_Start(void) {
IIC_SDA(1);
IIC_SCL(1);
IIC_delay();
IIC_SDA(0);
IIC_delay();
IIC_SCL(0);
IIC_delay();
}
SPI
Configuración SPI0 como maestro
void SPI0_Init(void) {
spi_parameter_struct spi_init_struct;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_SPI0);
rcu_periph_clock_enable(RCU_AF);
gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.prescale = SPI_PSC_256;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init(SPI0, &spi_init_struct);
spi_enable(SPI0);
}
Flash
Escritura y lectura segura en Flash
void GDFLASH_Write(uint32_t waddr, uint16_t *pbuf, uint16_t length) {
fmc_unlock();
// Implementación completa de escritura con verificación de página
fmc_lock();
}
IAP (In-Application Programming)
Actualización de firmware desde bootloader
void iap_load_app(uint32_t appxaddr) {
if (((REG32(appxaddr)) & 0x2FFE0000) == 0x20000000) {
jump2app = (iapfun) REG32(appxaddr + 4);
sys_msr_msp(REG32(appxaddr));
jump2app();
}
}