diff --git a/libraries/AP_HAL_ChibiOS/GPIO.cpp b/libraries/AP_HAL_ChibiOS/GPIO.cpp
index a9b927c942..ad6326dca3 100644
--- a/libraries/AP_HAL_ChibiOS/GPIO.cpp
+++ b/libraries/AP_HAL_ChibiOS/GPIO.cpp
@@ -67,7 +67,46 @@ void GPIO::init()
             g->enabled = g->pwm_num > pwm_count;
         }
     }
+#ifdef HAL_PIN_ALT_CONFIG
+    setup_alt_config();
+#endif
+}
+
+#ifdef HAL_PIN_ALT_CONFIG
+/*
+  alternative config table, selected using BRD_ALT_CONFIG
+ */
+static const struct alt_config {
+    uint8_t alternate;
+    uint16_t mode;
+    ioline_t line;
+} alternate_config[] HAL_PIN_ALT_CONFIG;
+
+/*
+  change pin configuration based on ALT() lines in hwdef.dat
+ */
+void GPIO::setup_alt_config(void)
+{
+    AP_BoardConfig *bc = AP::boardConfig();
+    if (!bc) {
+        return;
+    }
+    const uint8_t alt = bc->get_alt_config();
+    if (alt == 0) {
+        // use defaults
+        return;
+    }
+    for (uint8_t i=0; i<ARRAY_SIZE(alternate_config); i++) {
+        if (alt == alternate_config[i].alternate) {
+            const iomode_t mode = alternate_config[i].mode & ~PAL_STM32_HIGH;
+            const uint8_t odr = (alternate_config[i].mode & PAL_STM32_HIGH)?1:0;
+            palSetLineMode(alternate_config[i].line, mode);
+            palWriteLine(alternate_config[i].line, odr);
+        }
+    }
 }
+#endif // HAL_PIN_ALT_CONFIG
+
 
 void GPIO::pinMode(uint8_t pin, uint8_t output)
 {
diff --git a/libraries/AP_HAL_ChibiOS/GPIO.h b/libraries/AP_HAL_ChibiOS/GPIO.h
index 3aa5f941da..75241983e5 100644
--- a/libraries/AP_HAL_ChibiOS/GPIO.h
+++ b/libraries/AP_HAL_ChibiOS/GPIO.h
@@ -61,6 +61,9 @@ private:
     bool _ext_started;
 
     bool _attach_interrupt(ioline_t line, palcallback_t cb, void *p, uint8_t mode);
+#ifdef HAL_PIN_ALT_CONFIG
+    void setup_alt_config(void);
+#endif
 };
 
 class ChibiOS::DigitalSource : public AP_HAL::DigitalSource {
diff --git a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py
index 3dd1d7cdbb..86561d158b 100644
--- a/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py
+++ b/libraries/AP_HAL_ChibiOS/hwdef/scripts/chibios_hwdef.py
@@ -47,6 +47,9 @@ portmap = {}
 # dictionary of all config lines, indexed by first word
 config = {}
 
+# alternate pin mappings
+altmap = {}
+
 # list of all pins in config file order
 allpins = []
 
@@ -255,7 +258,7 @@ class generic_pin(object):
         '''return true if this is a CS pin'''
         return self.has_extra("CS") or self.type == "CS"
 
-    def get_MODER(self):
+    def get_MODER_value(self):
         '''return one of ALTERNATE, OUTPUT, ANALOG, INPUT'''
         if self.af is not None:
             v = "ALTERNATE"
@@ -269,9 +272,18 @@ class generic_pin(object):
             v = "OUTPUT"
         else:
             v = "INPUT"
-        return "PIN_MODE_%s(%uU)" % (v, self.pin)
+        return v
 
-    def get_OTYPER(self):
+    def get_MODER_mode(self):
+        '''return one of ALTERNATE, OUTPUT, ANALOG, INPUT'''
+        return 'PAL_STM32_MODE_' + v
+
+    def get_MODER(self):
+        '''return one of ALTERNATE, OUTPUT, ANALOG, INPUT'''
+        return "PIN_MODE_%s(%uU)" % (self.get_MODER_value(), self.pin)
+
+    
+    def get_OTYPER_value(self):
         '''return one of PUSHPULL, OPENDRAIN'''
         v = 'PUSHPULL'
         if self.type.startswith('I2C'):
@@ -281,9 +293,13 @@ class generic_pin(object):
         for e in self.extra:
             if e in values:
                 v = e
-        return "PIN_OTYPE_%s(%uU)" % (v, self.pin)
+        return v
 
-    def get_OSPEEDR(self):
+    def get_OTYPER(self):
+        '''return one of PUSHPULL, OPENDRAIN'''
+        return "PIN_OTYPE_%s(%uU)" % (self.get_OTYPER_value(), self.pin)
+
+    def get_OSPEEDR_value(self):
         '''return one of SPEED_VERYLOW, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH'''
         # on STM32F4 these speeds correspond to 2MHz, 25MHz, 50MHz and 100MHz
         values = ['SPEED_VERYLOW', 'SPEED_LOW', 'SPEED_MEDIUM', 'SPEED_HIGH']
@@ -291,9 +307,21 @@ class generic_pin(object):
         for e in self.extra:
             if e in values:
                 v = e
-        return "PIN_O%s(%uU)" % (v, self.pin)
+        return v
 
-    def get_PUPDR(self):
+    def get_OSPEEDR_int(self):
+        '''return value from 0 to 3 for speed'''
+        values = ['SPEED_VERYLOW', 'SPEED_LOW', 'SPEED_MEDIUM', 'SPEED_HIGH']
+        v = self.get_OSPEEDR_value()
+        if not v in values:
+            error("Bad OSPEED %s" % v)
+        return values.index(v)
+    
+    def get_OSPEEDR(self):
+        '''return one of SPEED_VERYLOW, SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH'''
+        return "PIN_O%s(%uU)" % (self.get_OSPEEDR_value(), self.pin)
+
+    def get_PUPDR_value(self):
         '''return one of FLOATING, PULLUP, PULLDOWN'''
         values = ['FLOATING', 'PULLUP', 'PULLDOWN']
         v = 'FLOATING'
@@ -319,9 +347,13 @@ class generic_pin(object):
         for e in self.extra:
             if e in values:
                 v = e
-        return "PIN_PUPDR_%s(%uU)" % (v, self.pin)
+        return v
 
-    def get_ODR_F1(self):
+    def get_PUPDR(self):
+        '''return one of FLOATING, PULLUP, PULLDOWN wrapped in PIN_PUPDR_ macro'''
+        return "PIN_PUPDR_%s(%uU)" % (self.get_PUPDR_value(), self.pin)
+
+    def get_ODR_F1_value(self):
         '''return one of LOW, HIGH'''
         values = ['LOW', 'HIGH']
         v = 'HIGH'
@@ -337,25 +369,33 @@ class generic_pin(object):
             v = 'LOW'
             if 'PULLUP' in self.extra:
                 v = "HIGH"
-        return "PIN_ODR_%s(%uU)" % (v, self.pin)
+        return v
 
-    def get_ODR(self):
+    def get_ODR_value(self):
         '''return one of LOW, HIGH'''
         if mcu_series.startswith("STM32F1"):
-            return self.get_ODR_F1()
+            return self.get_ODR_F1_value()
         values = ['LOW', 'HIGH']
         v = 'HIGH'
         for e in self.extra:
             if e in values:
                 v = e
-        return "PIN_ODR_%s(%uU)" % (v, self.pin)
+        return v
 
-    def get_AFIO(self):
+    def get_ODR(self):
+        '''return one of LOW, HIGH wrapped in PIN_ODR macro'''
+        return "PIN_ODR_%s(%uU)" % (self.get_ODR_value(), self.pin)
+
+    def get_AFIO_value(self):
         '''return AFIO'''
         af = self.af
         if af is None:
             af = 0
-        return "PIN_AFIO_AF(%uU, %uU)" % (self.pin, af)
+        return af
+
+    def get_AFIO(self):
+        '''return AFIO wrapped in PIN_AFIO_AF macro'''
+        return "PIN_AFIO_AF(%uU, %uU)" % (self.pin, self.get_AFIO_value())
 
     def get_AFRL(self):
         '''return AFIO low 8'''
@@ -454,6 +494,19 @@ class generic_pin(object):
             return None
         return self.get_CR()
 
+    def pal_modeline(self):
+        '''return a mode line suitable for palSetModeLine()'''
+        # MODER, OTYPER, OSPEEDR, PUPDR, ODR, AFRL, AFRH
+        ret = 'PAL_STM32_MODE_' + self.get_MODER_value()
+        ret += '|PAL_STM32_OTYPE_' + self.get_OTYPER_value()
+        ret += '|PAL_STM32_SPEED(%u)' % self.get_OSPEEDR_int()
+        ret += '|PAL_STM32_PUPDR_' + self.get_PUPDR_value()
+        af = self.get_AFIO_value()
+        if af != 0:
+            ret += '|PAL_STM32_ALTERNATE(%u)' % af
+
+        return ret
+
     def __str__(self):
         str = ''
         if self.af is not None:
@@ -1437,6 +1490,24 @@ def get_dma_exclude(periph_list):
             dma_exclude.append(periph)
     return dma_exclude
 
+def write_alt_config(f):
+    '''write out alternate config settings'''
+    if len(altmap.keys()) == 0:
+        # no alt configs
+        return
+    f.write('''
+/* alternative configurations */
+#define PAL_STM32_SPEED(n) ((n&3U)<<3U)
+#define PAL_STM32_HIGH     0x8000U
+
+#define HAL_PIN_ALT_CONFIG { \\
+''')
+    for alt in altmap.keys():
+        for pp in altmap[alt].keys():
+            p = altmap[alt][pp]
+            f.write("    { %u, %s, PAL_LINE(GPIO%s,%uU)}, /* %s */ \\\n" % (alt, p.pal_modeline(), p.port, p.pin, str(p)))
+    f.write('}\n\n')
+
 def write_hwdef_header(outfilename):
     '''write hwdef header file'''
     print("Writing hwdef setup in %s" % outfilename)
@@ -1569,6 +1640,7 @@ def write_hwdef_header(outfilename):
                 # there were no pin definitions, use 0
                 f.write("0")
             f.write(")\n\n")
+    write_alt_config(f)
 
     if not mcu_series.startswith("STM32F1"):
         dma_required = ['SPI*', 'ADC*']
@@ -1655,15 +1727,7 @@ def process_line(line):
     # keep all config lines for later use
     alllines.append(line)
 
-    if a[0].startswith('P') and a[0][1] in ports and a[0] in config:
-        error("Pin %s redefined" % a[0])
-    
-    config[a[0]] = a[1:]
-    if a[0] == 'MCU':
-        global mcu_type, mcu_series
-        mcu_type = a[2]
-        mcu_series = a[1]
-        setup_mcu_type_defaults()
+    p = None
     if a[0].startswith('P') and a[0][1] in ports:
         # it is a port/pin definition
         try:
@@ -1677,28 +1741,51 @@ def process_line(line):
             return
 
         p = generic_pin(port, pin, label, type, extra)
+        af = get_alt_function(mcu_type, a[0], label)
+        if af is not None:
+            p.af = af
+
+        alt = p.extra_value("ALT", type=int, default=0)
+        if alt != 0:
+            if mcu_series.startswith("STM32F1"):
+                error("Alt config not allowed for F1 MCU")
+            if not alt in altmap:
+                altmap[alt] = {}
+            if p.portpin in altmap[alt]:
+                error("Pin %s ALT(%u) redefined" % (p.portpin, alt))
+            altmap[alt][p.portpin] = p
+            return
+
+        if a[0] in config:
+            error("Pin %s redefined" % a[0])
+    
+    config[a[0]] = a[1:]
+    if p is not None:
+        # add to set of pins for primary config
         portmap[port][pin] = p
         allpins.append(p)
         if not type in bytype:
             bytype[type] = []
         bytype[type].append(p)
         bylabel[label] = p
-        af = get_alt_function(mcu_type, a[0], label)
-        if af is not None:
-            p.af = af
-    if a[0] == 'SPIDEV':
+    elif a[0] == 'MCU':
+        global mcu_type, mcu_series
+        mcu_type = a[2]
+        mcu_series = a[1]
+        setup_mcu_type_defaults()
+    elif a[0] == 'SPIDEV':
         spidev.append(a[1:])
-    if a[0] == 'IMU':
+    elif a[0] == 'IMU':
         imu_list.append(a[1:])
-    if a[0] == 'COMPASS':
+    elif a[0] == 'COMPASS':
         compass_list.append(a[1:])
-    if a[0] == 'BARO':
+    elif a[0] == 'BARO':
         baro_list.append(a[1:])
-    if a[0] == 'ROMFS':
+    elif a[0] == 'ROMFS':
         romfs_add(a[1],a[2])
-    if a[0] == 'ROMFS_WILDCARD':
+    elif a[0] == 'ROMFS_WILDCARD':
         romfs_wildcard(a[1])
-    if a[0] == 'undef':
+    elif a[0] == 'undef':
         print("Removing %s" % a[1])
         config.pop(a[1], '')
         bytype.pop(a[1],'')
@@ -1723,7 +1810,7 @@ def process_line(line):
             compass_list = []
         if a[1] == 'BARO':
             baro_list = []
-    if a[0] == 'env':
+    elif a[0] == 'env':
         print("Adding environment %s" % ' '.join(a[1:]))
         if len(a[1:]) < 2:
             error("Bad env line for %s" % a[0])