hc32f460: Add support for hc32f460 micro-controllers
Signed-off-by: Steven Gotthardt <gotthardt@gmail.com>
This commit is contained in:
parent
94cbf5ff48
commit
72b6bd7efa
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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 $< $@
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 (;;) ;
|
||||
}
|
|
@ -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);
|
Loading…
Reference in New Issue