hc32f460: Add support for hc32f460 micro-controllers

Signed-off-by: Steven Gotthardt <gotthardt@gmail.com>
This commit is contained in:
Steven Gotthardt 2022-12-18 15:05:51 -07:00 committed by Kevin O'Connor
parent 94cbf5ff48
commit 72b6bd7efa
11 changed files with 928 additions and 0 deletions

View File

@ -20,6 +20,8 @@ choice
bool "LPC176x (Smoothieboard)"
config MACH_STM32
bool "STMicroelectronics STM32"
config MACH_HC32F460
bool "Huada Semiconductor HC32F460"
config MACH_RP2040
bool "Raspberry Pi RP2040"
config MACH_PRU
@ -35,6 +37,7 @@ source "src/atsam/Kconfig"
source "src/atsamd/Kconfig"
source "src/lpc176x/Kconfig"
source "src/stm32/Kconfig"
source "src/hc32f460/Kconfig"
source "src/rp2040/Kconfig"
source "src/pru/Kconfig"
source "src/linux/Kconfig"

79
src/hc32f460/Kconfig Normal file
View File

@ -0,0 +1,79 @@
# Kconfig settings for Huada HC32F460 processor
if MACH_HC32F460
config HC32F460_SELECT
bool
default y
select HAVE_GPIO
select HAVE_GPIO_ADC
select HAVE_GPIO_BITBANGING
select HAVE_STRICT_TIMING
select HAVE_GPIO_HARD_PWM
select HAVE_STEPPER_BOTH_EDGE
config BOARD_DIRECTORY
string
default "hc32f460"
######################################################################
# Communication interface
######################################################################
choice
prompt "Communication interface"
config HC32F460_SERIAL_PA7_PA8
bool "Serial (PA7 & PA8) - Creality Ender 2 PRO"
select SERIAL
config HC32F460_SERIAL_PA3_PA2
bool "Serial (PA3 & PA2) - Anycube"
select SERIAL
config HC32F460_SERIAL_PA15_PA9
bool "Serial (PA15 & PA09) - Voxelab"
select SERIAL
config HC32F460_SERIAL_PC0_PC1
bool "Serial (PC0 & PC1) - on LCD connector"
select SERIAL
endchoice
######################################################################
# Bootloader
# bootloader moves code and then VTOR.RESET points here:
######################################################################
config FLASH_SIZE
hex
default 0x40000
config FLASH_APPLICATION_ADDRESS
default 0x8000 # Aquila is 0xC000
config FLASH_BOOT_ADDRESS
hex
default 0x0
config RAM_SIZE
hex
default 0x8000
# use the fast RAM in the HC32F460
config RAM_START
hex
default 0x1fff8000
config STACK_SIZE
int
default 1024
config CLOCK_FREQ
int
default 200000000 # Voxelab uses 168000000
config MCU
string
default "HC32F460"
endif

37
src/hc32f460/Makefile Normal file
View File

@ -0,0 +1,37 @@
# hc32f460 build rules
# Setup the toolchain
CROSS_PREFIX=arm-none-eabi-
dirs-y += src/hc32f460 src/generic lib/hc32f460/driver/src lib/hc32f460/mcu/common
CFLAGS += -mthumb -mcpu=cortex-m4 -Isrc/hc32f460 -Ilib/hc32f460/driver/inc -Ilib/hc32f460/mcu/common -Ilib/cmsis-core -DHC32F460
CFLAGS_klipper.elf += --specs=nano.specs --specs=nosys.specs
CFLAGS_klipper.elf += -T $(OUT)src/generic/armcm_link.ld
$(OUT)klipper.elf: $(OUT)src/generic/armcm_link.ld
# Add source files
src-y += hc32f460/main.c
src-y += hc32f460/interrupts.c
src-y += hc32f460/gpio.c
src-y += ../lib/hc32f460/mcu/common/system_hc32f460.c
src-y += ../lib/hc32f460/driver/src/hc32f460_clk.c
src-y += ../lib/hc32f460/driver/src/hc32f460_efm.c
src-y += ../lib/hc32f460/driver/src/hc32f460_sram.c
src-y += ../lib/hc32f460/driver/src/hc32f460_utility.c
src-y += ../lib/hc32f460/driver/src/hc32f460_gpio.c
src-y += ../lib/hc32f460/driver/src/hc32f460_pwc.c
src-$(CONFIG_HAVE_GPIO_ADC) += hc32f460/adc.c ../lib/hc32f460/driver/src/hc32f460_adc.c
src-$(CONFIG_SERIAL) += hc32f460/serial.c generic/serial_irq.c ../lib/hc32f460/driver/src/hc32f460_usart.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += hc32f460/hard_pwm.c ../lib/hc32f460/driver/src/hc32f460_timera.c
src-y += generic/armcm_boot.c generic/armcm_irq.c generic/armcm_timer.c
src-y += generic/armcm_reset.c generic/crc16_ccitt.c
# Build the additional bin output file
target-y += $(OUT)klipper.bin
$(OUT)klipper.bin: $(OUT)klipper.elf
@echo " Creating bin file $@"
$(Q)$(OBJCOPY) -O binary $< $@

155
src/hc32f460/adc.c Normal file
View File

@ -0,0 +1,155 @@
// ADC functions on Huada HC32F460
//
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "generic/misc.h" // timer_from_us
#include "command.h" // shutdown
#include "board/gpio.h" // gpio_adc_setup
#include "board/internal.h" // GPIO
#include "sched.h" // sched_shutdown
// library
#include "hc32f460_adc.h"
#include "hc32f460_pwc.h"
#include "hc32f460_gpio.h"
#define ADC_RESOLUTION_12BIT (12u)
#define ADC_RESOLUTION_10BIT (10u)
#define ADC_RESOLUTION_8BIT (8u)
#define ADC1_RESOLUTION (ADC_RESOLUTION_12BIT)
#define ADC1_PRECISION (1ul << ADC1_RESOLUTION)
#if ADC1_RESOLUTION == ADC_RESOLUTION_12BIT
#define AdcResolution AdcResolution_12Bit
#elif ADC1_RESOLUTION == ADC_RESOLUTION_10BIT
#define AdcResolution AdcResolution_10Bit
#else
#define AdcResolution AdcResolution_8Bit
#endif
/* Timeout value definitions. Found in example code */
#define TIMEOUT_VAL (30u)
DECL_CONSTANT("ADC_MAX", ADC1_PRECISION-1);
// These pins can be used for ADC
static const uint8_t adc_gpio[] = {
GPIO('A', 0), // Chan 0
GPIO('A', 1), // Chan 1
GPIO('A', 2), // Chan 2
GPIO('A', 3), // Chan 3
GPIO('A', 4), // Chan 4
GPIO('A', 5), // Chan 5
GPIO('A', 6), // Chan 6
GPIO('A', 7), // Chan 7
GPIO('B', 0), // Chan 8
GPIO('B', 1), // Chan 9
GPIO('C', 0), // Chan 10 // TBed on TriGorilla
GPIO('C', 1), // Chan 11 // THead on TriGorilla
GPIO('C', 2), // Chan 12
GPIO('C', 3), // Chan 13
GPIO('C', 4), // Chan 14 // TBed on aquilla
GPIO('C', 5), // Chan 15 // THead on aquilla
};
struct gpio_adc
gpio_adc_setup(uint32_t gpio)
{
// validate pin in adc_pins table
int chan;
for (chan=0; ; chan++)
{
if (chan >= ARRAY_SIZE(adc_gpio))
{
shutdown("Not a valid ADC pin");
}
if (adc_gpio[chan] == gpio)
{
break;
}
}
// set as analog
gpio_peripheral(gpio, Pin_Mode_Ana, 0);
uint8_t sampleTime[ARRAY_SIZE(adc_gpio)] = { TIMEOUT_VAL }; // all chans
stc_adc_ch_cfg_t stcAdcChan;
stcAdcChan.u32Channel = 1 << chan;
stcAdcChan.u8Sequence = ADC_SEQ_A; // all conversions are in SEQ A
stcAdcChan.pu8SampTime = sampleTime;
ADC_AddAdcChannel(M4_ADC1, &stcAdcChan);
return (struct gpio_adc){ .chan = chan };
}
// Try to sample a value. Returns zero if sample ready, otherwise
// returns the number of clock ticks the caller should wait before
// retrying this function.
uint32_t
gpio_adc_sample(struct gpio_adc g)
{
// true if the sequence is finished
if (ADC_GetEocFlag(M4_ADC1, ADC_SEQ_A))
{
// all conversions are done - clear the flag
ADC_ClrEocFlag(M4_ADC1, ADC_SEQ_A);
return 0;
}
else if (M4_ADC1->STR & 1)
{
// running but not done yet
return timer_from_us(TIMEOUT_VAL/2);
}
else
{
// not running - so start
ADC_StartConvert(M4_ADC1);
}
return timer_from_us(TIMEOUT_VAL);
}
// Read a value; use only after gpio_adc_sample() returns zero
uint16_t
gpio_adc_read(struct gpio_adc g)
{
// return the one we want...
return ADC_GetValue(M4_ADC1, g.chan);
}
// Cancel a sample that may have been started with gpio_adc_sample()
void
gpio_adc_cancel_sample(struct gpio_adc g)
{
ADC_StopConvert(M4_ADC1);
}
// The clocks are already set by the loader.
// There is ADC1 and ADC2. Sequences do all channels at once.
void
adc_init(void)
{
// PCLK2 (ADC clock) is 'divide by 4', Max ADC clock is 60MHz
stc_adc_init_t stcAdcInit = {0};
stcAdcInit.enResolution = AdcResolution; // see define above
stcAdcInit.enDataAlign = AdcDataAlign_Right;
stcAdcInit.enAutoClear = AdcClren_Disable;
stcAdcInit.enScanMode = AdcMode_SAOnce;
// power-on ADC
PWC_Fcg3PeriphClockCmd(PWC_FCG3_PERIPH_ADC1, Enable);
// only using ADC1
ADC_Init(M4_ADC1, &stcAdcInit);
}
DECL_INIT(adc_init);

153
src/hc32f460/gpio.c Normal file
View File

@ -0,0 +1,153 @@
// GPIO functions on HC32F460
//
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // ffs
#include "board/irq.h" // irq_save
#include "command.h" // DECL_ENUMERATION_RANGE
#include "board/gpio.h" // gpio_out_setup
#include "internal.h"
#include "sched.h" // sched_shutdown
#include "hc32f460_gpio.h"
// 64pin package
DECL_ENUMERATION_RANGE("pin", "PA0", GPIO('A', 0), 16);
DECL_ENUMERATION_RANGE("pin", "PB0", GPIO('B', 0), 16);
DECL_ENUMERATION_RANGE("pin", "PC0", GPIO('C', 0), 16);
DECL_ENUMERATION_RANGE("pin", "PD2", GPIO('D', 2), 1);
DECL_ENUMERATION_RANGE("pin", "PH2", PortH * 16 + 2, 1); // H: special case
// HC32F460 ports are in one M4_PORT - offset by 0x10
// eg toggle: M4_PORT->POTRA + 0x10 => M4_PORT->POTRB
// 'gpio' is port (0-4) * 16 + pinPosition (0-15)
#define POTR_OFFSET offsetof(M4_PORT_TypeDef, POTRA) // output flip
#define PODR_OFFSET offsetof(M4_PORT_TypeDef, PODRA) // output data
#define PIDR_OFFSET offsetof(M4_PORT_TypeDef, PIDRA) // input data
#define POSR_OFFSET offsetof(M4_PORT_TypeDef, POSRA) // output set
#define PORR_OFFSET offsetof(M4_PORT_TypeDef, PORRA) // output reset
#define PORT_OFFSET offsetof(M4_PORT_TypeDef, PIDRB) // space between PORTS
void
gpio_peripheral(uint32_t gpio, int func, int pull_up)
{
stc_port_init_t stcPortInit;
irqstatus_t flag = irq_save();
stcPortInit.enPinMode = func;
stcPortInit.enLatch = Disable;
stcPortInit.enExInt = Disable;
stcPortInit.enInvert = Disable;
stcPortInit.enPullUp = pull_up ? Enable : Disable;
stcPortInit.enPinDrv = Pin_Drv_L;
stcPortInit.enPinOType = Pin_OType_Cmos;
stcPortInit.enPinSubFunc = Disable;
// make the port GPIO and disable the sub functionality
PORT_SetFunc(GPIO2PORT(gpio), GPIO2BIT(gpio), Func_Gpio, Disable);
PORT_Init(GPIO2PORT(gpio), GPIO2BIT(gpio), &stcPortInit);
irq_restore(flag);
}
struct gpio_out
gpio_out_setup(uint32_t gpio, uint32_t val)
{
uint32_t port = (uint32_t)M4_PORT + GPIO2PORT(gpio) * PORT_OFFSET;
struct gpio_out g =
{ .gpio = gpio, .portAddress = port, .bitMask = GPIO2BIT(gpio) };
gpio_out_reset(g, val);
return g;
}
void
gpio_out_reset(struct gpio_out g, uint32_t val)
{
irqstatus_t flag = irq_save();
if (val)
{
uint16_t *POSRx = (uint16_t *)(g.portAddress + POSR_OFFSET);
*POSRx = g.bitMask;
}
else
{
uint16_t *PORRx = (uint16_t *)(g.portAddress + PORR_OFFSET);
*PORRx = g.bitMask;
}
gpio_peripheral(g.gpio, Pin_Mode_Out, 0);
irq_restore(flag);
}
void
gpio_out_toggle_noirq(struct gpio_out g)
{
uint16_t *POTRx = (uint16_t *)(g.portAddress + POTR_OFFSET);
*POTRx = g.bitMask;
}
void
gpio_out_toggle(struct gpio_out g)
{
irqstatus_t flag = irq_save();
gpio_out_toggle_noirq(g);
irq_restore(flag);
}
void
gpio_out_write(struct gpio_out g, uint32_t val)
{
if (val)
{
uint16_t *POSRx = (uint16_t *)(g.portAddress + POSR_OFFSET);
*POSRx = g.bitMask;
}
else
{
uint16_t *PORRx = (uint16_t *)(g.portAddress + PORR_OFFSET);
*PORRx = g.bitMask;
}
}
struct gpio_in
gpio_in_setup(uint32_t gpio, int32_t pull_up)
{
uint32_t port = (uint32_t)M4_PORT + GPIO2PORT(gpio) * PORT_OFFSET;
struct gpio_in g =
{ .gpio = gpio, .portAddress = port, .bitMask = GPIO2BIT(gpio) };
gpio_in_reset(g, pull_up);
return g;
}
void
gpio_in_reset(struct gpio_in g, int32_t pull_up)
{
irqstatus_t flag = irq_save();
gpio_peripheral(g.gpio, Pin_Mode_In, pull_up);
irq_restore(flag);
}
uint8_t
gpio_in_read(struct gpio_in g)
{
uint16_t *PIDRx = (uint16_t *)(g.portAddress + PIDR_OFFSET);
return !!(*PIDRx & g.bitMask);
}

62
src/hc32f460/gpio.h Normal file
View File

@ -0,0 +1,62 @@
#ifndef __HC32F460_GPIO_H
#define __HC32F460_GPIO_H
#include <stdint.h> // uint32_t
struct gpio_out {
uint32_t portAddress; // M4_PORT or offset
uint16_t gpio; // the mangled pin+port
uint16_t bitMask; // the pin shifted to use in register
};
struct gpio_out gpio_out_setup(uint32_t gpio, uint32_t val);
void gpio_out_reset(struct gpio_out g, uint32_t val);
void gpio_out_toggle_noirq(struct gpio_out g);
void gpio_out_toggle(struct gpio_out g);
void gpio_out_write(struct gpio_out g, uint32_t val);
struct gpio_in {
uint32_t portAddress;
uint16_t gpio;
uint16_t bitMask;
};
struct gpio_in gpio_in_setup(uint32_t gpio, int32_t pull_up);
void gpio_in_reset(struct gpio_in g, int32_t pull_up);
uint8_t gpio_in_read(struct gpio_in g);
struct gpio_pwm {
void *timer;
uint32_t chan;
};
struct gpio_pwm gpio_pwm_setup(uint8_t gpio, uint32_t cycle_time, uint8_t val);
void gpio_pwm_write(struct gpio_pwm g, uint32_t val);
// all ADC operations will be on ADC1
struct gpio_adc {
uint32_t chan;
};
struct gpio_adc gpio_adc_setup(uint32_t gpio);
uint32_t gpio_adc_sample(struct gpio_adc g);
uint16_t gpio_adc_read(struct gpio_adc g);
void gpio_adc_cancel_sample(struct gpio_adc g);
struct spi_config {
void *spi;
uint32_t spi_cr1;
};
struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate);
void spi_prepare(struct spi_config config);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
struct i2c_config {
void *i2c;
uint8_t addr;
};
struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr);
void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write);
void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
, uint8_t read_len, uint8_t *read);
#endif // gpio.h

164
src/hc32f460/hard_pwm.c Normal file
View File

@ -0,0 +1,164 @@
// Hardware PWM support on HC32F460
//
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h"
#include "board/irq.h" // irq_save
#include "command.h" // shutdown
#include "board/gpio.h" // gpio_pwm_write
#include "internal.h" // GPIO
#include "sched.h" // sched_shutdown
// library
#include "hc32f460_timera.h"
#include "hc32f460_pwc.h"
#include "hc32f460_gpio.h"
#define MAX_PWM ((1 << 16) - 1)
DECL_CONSTANT("PWM_MAX", MAX_PWM);
// timer A (general purpose) has 6 units and each has 8 PWM
// M4_TMRA_TypeDef* timer;
struct gpio_pwm_info {
uint8_t gpio;
uint8_t unit; // 6 units in Timer A
en_timera_channel_t chan;
};
// Timer A (general purpose) is only timer used
// These are for pin function 4 only
// Some PWM come out on more than 1 pin
// 64pin package
static const struct gpio_pwm_info pwm_mapping[] = {
{GPIO('A', 0), 2, TimeraCh1},
{GPIO('A', 1), 2, TimeraCh2},
{GPIO('A', 2), 2, TimeraCh3},
{GPIO('A', 3), 2, TimeraCh4},
{GPIO('A', 5), 2, TimeraCh1},
{GPIO('A', 8), 1, TimeraCh1},
{GPIO('A', 9), 1, TimeraCh2},
{GPIO('A',10), 1, TimeraCh3},
{GPIO('A',11), 1, TimeraCh4},
{GPIO('A',13), 2, TimeraCh5},
{GPIO('A',14), 2, TimeraCh6},
{GPIO('A',15), 2, TimeraCh1},
{GPIO('B', 0), 1, TimeraCh6},
{GPIO('B', 1), 1, TimeraCh7},
{GPIO('B', 2), 1, TimeraCh8},
{GPIO('B', 3), 2, TimeraCh2},
{GPIO('B', 4), 3, TimeraCh1},
{GPIO('B', 5), 3, TimeraCh2},
{GPIO('B', 6), 4, TimeraCh1},
{GPIO('B', 7), 4, TimeraCh2},
{GPIO('B', 8), 4, TimeraCh3},
{GPIO('B', 9), 4, TimeraCh4},
{GPIO('B',10), 2, TimeraCh3},
{GPIO('B',12), 1, TimeraCh8},
{GPIO('B',13), 1, TimeraCh5},
{GPIO('B',14), 1, TimeraCh6},
{GPIO('B',15), 1, TimeraCh7},
{GPIO('C', 0), 2, TimeraCh5},
{GPIO('C', 1), 2, TimeraCh6},
{GPIO('C', 2), 2, TimeraCh7},
{GPIO('C', 3), 2, TimeraCh8},
{GPIO('C', 6), 3, TimeraCh1},
{GPIO('C', 7), 3, TimeraCh2},
{GPIO('C', 8), 3, TimeraCh3},
{GPIO('C', 9), 3, TimeraCh4},
{GPIO('C',10), 2, TimeraCh7},
{GPIO('C',11), 2, TimeraCh8},
{GPIO('C',13), 4, TimeraCh8},
{GPIO('C',14), 4, TimeraCh5},
{GPIO('C',15), 4, TimeraCh6},
{GPIO('D', 2), 2, TimeraCh4},
};
struct gpio_pwm
gpio_pwm_setup(uint8_t gpio, uint32_t cycle_time, uint8_t val)
{
// Find gpio pin in pwm_regs table
const struct gpio_pwm_info* p = pwm_mapping;
for (;; p++) {
if (p >= &pwm_mapping[ARRAY_SIZE(pwm_mapping)])
shutdown("Not a valid PWM pin");
if (p->gpio == gpio)
break;
}
// select registers - could make it programmatic
// All TimerA power control bits are in PWC_FCG2
M4_TMRA_TypeDef *timerA;
switch (p->unit)
{
default:
case 1:
timerA = M4_TMRA1;
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA1, Enable);
break;
case 2:
timerA = M4_TMRA2;
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA2, Enable);
break;
case 3:
timerA = M4_TMRA3;
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA3, Enable);
break;
case 4:
timerA = M4_TMRA4;
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA4, Enable);
break;
case 5:
timerA = M4_TMRA5;
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA5, Enable);
break;
case 6:
timerA = M4_TMRA6;
PWC_Fcg2PeriphClockCmd(PWC_FCG2_PERIPH_TIMA6, Enable);
break;
}
// set the function - only using #4 (Func_Tima0)
PORT_SetFunc(GPIO2PORT(gpio), GPIO2PIN(gpio), Func_Tima0, Disable);
/* Configuration timera unit 1 base structure */
stc_timera_base_init_t stcTimeraInit;
stcTimeraInit.enClkDiv = TimeraPclkDiv128; // 128 chosen - use below
stcTimeraInit.enCntMode = TimeraCountModeTriangularWave;
stcTimeraInit.enCntDir = TimeraCountDirUp;
stcTimeraInit.enSyncStartupEn = Disable;
stcTimeraInit.u16PeriodVal = cycle_time / 2U / 128U / 2;
TIMERA_BaseInit(timerA, &stcTimeraInit);
/* Configuration timera unit 1 compare structure */
stc_timera_compare_init_t stcTimerCompareInit;
stcTimerCompareInit.u16CompareVal = stcTimeraInit.u16PeriodVal * 4u / 5u;
stcTimerCompareInit.enStartCountOutput = TimeraCountStartOutputHigh;
stcTimerCompareInit.enStopCountOutput = TimeraCountStopOutputHigh;
stcTimerCompareInit.enCompareMatchOutput = TimeraCompareMatchOutputReverse;
stcTimerCompareInit.enPeriodMatchOutput = TimeraPeriodMatchOutputKeep;
stcTimerCompareInit.enSpecifyOutput = TimeraSpecifyOutputInvalid;
stcTimerCompareInit.enCacheEn = Disable;
stcTimerCompareInit.enTriangularTroughTransEn = Enable;
stcTimerCompareInit.enTriangularCrestTransEn = Disable;
stcTimerCompareInit.u16CompareCacheVal = stcTimerCompareInit.u16CompareVal;
TIMERA_CompareInit(timerA, p->chan, &stcTimerCompareInit);
TIMERA_CompareCmd(timerA, p->chan, Enable);
// default setup - all disabled
stc_timera_hw_startup_config_t stcTimeraHwConfig = { 0 };
TIMERA_HwStartupConfig(timerA, &stcTimeraHwConfig);
return (struct gpio_pwm){.timer = timerA, .chan = p->chan};
}
void
gpio_pwm_write(struct gpio_pwm g, uint32_t val)
{
TIMERA_SetCompareValue(g.timer, g.chan, (uint16_t)val);
}

29
src/hc32f460/internal.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef __HC32F460_INTERNAL_H
#define __HC32F460_INTERNAL_H
// Local definitions for Huada HC32F460
#include "autoconf.h"
#include "hc32f460.h"
// The HC32F460 library uses a port address and a shifted pin bit
// eg en_result_t PORT_Toggle(en_port_t enPort, uint16_t u16Pin);
// see hc32f460_gpio.h
// encode and decode gpio ports and pins
#define GPIO(PORT, NUM) (((PORT)-'A') * 16 + (NUM))
#define GPIO2PORT(GPIO) ((GPIO) / 16)
#define GPIO2BIT(GPIO) (1<<((GPIO) % 16))
#define GPIO2PIN(GPIO) ((GPIO) % 16)
#define GPIO_INPUT 0
#define GPIO_OUTPUT 1
void gpio_peripheral(uint32_t pin, int func, int pull_up);
// from local interrupts.c - helper
void IrqRegistration(en_int_src_t irqSrc, IRQn_Type irqType);
#endif // internal.h

29
src/hc32f460/interrupts.c Normal file
View File

@ -0,0 +1,29 @@
// Interrupt support for HC32F460
// The library interrupt support is huge and redefines systick
//
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "hc32f460.h"
#define IRQ_PRIORITY_DEFAULT 15u
/* the interrupts on the hc32f460 can be 're-assigned'
The author can choose the irqType (IRQ000_Handler, etc...)
that the source (irqSrc) triggers
*/
void IrqRegistration(en_int_src_t irqSrc, IRQn_Type irqType)
{
stc_intc_sel_field_t *stcIntSel = (stc_intc_sel_field_t *)
((uint32_t)(&M4_INTC->SEL0) + (4u * irqType));
// what is the source of the selected interrupt? (USART, etc...)
stcIntSel->INTSEL = irqSrc;
// set priority and enable
NVIC_SetPriority(irqType, IRQ_PRIORITY_DEFAULT);
NVIC_ClearPendingIRQ(irqType);
NVIC_EnableIRQ(irqType);
}

28
src/hc32f460/main.c Normal file
View File

@ -0,0 +1,28 @@
// Code to setup clocks on Huada HC32F460
//
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_MACH_AVR
#include "sched.h"
#include "system_hc32f460.h"
/****************************************************************
* Startup
****************************************************************/
// Main entry point - called from armcm_boot.c:ResetHandler()
void __attribute__((noreturn))
armcm_main(void)
{
// sets the system clock speed variable for library use
SystemInit();
// manage the system
sched_main();
// never get here
for (;;) ;
}

189
src/hc32f460/serial.c Normal file
View File

@ -0,0 +1,189 @@
// HC32F460 serial
//
// Copyright (C) 2022 Steven Gotthardt <gotthardt@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#include "autoconf.h" // CONFIG_SERIAL_BAUD
#include "command.h" // DECL_CONSTANT_STR
#include "internal.h"
#include "sched.h" // DECL_INIT
#include "generic/serial_irq.h"
#include "generic/armcm_boot.h"
#include "hc32f460_usart.h"
#include "hc32f460_gpio.h"
#include "hc32f460_pwc.h"
#define USART_BAUDRATE (CONFIG_SERIAL_BAUD)
// Aquila 1.0.3 pins for TX, RX to CH304 on PA15 and PA09
// for the LCD connector: TX, RX on PC00 and PC01
#if CONFIG_HC32F460_SERIAL_PA15_PA9
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA15,PA9");
#define USART_RX_PORT (PortA)
#define USART_RX_PIN (Pin15)
#define USART_TX_PORT (PortA)
#define USART_TX_PIN (Pin09)
#elif CONFIG_HC32F460_SERIAL_PC0_PC1
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PC0,PC1");
#define USART_RX_PORT (PortC)
#define USART_RX_PIN (Pin00)
#define USART_TX_PORT (PortC)
#define USART_TX_PIN (Pin01)
#elif CONFIG_HC32F460_SERIAL_PA3_PA2
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA3,PA2");
#define USART_RX_PORT (PortA)
#define USART_RX_PIN (Pin03)
#define USART_TX_PORT (PortA)
#define USART_TX_PIN (Pin02)
#elif CONFIG_HC32F460_SERIAL_PA7_PA8
DECL_CONSTANT_STR("RESERVE_PINS_serial", "PA7,PA8");
#define USART_RX_PORT (PortA)
#define USART_RX_PIN (Pin07)
#define USART_TX_PORT (PortA)
#define USART_TX_PIN (Pin08)
#endif
// use USART 1 for serial connection
#define USARTx M4_USART1
#define USART_ENABLE (PWC_FCG1_PERIPH_USART1 | PWC_FCG1_PERIPH_USART2 | \
PWC_FCG1_PERIPH_USART3 | PWC_FCG1_PERIPH_USART4)
#define USART_RX_FUNC Func_Usart1_Rx
#define USART_TX_FUNC Func_Usart1_Tx
#define USART_RI_NUM INT_USART1_RI
#define USART_TI_NUM INT_USART1_TI
#define USART_EI_NUM INT_USART1_EI
#define USART_TCI_NUM INT_USART1_TCI
void
serialError(void)
{
if (Set == USART_GetStatus(USARTx, UsartFrameErr))
{
USART_ClearStatus(USARTx, UsartFrameErr);
// use it anyway
serial_rx_byte(USARTx->DR_f.RDR);
}
if (Set == USART_GetStatus(USARTx, UsartOverrunErr))
{
USART_ClearStatus(USARTx, UsartOverrunErr);
}
}
DECL_ARMCM_IRQ(serialError, Int001_IRQn);
void
serialTxComplete(void)
{
USART_FuncCmd(USARTx, UsartTx, Disable);
USART_FuncCmd(USARTx, UsartTxCmpltInt, Disable);
}
DECL_ARMCM_IRQ(serialTxComplete, Int003_IRQn);
void
serialRx(void)
{
uint16_t data = USART_RecData(USARTx);
// call to klipper generic/serial_irq function
serial_rx_byte(data);
}
DECL_ARMCM_IRQ(serialRx, Int000_IRQn);
void
serialTxEmpty(void)
{
uint8_t data2send;
// use helper from generic - zero means byte ready
if (!serial_get_tx_byte(&data2send))
{
//USARTx->DR_f.TDR = data2send;
USART_SendData(USARTx, data2send);
}
else
{
// no more data - stop the interrupt
USART_FuncCmd(USARTx, UsartTxCmpltInt, Enable);
USART_FuncCmd(USARTx, UsartTxEmptyInt, Disable);
}
}
DECL_ARMCM_IRQ(serialTxEmpty, Int002_IRQn);
// called by generic framework
void
serial_enable_tx_irq(void)
{
/* Enable TX && TX empty interrupt */
USART_FuncCmd(USARTx, UsartTxAndTxEmptyInt, Enable);
}
void
serial_init(void)
{
const stc_usart_uart_init_t stcInitCfg = {
UsartIntClkCkNoOutput,
UsartClkDiv_1,
UsartDataBits8,
UsartDataLsbFirst,
UsartOneStopBit,
UsartParityNone,
UsartSampleBit16,
UsartStartBitFallEdge,
UsartRtsEnable,
};
// Function Clock Control - USART enable (sets bit to a 0)
PWC_Fcg1PeriphClockCmd(USART_ENABLE, Enable);
/* Initialize port pins for USART IO - Disable = NO subfunction */
PORT_SetFunc(USART_RX_PORT, USART_RX_PIN, USART_RX_FUNC, Disable);
PORT_SetFunc(USART_TX_PORT, USART_TX_PIN, USART_TX_FUNC, Disable);
/* Initialize UART */
USART_UART_Init(USARTx, &stcInitCfg);
/* Set baudrate */
USART_SetBaudrate(USARTx, USART_BAUDRATE);
/* A word on interrupts in HC32F460
In other vendors (STM) the irqs are assigned names in the vector list
eg: USART1_IRQn but HC32F460 has numbered IRQs: IRQ000_IRQn IRQ143_IRQn
Using INT_USART1_RI from hc32f460.h put in to IRQn->INTSEL
if n = 5 then the USART1_RI iterrupt will be at IRQ005_IRQn
For the specific case of each USART there are 5 separate irqs to map
*/
/* Set USART RX IRQ */
IrqRegistration(USART_RI_NUM, Int000_IRQn);
/* Set USART err */
IrqRegistration(USART_EI_NUM, Int001_IRQn);
/* Set USART TX IRQ */
IrqRegistration(USART_TI_NUM, Int002_IRQn);
/* Set USART TX complete IRQ */
IrqRegistration(USART_TCI_NUM, Int003_IRQn);
/* Enable RX && RX interrupt function */
USART_FuncCmd(USARTx, UsartRx, Enable);
USART_FuncCmd(USARTx, UsartRxInt, Enable);
}
DECL_INIT(serial_init);