aboutsummaryrefslogtreecommitdiff
path: root/lib/stm32
diff options
context:
space:
mode:
authorUwe Hermann2010-12-30 02:12:31 +0100
committerUwe Hermann2010-12-30 02:12:31 +0100
commit8e084b1517088036e3db9bbd255b1c231cd2a108 (patch)
tree3951e9861d36a8007e71a41d432329ca63cd81bf /lib/stm32
parentbcce98da54b047a15ef0abf41e0c7794f29f0439 (diff)
Move STM32-specific source to lib/stm32 subdir.
Diffstat (limited to 'lib/stm32')
-rw-r--r--lib/stm32/adc.c375
-rw-r--r--lib/stm32/can.c305
-rw-r--r--lib/stm32/dma.c543
-rw-r--r--lib/stm32/ethernet.c53
-rw-r--r--lib/stm32/exti.c140
-rw-r--r--lib/stm32/flash.c189
-rw-r--r--lib/stm32/gpio.c122
-rw-r--r--lib/stm32/i2c.c93
-rw-r--r--lib/stm32/nvic.c106
-rw-r--r--lib/stm32/rcc.c542
-rw-r--r--lib/stm32/rtc.c282
-rw-r--r--lib/stm32/scb.c30
-rw-r--r--lib/stm32/spi.c290
-rw-r--r--lib/stm32/systick.c64
-rw-r--r--lib/stm32/timer.c166
-rw-r--r--lib/stm32/usart.c120
16 files changed, 3420 insertions, 0 deletions
diff --git a/lib/stm32/adc.c b/lib/stm32/adc.c
new file mode 100644
index 0000000..240c3f6
--- /dev/null
+++ b/lib/stm32/adc.c
@@ -0,0 +1,375 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2009 Edward Cheeseman <evbuilder@users.sourceforge.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Basic ADC handling API.
+ *
+ * Examples:
+ * rcc_peripheral_enable_clock(&RCC_APB2ENR, ADC1EN);
+ * rcc_peripheral_disable_clock(&RCC_APB2ENR, ADC1EN);
+ * rcc_peripheral_reset(&RCC_APB2RSTR, ADC1RST);
+ * rcc_peripheral_clear_reset(&RCC_APB2RSTR, ADC1RST);
+ *
+ * rcc_set_adc_clk(ADC_PRE_PLCK2_DIV2);
+ * adc_set_mode(ADC1, TODO);
+ * reg16 = adc_read(ADC1, ADC_CH_0);
+ */
+
+#include <libopenstm32/adc.h>
+
+void rcc_set_adc_clk(u32 prescaler)
+{
+ /* TODO */
+
+ /* FIXME: QUICK HACK to prevent compiler warnings. */
+ prescaler = prescaler;
+}
+
+void adc_set_mode(u32 block, /* TODO */ u8 mode)
+{
+ /* TODO */
+
+ /* FIXME: QUICK HACK to prevent compiler warnings. */
+ block = block;
+ mode = mode;
+}
+
+void adc_read(u32 block, u32 channel)
+{
+ /* TODO */
+
+ /* FIXME: QUICK HACK to prevent compiler warnings. */
+ block = block;
+ channel = channel;
+}
+
+void adc_enable_analog_watchdog_regular(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_AWDEN;
+}
+
+void adc_disable_analog_watchdog_regular(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_AWDEN;
+}
+
+void adc_enable_analog_watchdog_injected(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_JAWDEN;
+}
+
+void adc_disable_analog_watchdog_injected(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_JAWDEN;
+}
+
+void adc_enable_discontinous_mode_regular(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_DISCEN;
+}
+
+void adc_disable_discontinous_mode_regular(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_DISCEN;
+}
+
+void adc_enable_discontinous_mode_injected(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_JDISCEN;
+}
+
+void adc_disable_discontinous_mode_injected(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_JDISCEN;
+}
+
+void adc_enable_automatic_injected_group_conversion(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_JAUTO;
+}
+
+void adc_disable_automatic_injected_group_conversion(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_JAUTO;
+}
+
+void adc_enable_analog_watchdog_on_all_channels(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_AWDSGL;
+}
+
+void adc_enable_analog_watchdog_on_selected_channel(u32 adc, u8 channel)
+{
+ u32 reg32;
+
+ reg32 = (ADC_CR1(adc) & 0xffffffe0); /* Clear bits [4:0]. */
+ if (channel < 18)
+ reg32 |= channel;
+ ADC_CR1(adc) = reg32;
+ ADC_CR1(adc) &= ~ADC_CR1_AWDSGL;
+}
+
+void adc_enable_scan_mode(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_SCAN;
+}
+
+void adc_disable_scan_mode(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_SCAN;
+}
+
+void adc_enable_jeoc_interrupt(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_JEOCIE;
+}
+
+void adc_disable_jeoc_interrupt(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_JEOCIE;
+}
+
+void adc_enable_awd_interrupt(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_AWDIE;
+}
+
+void adc_disable_awd_interrupt(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_AWDIE;
+}
+
+void adc_enable_eoc_interrupt(u32 adc)
+{
+ ADC_CR1(adc) |= ADC_CR1_EOCIE;
+}
+
+void adc_disable_eoc_interrupt(u32 adc)
+{
+ ADC_CR1(adc) &= ~ADC_CR1_EOCIE;
+}
+
+void adc_enable_temperature_sensor(u32 adc)
+{
+ ADC_CR2(adc) |= ADC_CR2_TSVREFE;
+}
+
+void adc_disable_temperature_sensor(u32 adc)
+{
+ ADC_CR2(adc) &= ~ADC_CR2_TSVREFE;
+}
+
+void adc_start_conversion_regular(u32 adc)
+{
+ /* start conversion on regular channels */
+ ADC_CR2(adc) |= ADC_CR2_SWSTART;
+
+ /* wait til the ADC starts the conversion */
+ while (ADC_CR2(adc) & ADC_CR2_SWSTART);
+}
+
+void adc_start_conversion_injected(u32 adc)
+{
+ /* start conversion on injected channels */
+ ADC_CR2(adc) |= ADC_CR2_JSWSTART;
+
+ /* wait til the ADC starts the conversion */
+ while (ADC_CR2(adc) & ADC_CR2_JSWSTART);
+}
+
+void adc_enable_external_trigger_regular(u32 adc, u8 trigger)
+{
+ u32 reg32;
+
+ reg32 = (ADC_CR2(adc) & 0xfff1ffff); /* Clear bits [19:17]. */
+ if (trigger < 8)
+ reg32 |= (trigger << ADC_CR2_EXTSEL_LSB);
+ ADC_CR2(adc) = reg32;
+ ADC_CR2(adc) |= ADC_CR2_EXTTRIG;
+}
+
+void adc_disable_external_trigger_regular(u32 adc)
+{
+ ADC_CR2(adc) &= ~ADC_CR2_EXTTRIG;
+}
+
+void adc_enable_external_trigger_injected(u32 adc, u8 trigger)
+{
+ u32 reg32;
+
+ reg32 = (ADC_CR2(adc) & 0xffff8fff); /* Clear bits [12:14]. */
+ if (trigger < 8)
+ reg32 |= (trigger << ADC_CR2_JEXTSEL_LSB);
+ ADC_CR2(adc) = reg32;
+ ADC_CR2(adc) |= ADC_CR2_JEXTTRIG;
+}
+
+void adc_disable_external_trigger_injected(u32 adc)
+{
+ ADC_CR2(adc) &= ~ADC_CR2_JEXTTRIG;
+}
+
+void adc_set_left_aligned(u32 adc)
+{
+ ADC_CR2(adc) |= ADC_CR2_ALIGN;
+}
+
+void adc_set_right_aligned(u32 adc)
+{
+ ADC_CR2(adc) &= ~ADC_CR2_ALIGN;
+}
+
+void adc_enable_dma(u32 adc)
+{
+ if ((adc == ADC1) | (adc==ADC3))
+ ADC_CR2(adc) |= ADC_CR2_DMA;
+}
+
+void adc_disable_dma(u32 adc)
+{
+ if ((adc == ADC1) | (adc==ADC3))
+ ADC_CR2(adc) &= ~ADC_CR2_DMA;
+}
+
+void adc_reset_calibration(u32 adc)
+{
+ ADC_CR2(adc) |= ADC_CR2_RSTCAL;
+ while (ADC_CR2(adc) & ADC_CR2_RSTCAL);
+}
+
+void adc_calibration(u32 adc)
+{
+ ADC_CR2(adc) |= ADC_CR2_CAL;
+ while (ADC_CR2(adc) & ADC_CR2_CAL);
+}
+
+void adc_set_continous_conversion_mode(u32 adc)
+{
+ ADC_CR2(adc) |= ADC_CR2_CONT;
+}
+
+void adc_set_single_conversion_mode(u32 adc)
+{
+ ADC_CR2(adc) &= ~ADC_CR2_CONT;
+}
+
+void adc_on(u32 adc)
+{
+ ADC_CR2(adc) |= ADC_CR2_ADON;
+}
+
+void adc_off(u32 adc)
+{
+ ADC_CR2(adc) &= ~ADC_CR2_ADON;
+}
+
+void adc_set_conversion_time(u32 adc, u8 channel, u8 time)
+{
+ u32 reg32;
+
+ if (channel < 10) {
+ reg32 = ADC_SMPR2(adc);
+ reg32 &= ~(0b111 << (channel * 3));
+ reg32 |= (time << (channel * 3));
+ ADC_SMPR2(adc) = reg32;
+ }
+ else {
+ reg32 = ADC_SMPR1(adc);
+ reg32 &= ~(0b111 << ((channel-10) * 3));
+ reg32 |= (time << ((channel-10) * 3));
+ ADC_SMPR1(adc) = reg32;
+ }
+}
+
+void adc_set_conversion_time_on_all_channels(u32 adc, u8 time)
+{
+ u32 reg32 = 0;
+ u8 i;
+
+ for (i = 0; i <= 9; i++) {
+ reg32 |= (time << (i * 3));
+ }
+ ADC_SMPR2(adc) = reg32;
+
+ for (i = 10; i <= 17; i++) {
+ reg32 |= (time << ((i-10) * 3));
+ }
+ ADC_SMPR1(adc) = reg32;
+}
+
+void adc_set_watchdog_high_threshold(u32 adc, u16 threshold)
+{
+ u32 reg32 = 0;
+
+ reg32 = (u32)threshold;
+ reg32 &= ~0xfffff000; /* clear all bits above 11 */
+ ADC_HTR(adc) = reg32;
+}
+
+void adc_set_watchdog_low_threshold(u32 adc, u16 threshold)
+{
+ u32 reg32 = 0;
+
+ reg32 = (u32)threshold;
+ reg32 &= ~0xfffff000; /* clear all bits above 11 */
+ ADC_LTR(adc) = reg32;
+}
+
+void adc_set_regular_sequence(u32 adc, u8 length, u8 channel[])
+{
+ u32 reg32_1 = 0;
+ u32 reg32_2 = 0;
+ u32 reg32_3 = 0;
+ u8 i = 0;
+
+ /* maximum sequence length is 16 channels */
+ if (length > 16)
+ return;
+
+ for (i=1; i<=length; i++) {
+ if (i <= 6)
+ reg32_3 |= (channel[i-1] << ((i-1) * 5));
+ if ((i > 6) & (i <= 12))
+ reg32_2 |= (channel[i-1] << ((i-6-1) * 5));
+ if ((i > 12) & (i <= 16))
+ reg32_1 |= (channel[i-1] << ((i-12-1) * 5));
+ }
+ reg32_1 |= ((length -1) << ADC_SQR1_L_LSB);
+
+ ADC_SQR1(adc) = reg32_1;
+ ADC_SQR2(adc) = reg32_2;
+ ADC_SQR3(adc) = reg32_3;
+}
+
+void adc_set_injected_sequence(u32 adc, u8 length, u8 channel[])
+{
+ u32 reg32 = 0;
+ u8 i = 0;
+
+ /* maximum sequence length is 4 channels */
+ if (length > 4)
+ return;
+
+ for (i = 1; i <= length; i++) {
+ reg32 |= (channel[i-1] << ((i-1) * 5));
+ }
+ reg32 |= ((length-1) << ADC_JSQR_JL_LSB);
+
+ ADC_JSQR(adc) = reg32;
+}
diff --git a/lib/stm32/can.c b/lib/stm32/can.c
new file mode 100644
index 0000000..baddab3
--- /dev/null
+++ b/lib/stm32/can.c
@@ -0,0 +1,305 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/can.h>
+#include <libopenstm32/rcc.h>
+
+void can_reset(u32 canport)
+{
+ if (canport == CAN1) {
+ rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1RSTR_CAN1RST);
+ rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1RSTR_CAN1RST);
+ } else {
+ rcc_peripheral_reset(&RCC_APB1RSTR, RCC_APB1RSTR_CAN2RST);
+ rcc_peripheral_clear_reset(&RCC_APB1RSTR, RCC_APB1RSTR_CAN2RST);
+ }
+}
+
+int can_init(u32 canport, bool ttcm, bool abom, bool awum, bool nart,
+ bool rflm, bool txfp, u32 sjw, u32 ts1, u32 ts2, u32 brp)
+{
+ u32 wait_ack = 0x00000000;
+ u32 can_msr_inak_timeout = 0x0000FFFF;
+ int ret = 0;
+
+ /* Exit from sleep mode. */
+ CAN_MCR(canport) &= ~CAN_MCR_SLEEP;
+
+ /* Request initialization "enter". */
+ CAN_MCR(canport) |= CAN_MCR_INRQ;
+
+ /* Wait for acknowledge. */
+ while ((wait_ack != can_msr_inak_timeout) &&
+ ((CAN_MSR(canport) & CAN_MSR_INAK) != CAN_MSR_INAK)) {
+ wait_ack++;
+ }
+
+ /* Check the acknowledge. */
+ if ((CAN_MSR(canport) & CAN_MSR_INAK) != CAN_MSR_INAK)
+ return 1;
+
+ /* Set the automatic bus-off management. */
+ if (ttcm)
+ CAN_MCR(canport) |= CAN_MCR_TTCM;
+ else
+ CAN_MCR(canport) &= ~CAN_MCR_TTCM;
+
+ if (abom)
+ CAN_MCR(canport) |= CAN_MCR_ABOM;
+ else
+ CAN_MCR(canport) &= ~CAN_MCR_ABOM;
+
+ if (awum)
+ CAN_MCR(canport) |= CAN_MCR_AWUM;
+ else
+ CAN_MCR(canport) &= ~CAN_MCR_AWUM;
+
+ if (nart)
+ CAN_MCR(canport) |= CAN_MCR_NART;
+ else
+ CAN_MCR(canport) &= ~CAN_MCR_NART;
+
+ if (rflm)
+ CAN_MCR(canport) |= CAN_MCR_RFLM;
+ else
+ CAN_MCR(canport) &= ~CAN_MCR_RFLM;
+
+ if (txfp)
+ CAN_MCR(canport) |= CAN_MCR_TXFP;
+ else
+ CAN_MCR(canport) &= ~CAN_MCR_TXFP;
+
+ /* Set bit timings. */
+ CAN_BTR(canport) = sjw | ts2 | ts1 |
+ (u32)(CAN_BTR_BRP_MASK & (brp - 1));
+
+ /* Request initialization "leave". */
+ CAN_MCR(canport) &= ~CAN_MCR_INRQ;
+
+ /* Wait for acknowledge. */
+ wait_ack = 0x00000000;
+ while ((wait_ack != can_msr_inak_timeout) &&
+ ((CAN_MSR(canport) & CAN_MSR_INAK) == CAN_MSR_INAK)) {
+ wait_ack++;
+ }
+
+ if ((CAN_MSR(canport) & CAN_MSR_INAK) == CAN_MSR_INAK)
+ ret = 1;
+
+ return ret;
+}
+
+void can_filter_init(u32 canport, u32 nr, bool scale_32bit, bool id_list_mode,
+ u32 fr1, u32 fr2, u32 fifo, bool enable)
+{
+ u32 filter_select_bit = 0x00000001 << nr;
+
+ /* Request initialization "enter". */
+ CAN_FMR(canport) |= CAN_FMR_FINIT;
+
+ /* Deactivate the filter. */
+ CAN_FA1R(canport) &= ~filter_select_bit;
+
+ if (scale_32bit) {
+ /* Set 32-bit scale for the filter. */
+ CAN_FS1R(canport) |= filter_select_bit;
+ } else {
+ /* Set 16-bit scale for the filter. */
+ CAN_FS1R(canport) &= ~filter_select_bit;
+ }
+
+ if (id_list_mode) {
+ /* Set filter mode to ID list mode. */
+ CAN_FM1R(canport) |= filter_select_bit;
+ } else {
+ /* Set filter mode to id/mask mode. */
+ CAN_FM1R(canport) &= ~filter_select_bit;
+ }
+
+ /* Set the first filter register. */
+ CAN_FiR1(canport, nr) = fr1;
+
+ /* Set the second filter register. */
+ CAN_FiR2(canport, nr) = fr2;
+
+ /* Select FIFO0 or FIFO1 as filter assignement. */
+ if (fifo)
+ CAN_FFA1R(canport) |= filter_select_bit; /* FIFO1 */
+ else
+ CAN_FFA1R(canport) &= ~filter_select_bit; /* FIFO0 */
+
+ if (enable)
+ CAN_FA1R(canport) |= filter_select_bit; /* Activate filter. */
+
+ /* Request initialization "leave". */
+ CAN_FMR(canport) &= ~CAN_FMR_FINIT;
+}
+
+void can_filter_id_mask_16bit_init(u32 canport, u32 nr, u16 id1, u16 mask1,
+ u16 id2, u16 mask2, u32 fifo, bool enable)
+{
+ can_filter_init(canport, nr, false, false,
+ ((u32)id1 << 16) | (u32)mask1,
+ ((u32)id2 << 16) | (u32)mask2, fifo, enable);
+}
+
+void can_filter_id_mask_32bit_init(u32 canport, u32 nr, u32 id, u32 mask,
+ u32 fifo, bool enable)
+{
+ can_filter_init(canport, nr, true, false, id, mask, fifo, enable);
+}
+
+void can_filter_id_list_16bit_init(u32 canport, u32 nr, u16 id1, u16 id2,
+ u16 id3, u16 id4, u32 fifo, bool enable)
+{
+ can_filter_init(canport, nr, false, true,
+ ((u32)id1 << 16) | (u32)id2,
+ ((u32)id3 << 16) | (u32)id4, fifo, enable);
+}
+
+void can_filter_id_list_32bit_init(u32 canport, u32 nr, u32 id1, u32 id2,
+ u32 fifo, bool enable)
+{
+ can_filter_init(canport, nr, true, true, id1, id2, fifo, enable);
+}
+
+void can_enable_irq(u32 canport, u32 irq)
+{
+ CAN_IER(canport) |= irq;
+}
+
+void can_disable_irq(u32 canport, u32 irq)
+{
+ CAN_IER(canport) &= ~irq;
+}
+
+int can_transmit(u32 canport, u32 id, bool ext, bool rtr, u8 length, u8 *data)
+{
+ int ret = 0, i;
+ u32 mailbox = 0;
+
+ if ((CAN_TSR(canport) & CAN_TSR_TME0) == CAN_TSR_TME0) {
+ ret = 0;
+ mailbox = CAN_MBOX0;
+ } else if ((CAN_TSR(canport) & CAN_TSR_TME1) == CAN_TSR_TME1) {
+ ret = 1;
+ mailbox = CAN_MBOX1;
+ } else if ((CAN_TSR(canport) & CAN_TSR_TME2) == CAN_TSR_TME2) {
+ ret = 2;
+ mailbox = CAN_MBOX2;
+ } else {
+ ret = -1;
+ }
+
+ /* Check if we have an empty mailbox. */
+ if (ret == -1)
+ return ret;
+
+ if (ext) {
+ /* Set extended ID. */
+ CAN_TIxR(canport, mailbox) |= id << CAN_TIxR_EXID_SHIFT;
+ /* Set extended ID indicator bit. */
+ CAN_TIxR(canport, mailbox) |= CAN_TIxR_IDE;
+ } else {
+ /* Set standard ID. */
+ CAN_TIxR(canport, mailbox) |= id << CAN_TIxR_STID_SHIFT;
+ /* Unset extended ID indicator bit. */
+ CAN_TIxR(canport, mailbox) &= ~CAN_TIxR_IDE;
+ }
+
+ /* Set/clear remote transmission request bit. */
+ if (rtr)
+ CAN_TIxR(canport, mailbox) |= CAN_TIxR_RTR; /* Set */
+ else
+ CAN_TIxR(canport, mailbox) &= ~CAN_TIxR_RTR; /* Clear */
+
+ /* Set the DLC. */
+ CAN_TDTxR(canport, mailbox) &= 0xFFFFFFFF0;
+ CAN_TDTxR(canport, mailbox) |= length & CAN_TDTxR_DLC_MASK;
+
+ /* Set the data. */
+ CAN_TDLxR(canport, mailbox) = 0;
+ CAN_TDHxR(canport, mailbox) = 0;
+ for (i = 0; (i < 4) && (i < length); i++)
+ CAN_TDLxR(canport, mailbox) |= (u32)data[i] << (8 * i);
+ for (i = 4; (i < 8) && (i < length); i++)
+ CAN_TDHxR(canport, mailbox) |= (u32)data[i] << (8 * (i - 4));
+
+ /* Request transmission. */
+ CAN_TIxR(canport, mailbox) |= CAN_TIxR_TXRQ;
+
+ return ret;
+}
+
+void can_fifo_release(u32 canport, u8 fifo)
+{
+ if (fifo == 0)
+ CAN_RF0R(canport) |= CAN_RF1R_RFOM1;
+ else
+ CAN_RF1R(canport) |= CAN_RF1R_RFOM1;
+}
+
+void can_receive(u32 canport, u8 fifo, bool release, u32 *id, bool *ext,
+ bool *rtr, u32 *fmi, u8 *length, u8 *data)
+{
+ u32 fifo_id = 0;
+ int i;
+
+ if (fifo == 0)
+ fifo_id = CAN_FIFO0;
+ else
+ fifo_id = CAN_FIFO1;
+
+ /* Get type of CAN ID and CAN ID. */
+ if (CAN_RIxR(canport, fifo_id) & CAN_RIxR_IDE) {
+ *ext = true;
+ /* Get extended CAN ID. */
+ *id = ((CAN_RIxR(canport, fifo_id) & CAN_RIxR_EXID_MASK) >
+ CAN_RIxR_EXID_SHIFT);
+ } else {
+ *ext = false;
+ /* Get standard CAN ID. */
+ *id = ((CAN_RIxR(canport, fifo_id) & CAN_RIxR_STID_MASK) >
+ CAN_RIxR_STID_SHIFT);
+ }
+
+ /* Get request transmit flag. */
+ if (CAN_RIxR(canport, fifo_id) & CAN_RIxR_RTR)
+ *rtr = true;
+ else
+ *rtr = false;
+
+ /* Get filter match ID. */
+ *fmi = ((CAN_RDTxR(canport, fifo_id) & CAN_RDTxR_FMI_MASK) >
+ CAN_RDTxR_FMI_SHIFT);
+
+ /* Get data length. */
+ *length = CAN_RDTxR(canport, fifo_id) & CAN_RDTxR_DLC_MASK;
+
+ /* Get data. */
+ for (i = 0; (i < 4) && (i < *length); i++)
+ data[i] = (CAN_RDLxR(canport, fifo_id) >> (8 * i)) & 0xFF;
+
+ for (i = 4; (i < 8) && (i < *length); i++)
+ data[i] = (CAN_RDHxR(canport, fifo_id) >> (8 * (i - 4))) & 0xFF;
+
+ /* Release the FIFO. */
+ if (release)
+ can_fifo_release(CAN1, 0);
+}
diff --git a/lib/stm32/dma.c b/lib/stm32/dma.c
new file mode 100644
index 0000000..71df580
--- /dev/null
+++ b/lib/stm32/dma.c
@@ -0,0 +1,543 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/dma.h>
+
+void dma_enable_mem2mem_mode(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_MEM2MEM;
+ DMA_CCR1(dma) &= ~DMA_CCR1_CIRC;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_MEM2MEM;
+ DMA_CCR2(dma) &= ~DMA_CCR2_CIRC;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_MEM2MEM;
+ DMA_CCR3(dma) &= ~DMA_CCR3_CIRC;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_MEM2MEM;
+ DMA_CCR4(dma) &= ~DMA_CCR4_CIRC;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_MEM2MEM;
+ DMA_CCR5(dma) &= ~DMA_CCR5_CIRC;
+ case 6:
+ if (dma == DMA1) {
+ DMA_CCR6(dma) |= DMA_CCR6_MEM2MEM;
+ DMA_CCR6(dma) &= ~DMA_CCR6_CIRC;
+ }
+ case 7:
+ if (dma == DMA1) {
+ DMA_CCR7(dma) |= DMA_CCR7_MEM2MEM;
+ DMA_CCR7(dma) &= ~DMA_CCR7_CIRC;
+ }
+ }
+}
+
+
+
+
+void dma_set_priority(u32 dma, u8 channel, u8 prio)
+{
+ /* parameter check */
+ if (prio > 3)
+ return;
+
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~(0x3 << DMA_CCR1_PL_LSB);
+ DMA_CCR1(dma) |= (prio << DMA_CCR1_PL_LSB);
+ case 2:
+ DMA_CCR2(dma) &= ~(0x3 << DMA_CCR2_PL_LSB);
+ DMA_CCR2(dma) |= (prio << DMA_CCR2_PL_LSB);
+ case 3:
+ DMA_CCR3(dma) &= ~(0x3 << DMA_CCR3_PL_LSB);
+ DMA_CCR3(dma) |= (prio << DMA_CCR3_PL_LSB);
+ case 4:
+ DMA_CCR4(dma) &= ~(0x3 << DMA_CCR4_PL_LSB);
+ DMA_CCR4(dma) |= (prio << DMA_CCR4_PL_LSB);
+ case 5:
+ DMA_CCR5(dma) &= ~(0x3 << DMA_CCR5_PL_LSB);
+ DMA_CCR5(dma) |= (prio << DMA_CCR5_PL_LSB);
+ case 6:
+ if (dma == DMA1) {
+ DMA_CCR6(dma) &= ~(0x3 << DMA_CCR6_PL_LSB);
+ DMA_CCR6(dma) |= (prio << DMA_CCR6_PL_LSB);
+ }
+ case 7:
+ if (dma == DMA1) {
+ DMA_CCR7(dma) &= ~(0x3 << DMA_CCR7_PL_LSB);
+ DMA_CCR7(dma) |= (prio << DMA_CCR7_PL_LSB);
+ }
+ }
+}
+
+void dma_set_memory_size(u32 dma, u8 channel, u8 mem_size)
+{
+ /* parameter check */
+ if (mem_size > 2)
+ return;
+
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~(0x3 << DMA_CCR1_MSIZE_LSB);
+ DMA_CCR1(dma) |= (mem_size << DMA_CCR1_MSIZE_LSB);
+ case 2:
+ DMA_CCR2(dma) &= ~(0x3 << DMA_CCR2_MSIZE_LSB);
+ DMA_CCR2(dma) |= (mem_size << DMA_CCR2_MSIZE_LSB);
+ case 3:
+ DMA_CCR3(dma) &= ~(0x3 << DMA_CCR3_MSIZE_LSB);
+ DMA_CCR3(dma) |= (mem_size << DMA_CCR3_MSIZE_LSB);
+ case 4:
+ DMA_CCR4(dma) &= ~(0x3 << DMA_CCR4_MSIZE_LSB);
+ DMA_CCR4(dma) |= (mem_size << DMA_CCR4_MSIZE_LSB);
+ case 5:
+ DMA_CCR5(dma) &= ~(0x3 << DMA_CCR5_MSIZE_LSB);
+ DMA_CCR5(dma) |= (mem_size << DMA_CCR5_MSIZE_LSB);
+ case 6:
+ if (dma == DMA1) {
+ DMA_CCR6(dma) &= ~(0x3 << DMA_CCR6_MSIZE_LSB);
+ DMA_CCR6(dma) |= (mem_size << DMA_CCR6_MSIZE_LSB);
+ }
+ case 7:
+ if (dma == DMA1) {
+ DMA_CCR7(dma) &= ~(0x3 << DMA_CCR7_MSIZE_LSB);
+ DMA_CCR7(dma) |= (mem_size << DMA_CCR7_MSIZE_LSB);
+ }
+ }
+}
+
+
+
+void dma_set_peripheral_size(u32 dma, u8 channel, u8 peripheral_size)
+{
+ /* parameter check */
+ if (peripheral_size > 2)
+ return;
+
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~(0x3 << DMA_CCR1_PSIZE_LSB);
+ DMA_CCR1(dma) |= (peripheral_size << DMA_CCR1_PSIZE_LSB);
+ case 2:
+ DMA_CCR2(dma) &= ~(0x3 << DMA_CCR2_PSIZE_LSB);
+ DMA_CCR2(dma) |= (peripheral_size << DMA_CCR2_PSIZE_LSB);
+ case 3:
+ DMA_CCR3(dma) &= ~(0x3 << DMA_CCR3_PSIZE_LSB);
+ DMA_CCR3(dma) |= (peripheral_size << DMA_CCR3_PSIZE_LSB);
+ case 4:
+ DMA_CCR4(dma) &= ~(0x3 << DMA_CCR4_PSIZE_LSB);
+ DMA_CCR4(dma) |= (peripheral_size << DMA_CCR4_PSIZE_LSB);
+ case 5:
+ DMA_CCR5(dma) &= ~(0x3 << DMA_CCR5_PSIZE_LSB);
+ DMA_CCR5(dma) |= (peripheral_size << DMA_CCR5_PSIZE_LSB);
+ case 6:
+ if (dma == DMA1) {
+ DMA_CCR6(dma) &= ~(0x3 << DMA_CCR6_PSIZE_LSB);
+ DMA_CCR6(dma) |= (peripheral_size << DMA_CCR6_PSIZE_LSB);
+ }
+ case 7:
+ if (dma == DMA1) {
+ DMA_CCR7(dma) &= ~(0x3 << DMA_CCR7_PSIZE_LSB);
+ DMA_CCR7(dma) |= (peripheral_size << DMA_CCR7_PSIZE_LSB);
+ }
+ }
+}
+
+
+void dma_enable_memory_increment_mode(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_MINC;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_MINC;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_MINC;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_MINC;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_MINC;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_MINC;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_MINC;
+ }
+}
+
+void dma_enable_peripheral_increment_mode(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_PINC;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_PINC;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_PINC;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_PINC;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_PINC;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_PINC;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_PINC;
+ }
+}
+
+void dma_enable_circular_mode(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_CIRC;
+ DMA_CCR1(dma) &= ~DMA_CCR1_MEM2MEM;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_CIRC;
+ DMA_CCR2(dma) &= ~DMA_CCR2_MEM2MEM;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_CIRC;
+ DMA_CCR3(dma) &= ~DMA_CCR3_MEM2MEM;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_CIRC;
+ DMA_CCR4(dma) &= ~DMA_CCR4_MEM2MEM;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_CIRC;
+ DMA_CCR5(dma) &= ~DMA_CCR5_MEM2MEM;
+ case 6:
+ if (dma == DMA1) {
+ DMA_CCR6(dma) |= DMA_CCR6_CIRC;
+ DMA_CCR6(dma) &= ~DMA_CCR6_MEM2MEM;
+ }
+ case 7:
+ if (dma == DMA1) {
+ DMA_CCR7(dma) |= DMA_CCR7_CIRC;
+ DMA_CCR7(dma) &= ~DMA_CCR7_MEM2MEM;
+ }
+ }
+}
+
+void dma_set_read_from_peripheral(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~DMA_CCR1_DIR;
+ case 2:
+ DMA_CCR2(dma) &= ~DMA_CCR2_DIR;
+ case 3:
+ DMA_CCR3(dma) &= ~DMA_CCR3_DIR;
+ case 4:
+ DMA_CCR4(dma) &= ~DMA_CCR4_DIR;
+ case 5:
+ DMA_CCR5(dma) &= ~DMA_CCR5_DIR;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) &= ~DMA_CCR6_DIR;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) &= ~DMA_CCR7_DIR;
+ }
+}
+
+void dma_set_read_from_memory(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_DIR;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_DIR;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_DIR;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_DIR;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_DIR;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_DIR;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_DIR;
+ }
+}
+
+void dma_enable_transfer_error_interrupt(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_TEIE;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_TEIE;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_TEIE;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_TEIE;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_TEIE;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_TEIE;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_TEIE;
+ }
+}
+
+void dma_disable_transfer_error_interrupt(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~DMA_CCR1_TEIE;
+ case 2:
+ DMA_CCR2(dma) &= ~DMA_CCR2_TEIE;
+ case 3:
+ DMA_CCR3(dma) &= ~DMA_CCR3_TEIE;
+ case 4:
+ DMA_CCR4(dma) &= ~DMA_CCR4_TEIE;
+ case 5:
+ DMA_CCR5(dma) &= ~DMA_CCR5_TEIE;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) &= ~DMA_CCR6_TEIE;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) &= ~DMA_CCR7_TEIE;
+ }
+}
+
+void dma_enable_half_transfer_interrupt(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_HTIE;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_HTIE;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_HTIE;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_HTIE;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_HTIE;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_HTIE;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_HTIE;
+ }
+}
+
+void dma_disable_half_transfer_interrupt(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~DMA_CCR1_HTIE;
+ case 2:
+ DMA_CCR2(dma) &= ~DMA_CCR2_HTIE;
+ case 3:
+ DMA_CCR3(dma) &= ~DMA_CCR3_HTIE;
+ case 4:
+ DMA_CCR4(dma) &= ~DMA_CCR4_HTIE;
+ case 5:
+ DMA_CCR5(dma) &= ~DMA_CCR5_HTIE;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) &= ~DMA_CCR6_HTIE;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) &= ~DMA_CCR7_HTIE;
+ }
+}
+
+void dma_enable_transfer_complete_interrupt(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_TCIE;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_TCIE;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_TCIE;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_TCIE;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_TCIE;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_TCIE;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_TCIE;
+ }
+}
+
+void dma_disable_transfer_complete_interrupt(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~DMA_CCR1_TCIE;
+ case 2:
+ DMA_CCR2(dma) &= ~DMA_CCR2_TCIE;
+ case 3:
+ DMA_CCR3(dma) &= ~DMA_CCR3_TCIE;
+ case 4:
+ DMA_CCR4(dma) &= ~DMA_CCR4_TCIE;
+ case 5:
+ DMA_CCR5(dma) &= ~DMA_CCR5_TCIE;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) &= ~DMA_CCR6_TCIE;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) &= ~DMA_CCR7_TCIE;
+ }
+}
+
+void dma_enable_channel(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) |= DMA_CCR1_EN;
+ case 2:
+ DMA_CCR2(dma) |= DMA_CCR2_EN;
+ case 3:
+ DMA_CCR3(dma) |= DMA_CCR3_EN;
+ case 4:
+ DMA_CCR4(dma) |= DMA_CCR4_EN;
+ case 5:
+ DMA_CCR5(dma) |= DMA_CCR5_EN;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) |= DMA_CCR6_EN;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) |= DMA_CCR7_EN;
+ }
+}
+
+void dma_disable_channel(u32 dma, u8 channel)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CCR1(dma) &= ~DMA_CCR1_EN;
+ case 2:
+ DMA_CCR2(dma) &= ~DMA_CCR2_EN;
+ case 3:
+ DMA_CCR3(dma) &= ~DMA_CCR3_EN;
+ case 4:
+ DMA_CCR4(dma) &= ~DMA_CCR4_EN;
+ case 5:
+ DMA_CCR5(dma) &= ~DMA_CCR5_EN;
+ case 6:
+ if (dma == DMA1)
+ DMA_CCR6(dma) &= ~DMA_CCR6_EN;
+ case 7:
+ if (dma == DMA1)
+ DMA_CCR7(dma) &= ~DMA_CCR7_EN;
+ }
+}
+
+void dma_set_peripheral_address(u32 dma, u8 channel, u32 address)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CPAR1(dma) = (u32)address;
+ case 2:
+ DMA_CPAR2(dma) = (u32)address;
+ case 3:
+ DMA_CPAR3(dma) = (u32)address;
+ case 4:
+ DMA_CPAR4(dma) = (u32)address;
+ case 5:
+ DMA_CPAR5(dma) = (u32)address;
+ case 6:
+ if (dma == DMA1)
+ DMA_CPAR6(dma) = (u32)address;
+ case 7:
+ if (dma == DMA1)
+ DMA_CPAR7(dma) = (u32)address;
+ }
+}
+
+void dma_set_memory_address(u32 dma, u8 channel, u32 address)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CMAR1(dma) = (u32)address;
+ case 2:
+ DMA_CMAR2(dma) = (u32)address;
+ case 3:
+ DMA_CMAR3(dma) = (u32)address;
+ case 4:
+ DMA_CMAR4(dma) = (u32)address;
+ case 5:
+ DMA_CMAR5(dma) = (u32)address;
+ case 6:
+ if (dma == DMA1)
+ DMA_CMAR6(dma) = (u32)address;
+ case 7:
+ if (dma == DMA1)
+ DMA_CMAR7(dma) = (u32)address;
+ }
+}
+
+void dma_set_number_of_data(u32 dma, u8 channel, u16 number)
+{
+ switch (channel)
+ {
+ case 1:
+ DMA_CNDTR1(dma) = number;
+ case 2:
+ DMA_CNDTR2(dma) = number;
+ case 3:
+ DMA_CNDTR3(dma) = number;
+ case 4:
+ DMA_CNDTR4(dma) = number;
+ case 5:
+ DMA_CNDTR5(dma) = number;
+ case 6:
+ if (dma == DMA1)
+ DMA_CNDTR6(dma) = number;
+ case 7:
+ if (dma == DMA1)
+ DMA_CNDTR7(dma) = number;
+ }
+}
diff --git a/lib/stm32/ethernet.c b/lib/stm32/ethernet.c
new file mode 100644
index 0000000..c1abd03
--- /dev/null
+++ b/lib/stm32/ethernet.c
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/ethernet.h>
+
+void eth_smi_write(u8 phy, u8 reg, u16 data)
+{
+ /* Set PHY and register addresses for write access. */
+ ETH_MACMIIAR &= ~(ETH_MACMIIAR_MR | ETH_MACMIIAR_PA);
+ ETH_MACMIIAR |= (phy << 11) | (reg << 6) | ETH_MACMIIAR_MW;
+
+ /* Set register value. */
+ ETH_MACMIIDR = data;
+
+ /* Begin transaction. */
+ ETH_MACMIIAR |= ETH_MACMIIAR_MB;
+
+ /* Wait for not busy. */
+ while (ETH_MACMIIAR & ETH_MACMIIAR_MB);
+}
+
+u16 eth_smi_read(u8 phy, u8 reg)
+{
+ /* Set PHY and register addresses for write access. */
+ ETH_MACMIIAR &= ~(ETH_MACMIIAR_MR | ETH_MACMIIAR_PA |
+ ETH_MACMIIAR_MW);
+ ETH_MACMIIAR |= (phy << 11) | (reg << 6);
+
+ /* Begin transaction. */
+ ETH_MACMIIAR |= ETH_MACMIIAR_MB;
+
+ /* Wait for not busy. */
+ while (ETH_MACMIIAR & ETH_MACMIIAR_MB);
+
+ /* Set register value. */
+ return (u16)(ETH_MACMIIDR);
+}
diff --git a/lib/stm32/exti.c b/lib/stm32/exti.c
new file mode 100644
index 0000000..df7f4cb
--- /dev/null
+++ b/lib/stm32/exti.c
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Mark Butler <mbutler@physics.otago.ac.nz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/exti.h>
+#include <libopenstm32/gpio.h>
+
+void exti_set_trigger(u32 extis, exti_trigger_type trig)
+{
+ switch (trig) {
+ case EXTI_TRIGGER_RISING:
+ EXTI_RTSR |= extis;
+ EXTI_FTSR &= ~extis;
+ break;
+ case EXTI_TRIGGER_FALLING:
+ EXTI_RTSR &= ~extis;
+ EXTI_FTSR |= extis;
+ break;
+ case EXTI_TRIGGER_BOTH:
+ EXTI_RTSR |= extis;
+ EXTI_FTSR |= extis;
+ break;
+ }
+}
+
+void exti_enable_request(u32 extis)
+{
+ /* Enable interrupts. */
+ EXTI_IMR |= extis;
+
+ /* Enable events. */
+ EXTI_EMR |= extis;
+}
+
+void exti_disable_request(u32 extis)
+{
+ /* Disable interrupts. */
+ EXTI_IMR &= ~extis;
+
+ /* Disable events. */
+ EXTI_EMR &= ~extis;
+}
+
+/*
+ * Reset the interrupt request by writing a 1 to the corresponding
+ * pending bit register.
+ */
+void exti_reset_request(u32 extis)
+{
+ EXTI_PR |= extis;
+}
+
+/*
+ * Remap an external interrupt line to the corresponding pin on the
+ * specified GPIO port.
+ *
+ * TODO: This could be rewritten in less lines of code.
+ */
+void exti_select_source(u32 exti, u32 gpioport)
+{
+ u8 shift, bits;
+
+ shift = bits = 0;
+
+ switch (exti) {
+ case EXTI0:
+ case EXTI4:
+ case EXTI8:
+ case EXTI12:
+ shift = 0;
+ break;
+ case EXTI1:
+ case EXTI5:
+ case EXTI9:
+ case EXTI13:
+ shift = 4;
+ break;
+ case EXTI2:
+ case EXTI6:
+ case EXTI10:
+ case EXTI14:
+ shift = 8;
+ break;
+ case EXTI3:
+ case EXTI7:
+ case EXTI11:
+ case EXTI15:
+ shift = 12;
+ break;
+ }
+
+ switch (gpioport) {
+ case GPIOA:
+ bits = 0xf;
+ break;
+ case GPIOB:
+ bits = 0xe;
+ break;
+ case GPIOC:
+ bits = 0xd;
+ break;
+ case GPIOD:
+ bits = 0xc;
+ break;
+ case GPIOE:
+ bits = 0xb;
+ break;
+ case GPIOF:
+ bits = 0xa;
+ break;
+ case GPIOG:
+ bits = 0x9;
+ break;
+ }
+
+ /* Ensure that only valid EXTI lines are used. */
+ if (exti < EXTI4)
+ AFIO_EXTICR1 &= ~(bits << shift);
+ else if (exti < EXTI8)
+ AFIO_EXTICR2 &= ~(bits << shift);
+ else if (exti < EXTI12)
+ AFIO_EXTICR3 &= ~(bits << shift);
+ else if (exti < EXTI16)
+ AFIO_EXTICR4 &= ~(bits << shift);
+}
diff --git a/lib/stm32/flash.c b/lib/stm32/flash.c
new file mode 100644
index 0000000..085aed4
--- /dev/null
+++ b/lib/stm32/flash.c
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ * Copyright (C) 2010 Mark Butler <mbutler@physics.otago.ac.nz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/flash.h>
+
+void flash_prefetch_buffer_enable(void)
+{
+ FLASH_ACR |= FLASH_PRFTBE;
+}
+
+void flash_prefetch_buffer_disable(void)
+{
+ FLASH_ACR &= ~FLASH_PRFTBE;
+}
+
+void flash_halfcycle_enable(void)
+{
+ FLASH_ACR |= FLASH_HLFCYA;
+}
+
+void flash_halfcycle_disable(void)
+{
+ FLASH_ACR &= ~FLASH_HLFCYA;
+}
+
+void flash_set_ws(u32 ws)
+{
+ u32 reg32;
+
+ reg32 = FLASH_ACR;
+ reg32 &= ~((1 << 0) | (1 << 1) | (1 << 2));
+ reg32 |= ws;
+ FLASH_ACR = reg32;
+}
+
+void flash_unlock(void)
+{
+ /* Authorize the FPEC access. */
+ FLASH_KEYR = FLASH_KEY1;
+ FLASH_KEYR = FLASH_KEY2;
+}
+
+void flash_lock(void)
+{
+ FLASH_CR |= FLASH_LOCK;
+}
+
+void flash_clear_pgerr_flag(void)
+{
+ FLASH_SR |= FLASH_PGERR;
+}
+
+void flash_clear_eop_flag(void)
+{
+ FLASH_SR |= FLASH_EOP;
+}
+
+void flash_clear_wrprterr_flag(void)
+{
+ FLASH_SR |= FLASH_WRPRTERR;
+}
+
+void flash_clear_bsy_flag(void)
+{
+ FLASH_SR &= ~FLASH_BSY;
+}
+
+void flash_clear_status_flags(void)
+{
+ flash_clear_pgerr_flag();
+ flash_clear_eop_flag();
+ flash_clear_wrprterr_flag();
+ flash_clear_bsy_flag();
+}
+
+void flash_unlock_option_bytes(void)
+{
+ FLASH_OPTKEYR = FLASH_KEY1;
+ FLASH_OPTKEYR = FLASH_KEY2;
+}
+
+void flash_wait_for_last_operation(void)
+{
+ while ((FLASH_SR & FLASH_BSY) == FLASH_BSY)
+ ;
+}
+
+void flash_program_word(u32 address, u32 data)
+{
+ /* Ensure that all flash operations are complete. */
+ flash_wait_for_last_operation();
+
+ /* Enable writes to flash. */
+ FLASH_CR |= FLASH_PG;
+
+ /* Program the first half of the word. */
+ (*(volatile u16 *)address) = (u16)data;
+
+ /* Wait for the write to complete. */
+ flash_wait_for_last_operation();
+
+ /* Program the second half of the word. */
+ (*(volatile u16 *)(address + 2)) = data >> 16;
+
+ /* Wait for the write to complete. */
+ flash_wait_for_last_operation();
+
+ /* Disable writes to flash. */
+ FLASH_CR &= ~FLASH_PG;
+}
+
+void flash_program_half_word(u32 address, u16 data)
+{
+ flash_wait_for_last_operation();
+
+ FLASH_CR |= FLASH_PG;
+
+ (*(volatile u16 *)address) = data;
+
+ flash_wait_for_last_operation();
+
+ FLASH_CR &= ~FLASH_PG; /* Disable the PG bit. */
+}
+
+void flash_erase_page(u32 page_address)
+{
+ flash_wait_for_last_operation();
+
+ FLASH_CR |= FLASH_PER;
+ FLASH_AR = page_address;
+ FLASH_CR |= FLASH_STRT;
+
+ flash_wait_for_last_operation();
+ FLASH_CR &= ~FLASH_PER;
+}
+
+void flash_erase_all_pages(void)
+{
+ flash_wait_for_last_operation();
+
+ FLASH_CR |= FLASH_MER; /* Enable mass erase. */
+ FLASH_CR |= FLASH_STRT; /* Trigger the erase. */
+
+ flash_wait_for_last_operation();
+ FLASH_CR &= ~FLASH_MER; /* Disable mass erase. */
+}
+
+void flash_erase_option_bytes(void)
+{
+ flash_wait_for_last_operation();
+
+ if ((FLASH_CR & FLASH_OPTWRE) == 0)
+ flash_unlock_option_bytes();
+
+ FLASH_CR |= FLASH_OPTER; /* Enable option byte erase. */
+ FLASH_CR |= FLASH_STRT;
+ flash_wait_for_last_operation();
+ FLASH_CR &= ~FLASH_OPTER; /* Disable option byte erase. */
+}
+
+void flash_program_option_bytes(u32 address, u16 data)
+{
+ flash_wait_for_last_operation();
+
+ if ((FLASH_CR & FLASH_OPTWRE) == 0)
+ flash_unlock_option_bytes();
+
+ FLASH_CR |= FLASH_OPTPG; /* Enable option byte programming. */
+ (*(volatile u16 *)address) = data;
+ flash_wait_for_last_operation();
+ FLASH_CR &= ~FLASH_OPTPG; /* Disable option byte programming. */
+}
diff --git a/lib/stm32/gpio.c b/lib/stm32/gpio.c
new file mode 100644
index 0000000..747977d
--- /dev/null
+++ b/lib/stm32/gpio.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Basic GPIO handling API.
+ *
+ * Examples:
+ * gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,
+ * GPIO_CNF_OUTPUT_PUSHPULL, GPIO12);
+ * gpio_set(GPIOB, GPIO4);
+ * gpio_clear(GPIOG, GPIO2 | GPIO9);
+ * gpio_get(GPIOC, GPIO1);
+ * gpio_toggle(GPIOA, GPIO7);
+ * reg16 = gpio_port_read(GPIOD);
+ * gpio_port_write(GPIOF, 0xc8fe);
+ *
+ * TODO:
+ * - GPIO remapping support
+ */
+
+#include <libopenstm32/gpio.h>
+
+void gpio_set_mode(u32 gpioport, u8 mode, u8 cnf, u16 gpios)
+{
+ u16 i, offset = 0;
+ u32 crl = 0, crh = 0, tmp32 = 0;
+
+ /*
+ * We want to set the config only for the pins mentioned in gpios,
+ * but keeping the others, so read out the actual config first.
+ */
+ crl = GPIO_CRL(gpioport);
+ crh = GPIO_CRH(gpioport);
+
+ /* Iterate over all bits, use i as the bitnumber. */
+ for (i = 0; i < 16; i++) {
+ /* Only set the config if the bit is set in gpios. */
+ if (!((1 << i) & gpios))
+ continue;
+
+ /* Calculate bit offset. */
+ offset = (i < 8) ? (i * 4) : ((i - 8) * 4);
+
+ /* Use tmp32 to either modify crl or crh. */
+ tmp32 = (i < 8) ? crl : crh;
+
+ /* Modify bits are needed. */
+ tmp32 &= ~(0b1111 << offset); /* Clear the bits first. */
+ tmp32 |= (mode << offset) | (cnf << (offset + 2));
+
+ /* Write tmp32 into crl or crh, leave the other unchanged. */
+ crl = (i < 8) ? tmp32 : crl;
+ crh = (i >= 8) ? tmp32 : crh;
+ }
+
+ GPIO_CRL(gpioport) = crl;
+ GPIO_CRH(gpioport) = crh;
+}
+
+void gpio_set(u32 gpioport, u16 gpios)
+{
+ GPIO_BSRR(gpioport) = gpios;
+}
+
+void gpio_clear(u32 gpioport, u16 gpios)
+{
+ GPIO_BRR(gpioport) = gpios;
+}
+
+u16 gpio_get(u32 gpioport, u16 gpios)
+{
+ return gpio_port_read(gpioport) & gpios;
+}
+
+/* TODO: Should work for multiple GPIOs? */
+void gpio_toggle(u32 gpioport, u16 gpio)
+{
+ if ((gpio_port_read(gpioport) & gpio) == gpio)
+ gpio_clear(gpioport, gpio);
+ else
+ gpio_set(gpioport, gpio);
+}
+
+u16 gpio_port_read(u32 gpioport)
+{
+ return (u16)GPIO_IDR(gpioport);
+}
+
+void gpio_port_write(u32 gpioport, u16 data)
+{
+ GPIO_ODR(gpioport) = data;
+}
+
+void gpio_port_config_lock(u32 gpioport, u16 gpios)
+{
+ u32 reg32;
+
+ /* Special "Lock Key Writing Sequence", see datasheet. */
+ GPIO_LCKR(gpioport) = GPIO_LCKK | gpios; /* Set LCKK. */
+ GPIO_LCKR(gpioport) = ~GPIO_LCKK & gpios; /* Clear LCKK. */
+ GPIO_LCKR(gpioport) = GPIO_LCKK | gpios; /* Set LCKK. */
+ reg32 = GPIO_LCKR(gpioport); /* Read LCKK. */
+ reg32 = GPIO_LCKR(gpioport); /* Read LCKK again. */
+
+ /* If (reg32 & GPIO_LCKK) is true, the lock is now active. */
+}
diff --git a/lib/stm32/i2c.c b/lib/stm32/i2c.c
new file mode 100644
index 0000000..e161b7f
--- /dev/null
+++ b/lib/stm32/i2c.c
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/i2c.h>
+
+void i2c_peripheral_enable(u32 i2c)
+{
+ I2C_CR1(i2c) |= I2C_CR1_PE;
+}
+
+void i2c_peripheral_disable(u32 i2c)
+{
+ I2C_CR1(i2c) &= ~I2C_CR1_PE;
+}
+
+void i2c_send_start(u32 i2c)
+{
+ I2C_CR1(i2c) |= I2C_CR1_START;
+}
+
+void i2c_send_stop(u32 i2c)
+{
+ I2C_CR1(i2c) |= I2C_CR1_STOP;
+}
+
+void i2c_set_own_7bit_slave_address(u32 i2c, u8 slave)
+{
+ I2C_OAR1(i2c) = (u16)(slave << 1);
+ I2C_OAR1(i2c) &= ~I2C_OAR1_ADDMODE;
+ I2C_OAR1(i2c) |= (1 << 14); /* Datasheet: always keep 1 by software. */
+}
+
+void i2c_set_own_10bit_slave_address(u32 i2c, u16 slave)
+{
+ I2C_OAR1(i2c) = (u16)(I2C_OAR1_ADDMODE | slave);
+}
+
+void i2c_set_fast_mode(u32 i2c)
+{
+ I2C_CCR(i2c) |= I2C_CCR_FS;
+}
+
+void i2c_set_standard_mode(u32 i2c)
+{
+ I2C_CCR(i2c) &= ~I2C_CCR_FS;
+}
+
+void i2c_set_clock_frequency(u32 i2c, u8 freq)
+{
+ u16 reg16;
+ reg16 = I2C_CR2(i2c) & 0xffc0; /* Clear bits [5:0]. */
+ reg16 |= freq;
+ I2C_CR2(i2c) = reg16;
+}
+
+void i2c_set_ccr(u32 i2c, u16 freq)
+{
+ u16 reg16;
+ reg16 = I2C_CCR(i2c) & 0xf000; /* Clear bits [11:0]. */
+ reg16 |= freq;
+ I2C_CCR(i2c) = reg16;
+}
+
+void i2c_set_trise(u32 i2c, u16 trise)
+{
+ I2C_TRISE(i2c) = trise;
+}
+
+void i2c_send_7bit_address(u32 i2c, u8 slave, u8 readwrite)
+{
+ I2C_DR(i2c) = (u8)((slave << 1) | readwrite);
+}
+
+void i2c_send_data(u32 i2c, u8 data)
+{
+ I2C_DR(i2c) = data;
+}
diff --git a/lib/stm32/nvic.c b/lib/stm32/nvic.c
new file mode 100644
index 0000000..4dffe45
--- /dev/null
+++ b/lib/stm32/nvic.c
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/nvic.h>
+
+void nvic_enable_irq(u8 irqn)
+{
+ if (irqn < 32)
+ NVIC_ISER(0) |= (1 << irqn);
+ if ((irqn >= 32) & (irqn < 64))
+ NVIC_ISER(1) |= (1 << (irqn - 32));
+ if ((irqn >= 64) & (irqn < 68))
+ NVIC_ISER(2) |= (1 << (irqn - 64));
+}
+
+void nvic_disable_irq(u8 irqn)
+{
+ if (irqn < 32)
+ NVIC_ICER(0) |= (1 << irqn);
+ if ((irqn >= 32) & (irqn < 64))
+ NVIC_ICER(1) |= (1 << (irqn - 32));
+ if ((irqn >= 64) & (irqn < 68))
+ NVIC_ICER(2) |= (1 << (irqn - 64));
+}
+
+u8 nvic_get_pending_irq(u8 irqn)
+{
+ if (irqn < 32)
+ return (NVIC_ISPR(0) & (1 << irqn));
+ if ((irqn >= 32) & (irqn < 64))
+ return (NVIC_ISPR(1) & (1 << (irqn - 32)));
+ if ((irqn >= 64) & (irqn < 68))
+ return (NVIC_ISPR(2) & (1 << (irqn - 64)));
+ return 0;
+}
+
+void nvic_set_pending_irq(u8 irqn)
+{
+ if (irqn < 32)
+ NVIC_ISPR(0) |= (1 << irqn);
+ if ((irqn >= 32) & (irqn < 64))
+ NVIC_ISPR(1) |= (1 << (irqn - 32));
+ if ((irqn >= 64) & (irqn < 68))
+ NVIC_ISPR(2) |= (1 << (irqn - 64));
+}
+
+void nvic_clear_pending_irq(u8 irqn)
+{
+ if (irqn < 32)
+ NVIC_ICPR(0) |= (1 << irqn);
+ if ((irqn >= 32) & (irqn < 64))
+ NVIC_ICPR(1) |= (1 << (irqn - 32));
+ if ((irqn >= 64) & (irqn < 68))
+ NVIC_ICPR(2) |= (1 << (irqn - 64));
+}
+
+u8 nvic_get_active_irq(u8 irqn)
+{
+ if (irqn < 32)
+ return (NVIC_IABR(0) & (1 << irqn));
+ if ((irqn >= 32) & (irqn < 64))
+ return (NVIC_IABR(1) & (1 << (irqn - 32)));
+ if ((irqn >= 64) & (irqn < 68))
+ return (NVIC_IABR(2) & (1 << (irqn - 64)));
+ return 0;
+}
+
+u8 nvic_get_irq_enabled(u8 irqn)
+{
+ if (irqn < 32)
+ return (NVIC_ISER(0) & (1 << irqn));
+ if ((irqn >= 32) & (irqn < 64))
+ return (NVIC_ISER(1) & (1 << (irqn - 32)));
+ if ((irqn >= 64) & (irqn < 68))
+ return (NVIC_ISER(2) & (1 << (irqn - 64)));
+ return 0;
+}
+
+void nvic_set_priority(u8 irqn, u8 priority)
+{
+ NVIC_IPR(irqn/4) |= (priority << ((irqn % 4) * 8));
+}
+
+void nvic_generate_software_interrupt(u8 irqn)
+{
+ if (irqn <= 239)
+ NVIC_STIR |= irqn;
+}
+
+
diff --git a/lib/stm32/rcc.c b/lib/stm32/rcc.c
new file mode 100644
index 0000000..dc4dca1
--- /dev/null
+++ b/lib/stm32/rcc.c
@@ -0,0 +1,542 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2009 Federico Ruiz-Ugalde <memeruiz at gmail dot com>
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/rcc.h>
+#include <libopenstm32/flash.h>
+
+void rcc_osc_ready_int_clear(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ RCC_CIR |= RCC_CIR_PLLRDYC;
+ break;
+ case HSE:
+ RCC_CIR |= RCC_CIR_HSERDYC;
+ break;
+ case HSI:
+ RCC_CIR |= RCC_CIR_HSIRDYC;
+ break;
+ case LSE:
+ RCC_CIR |= RCC_CIR_LSERDYC;
+ break;
+ case LSI:
+ RCC_CIR |= RCC_CIR_LSIRDYC;
+ break;
+ }
+}
+
+void rcc_osc_ready_int_enable(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ RCC_CIR |= RCC_CIR_PLLRDYIE;
+ break;
+ case HSE:
+ RCC_CIR |= RCC_CIR_HSERDYIE;
+ break;
+ case HSI:
+ RCC_CIR |= RCC_CIR_HSIRDYIE;
+ break;
+ case LSE:
+ RCC_CIR |= RCC_CIR_LSERDYIE;
+ break;
+ case LSI:
+ RCC_CIR |= RCC_CIR_LSIRDYIE;
+ break;
+ }
+}
+
+void rcc_osc_ready_int_disable(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ RCC_CIR &= ~RCC_CIR_PLLRDYIE;
+ break;
+ case HSE:
+ RCC_CIR &= ~RCC_CIR_HSERDYIE;
+ break;
+ case HSI:
+ RCC_CIR &= ~RCC_CIR_HSIRDYIE;
+ break;
+ case LSE:
+ RCC_CIR &= ~RCC_CIR_LSERDYIE;
+ break;
+ case LSI:
+ RCC_CIR &= ~RCC_CIR_LSIRDYIE;
+ break;
+ }
+}
+
+int rcc_osc_ready_int_flag(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ return ((RCC_CIR & RCC_CIR_PLLRDYF) != 0);
+ break;
+ case HSE:
+ return ((RCC_CIR & RCC_CIR_HSERDYF) != 0);
+ break;
+ case HSI:
+ return ((RCC_CIR & RCC_CIR_HSIRDYF) != 0);
+ break;
+ case LSE:
+ return ((RCC_CIR & RCC_CIR_LSERDYF) != 0);
+ break;
+ case LSI:
+ return ((RCC_CIR & RCC_CIR_LSIRDYF) != 0);
+ break;
+ }
+
+ /* Shouldn't be reached. */
+ return -1;
+}
+
+void rcc_css_int_clear(void)
+{
+ RCC_CIR |= RCC_CIR_CSSC;
+}
+
+int rcc_css_int_flag(void)
+{
+ return ((RCC_CIR & RCC_CIR_CSSF) != 0);
+}
+
+void rcc_wait_for_osc_ready(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ while ((RCC_CR & RCC_CR_PLLRDY) == 0);
+ break;
+ case HSE:
+ while ((RCC_CR & RCC_CR_HSERDY) == 0);
+ break;
+ case HSI:
+ while ((RCC_CR & RCC_CR_HSIRDY) == 0);
+ break;
+ case LSE:
+ while ((RCC_BDCR & RCC_BDCR_LSERDY) == 0);
+ break;
+ case LSI:
+ while ((RCC_CSR & RCC_CSR_LSIRDY) == 0);
+ break;
+ }
+}
+
+void rcc_osc_on(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ RCC_CR |= RCC_CR_PLLON;
+ break;
+ case HSE:
+ RCC_CR |= RCC_CR_HSEON;
+ break;
+ case HSI:
+ RCC_CR |= RCC_CR_HSION;
+ break;
+ case LSE:
+ RCC_BDCR |= RCC_BDCR_LSEON;
+ break;
+ case LSI:
+ RCC_CSR |= RCC_CSR_LSION;
+ break;
+ }
+}
+
+void rcc_osc_off(osc_t osc)
+{
+ switch (osc) {
+ case PLL:
+ RCC_CR &= ~RCC_CR_PLLON;
+ break;
+ case HSE:
+ RCC_CR &= ~RCC_CR_HSEON;
+ break;
+ case HSI:
+ RCC_CR &= ~RCC_CR_HSION;
+ break;
+ case LSE:
+ RCC_BDCR &= ~RCC_BDCR_LSEON;
+ break;
+ case LSI:
+ RCC_CSR &= ~RCC_CSR_LSION;
+ break;
+ }
+}
+
+void rcc_css_enable(void)
+{
+ RCC_CR |= RCC_CR_CSSON;
+}
+
+void rcc_css_disable(void)
+{
+ RCC_CR &= ~RCC_CR_CSSON;
+}
+
+void rcc_osc_bypass_enable(osc_t osc)
+{
+ switch (osc) {
+ case HSE:
+ RCC_CR |= RCC_CR_HSEBYP;
+ break;
+ case LSE:
+ RCC_BDCR |= RCC_BDCR_LSEBYP;
+ break;
+ case PLL:
+ case HSI:
+ case LSI:
+ /* Do nothing, only HSE/LSE allowed here. */
+ break;
+ }
+}
+
+void rcc_osc_bypass_disable(osc_t osc)
+{
+ switch (osc) {
+ case HSE:
+ RCC_CR &= ~RCC_CR_HSEBYP;
+ break;
+ case LSE:
+ RCC_BDCR &= ~RCC_BDCR_LSEBYP;
+ break;
+ case PLL:
+ case HSI:
+ case LSI:
+ /* Do nothing, only HSE/LSE allowed here. */
+ break;
+ }
+}
+
+void rcc_peripheral_enable_clock(volatile u32 *reg, u32 en)
+{
+ *reg |= en;
+}
+
+void rcc_peripheral_disable_clock(volatile u32 *reg, u32 en)
+{
+ *reg &= ~en;
+}
+
+void rcc_peripheral_reset(volatile u32 *reg, u32 reset)
+{
+ *reg |= reset;
+}
+
+void rcc_peripheral_clear_reset(volatile u32 *reg, u32 clear_reset)
+{
+ *reg &= ~clear_reset;
+}
+
+void rcc_set_sysclk_source(u32 clk)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~((1 << 1) | (1 << 0));
+ RCC_CFGR = (reg32 | clk);
+}
+
+void rcc_set_pll_multiplication_factor(u32 mul)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~((1 << 21) | (1 << 20) | (1 << 19) | (1 << 18));
+ RCC_CFGR = (reg32 | (mul << 18));
+}
+
+void rcc_set_pll_source(u32 pllsrc)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~(1 << 16);
+ RCC_CFGR = (reg32 | (pllsrc << 16));
+}
+
+void rcc_set_pllxtpre(u32 pllxtpre)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~(1 << 17);
+ RCC_CFGR = (reg32 | (pllxtpre << 17));
+}
+
+void rcc_set_adcpre(u32 adcpre)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~((1 << 14) | (1 << 15));
+ RCC_CFGR = (reg32 | (adcpre << 14));
+}
+
+void rcc_set_ppre2(u32 ppre2)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~((1 << 11) | (1 << 12) | (1 << 13));
+ RCC_CFGR = (reg32 | (ppre2 << 11));
+}
+
+void rcc_set_ppre1(u32 ppre1)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~((1 << 8) | (1 << 9) | (1 << 10));
+ RCC_CFGR = (reg32 | (ppre1 << 8));
+}
+
+void rcc_set_hpre(u32 hpre)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~((1 << 4) | (1 << 5) | (1 << 6) | (1 << 7));
+ RCC_CFGR = (reg32 | (hpre << 4));
+}
+
+void rcc_set_usbpre(u32 usbpre)
+{
+ u32 reg32;
+
+ reg32 = RCC_CFGR;
+ reg32 &= ~(1 << 22);
+ RCC_CFGR = (reg32 | (usbpre << 22));
+}
+
+u32 rcc_system_clock_source(void)
+{
+ /* Return the clock source which is used as system clock. */
+ return ((RCC_CFGR & 0x000c) >> 2);
+}
+
+/*
+ * These functions are setting up the whole clock system for the most common
+ * input clock and output clock configurations.
+ */
+void rcc_clock_setup_in_hsi_out_64mhz(void)
+{
+ /* Enable internal high-speed oscillator. */
+ rcc_osc_on(HSI);
+ rcc_wait_for_osc_ready(HSI);
+
+ /* Select HSI as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
+
+ /*
+ * Set prescalers for AHB, ADC, ABP1, ABP2.
+ * Do this before touching the PLL (TODO: why?).
+ */
+ rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /* Max. 72MHz */
+ rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV8); /* Max. 14MHz */
+ rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /* Max. 36MHz */
+ rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /* Max. 72MHz */
+
+ /*
+ * Sysclk is running with 64MHz -> 2 waitstates.
+ * 0WS from 0-24MHz
+ * 1WS from 24-48MHz
+ * 2WS from 48-72MHz
+ */
+ flash_set_ws(FLASH_LATENCY_2WS);
+
+ /*
+ * Set the PLL multiplication factor to 16.
+ * 8MHz (internal) * 16 (multiplier) / 2 (PLLSRC_HSI_CLK_DIV2) = 64MHz
+ */
+ rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL16);
+
+ /* Select HSI/2 as PLL source. */
+ rcc_set_pll_source(RCC_CFGR_PLLSRC_HSI_CLK_DIV2);
+
+ /* Enable PLL oscillator and wait for it to stabilize. */
+ rcc_osc_on(PLL);
+ rcc_wait_for_osc_ready(PLL);
+
+ /* Select PLL as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
+}
+
+void rcc_clock_setup_in_hsi_out_48mhz(void)
+{
+ /* Enable internal high-speed oscillator. */
+ rcc_osc_on(HSI);
+ rcc_wait_for_osc_ready(HSI);
+
+ /* Select HSI as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
+
+ /*
+ * Set prescalers for AHB, ADC, ABP1, ABP2.
+ * Do this before touching the PLL (TODO: why?).
+ */
+ rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /* Max. 72MHz */
+ rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV8); /* Max. 14MHz */
+ rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /* Max. 36MHz */
+ rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /* Max. 72MHz */
+ rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_CLK_NODIV); /* 48 MHz */
+
+ /*
+ * Sysclk runs with 48MHz -> 1 waitstates.
+ * 0WS from 0-24MHz
+ * 1WS from 24-48MHz
+ * 2WS from 48-72MHz
+ */
+ flash_set_ws(FLASH_LATENCY_1WS);
+
+ /*
+ * Set the PLL multiplication factor to 12.
+ * 8MHz (internal) * 12 (multiplier) / 2 (PLLSRC_HSI_CLK_DIV2) = 48MHz
+ */
+ rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL12);
+
+ /* Select HSI/2 as PLL source. */
+ rcc_set_pll_source(RCC_CFGR_PLLSRC_HSI_CLK_DIV2);
+
+ /* Enable PLL oscillator and wait for it to stabilize. */
+ rcc_osc_on(PLL);
+ rcc_wait_for_osc_ready(PLL);
+
+ /* Select PLL as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
+}
+
+void rcc_clock_setup_in_hse_8mhz_out_72mhz(void)
+{
+ /* Enable internal high-speed oscillator. */
+ rcc_osc_on(HSI);
+ rcc_wait_for_osc_ready(HSI);
+
+ /* Select HSI as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
+
+ /* Enable external high-speed oscillator 8MHz. */
+ rcc_osc_on(HSE);
+ rcc_wait_for_osc_ready(HSE);
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSECLK);
+
+ /*
+ * Set prescalers for AHB, ADC, ABP1, ABP2.
+ * Do this before touching the PLL (TODO: why?).
+ */
+ rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /* Max. 72MHz */
+ rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV8); /* Max. 14MHz */
+ rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /* Max. 36MHz */
+ rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /* Max. 72MHz */
+
+ /*
+ * Sysclk runs with 72MHz -> 2 waitstates.
+ * 0WS from 0-24MHz
+ * 1WS from 24-48MHz
+ * 2WS from 48-72MHz
+ */
+ flash_set_ws(FLASH_LATENCY_2WS);
+
+ /*
+ * Set the PLL multiplication factor to 9.
+ * 8MHz (external) * 9 (multiplier) = 72MHz
+ */
+ rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL9);
+
+ /* Select HSE as PLL source. */
+ rcc_set_pll_source(RCC_CFGR_PLLSRC_HSE_CLK);
+
+ /*
+ * External frequency undivided before entering PLL
+ * (only valid/needed for HSE).
+ */
+ rcc_set_pllxtpre(RCC_CFGR_PLLXTPRE_HSE_CLK);
+
+ /* Enable PLL oscillator and wait for it to stabilize. */
+ rcc_osc_on(PLL);
+ rcc_wait_for_osc_ready(PLL);
+
+ /* Select PLL as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
+}
+
+void rcc_clock_setup_in_hse_16mhz_out_72mhz(void)
+{
+ /* Enable internal high-speed oscillator. */
+ rcc_osc_on(HSI);
+ rcc_wait_for_osc_ready(HSI);
+
+ /* Select HSI as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
+
+ /* Enable external high-speed oscillator 16MHz. */
+ rcc_osc_on(HSE);
+ rcc_wait_for_osc_ready(HSE);
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSECLK);
+
+ /*
+ * Set prescalers for AHB, ADC, ABP1, ABP2.
+ * Do this before touching the PLL (TODO: why?).
+ */
+ rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV); /* Max. 72MHz */
+ rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV6); /* Max. 14MHz */
+ rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2); /* Max. 36MHz */
+ rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV); /* Max. 72MHz */
+
+ /*
+ * Sysclk runs with 72MHz -> 2 waitstates.
+ * 0WS from 0-24MHz
+ * 1WS from 24-48MHz
+ * 2WS from 48-72MHz
+ */
+ flash_set_ws(FLASH_LATENCY_2WS);
+
+ /*
+ * Set the PLL multiplication factor to 9.
+ * 16MHz (external) * 9 (multiplier) / 2 (PLLXTPRE_HSE_CLK_DIV2) = 72MHz
+ */
+ rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL9);
+
+ /* Select HSI as PLL source. */
+ rcc_set_pll_source(RCC_CFGR_PLLSRC_HSE_CLK);
+
+ /*
+ * Divide external frequency by 2 before entering PLL
+ * (only valid/needed for HSE).
+ */
+ rcc_set_pllxtpre(RCC_CFGR_PLLXTPRE_HSE_CLK_DIV2);
+
+ /* Enable PLL oscillator and wait for it to stabilize. */
+ rcc_osc_on(PLL);
+ rcc_wait_for_osc_ready(PLL);
+
+ /* Select PLL as SYSCLK source. */
+ rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
+}
+
+void rcc_backupdomain_reset(void)
+{
+ /* Set the backup domain software reset. */
+ RCC_BDCR |= RCC_BDCR_BDRST;
+
+ /* Clear the backup domain software reset. */
+ RCC_BDCR &= ~RCC_BDCR_BDRST;
+}
diff --git a/lib/stm32/rtc.c b/lib/stm32/rtc.c
new file mode 100644
index 0000000..604177f
--- /dev/null
+++ b/lib/stm32/rtc.c
@@ -0,0 +1,282 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe@hermann-uwe.de>
+ * Copyright (C) 2010 Lord James <lordjames@y7mail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/rcc.h>
+#include <libopenstm32/rtc.h>
+#include <libopenstm32/pwr.h>
+
+void rtc_awake_from_off(osc_t clock_source)
+{
+ u32 reg32;
+
+ /* Enable power and backup interface clocks. */
+ RCC_APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN);
+
+ /* Enable access to the backup registers and the RTC. */
+ PWR_CR |= PWR_CR_DBP;
+
+ /*
+ * Reset the backup domain, clears everything RTC related.
+ * If not wanted use the rtc_awake_from_standby() function.
+ */
+ rcc_backupdomain_reset();
+
+ switch (clock_source) {
+ case LSE:
+ /* Turn the LSE on and wait while it stabilises. */
+ RCC_BDCR |= RCC_BDCR_LSEON;
+ while ((reg32 = (RCC_BDCR & RCC_BDCR_LSERDY)) == 0);
+
+ /* Choose LSE as the RTC clock source. */
+ RCC_BDCR &= ~((1 << 8) | (1 << 9));
+ RCC_BDCR |= (1 << 8);
+ break;
+ case LSI:
+ /* Turn the LSI on and wait while it stabilises. */
+ RCC_CSR |= RCC_CSR_LSION;
+ while ((reg32 = (RCC_CSR & RCC_CSR_LSIRDY)) == 0);
+
+ /* Choose LSI as the RTC clock source. */
+ RCC_BDCR &= ~((1 << 8) | (1 << 9));
+ RCC_BDCR |= (1 << 9);
+ break;
+ case HSE:
+ /* Turn the HSE on and wait while it stabilises. */
+ RCC_CR |= RCC_CR_HSEON;
+ while ((reg32 = (RCC_CR & RCC_CR_HSERDY)) == 0);
+
+ /* Choose HSE as the RTC clock source. */
+ RCC_BDCR &= ~((1 << 8) | (1 << 9));
+ RCC_BDCR |= (1 << 9) | (1 << 8);
+ break;
+ case PLL:
+ case HSI:
+ /* Unusable clock source, here to prevent warnings. */
+ /* Turn off clock sources to RTC. */
+ RCC_BDCR &= ~((1 << 8) | (1 << 9));
+ break;
+ }
+
+ /* Enable the RTC. */
+ RCC_BDCR |= RCC_BDCR_RTCEN;
+
+ /* Wait for the RSF bit in RTC_CRL to be set by hardware. */
+ RTC_CRL &= ~RTC_CRL_RSF;
+ while ((reg32 = (RTC_CRL & RTC_CRL_RSF)) == 0);
+
+ /* Wait for the last write operation to finish. */
+ /* TODO: Necessary? */
+ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0);
+}
+
+void rtc_enter_config_mode(void)
+{
+ u32 reg32;
+
+ /* Wait until the RTOFF bit is 1 (no RTC register writes ongoing). */
+ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0);
+
+ /* Enter configuration mode. */
+ RTC_CRL |= RTC_CRL_CNF;
+}
+
+void rtc_exit_config_mode(void)
+{
+ /* u32 reg32; */
+
+ /* Exit configuration mode. */
+ RTC_CRL &= ~RTC_CRL_CNF;
+
+ /* Wait until the RTOFF bit is 1 (our RTC register write finished). */
+ /* while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0); */
+ /* TODO: Unnecessary since we poll the bit on config entry(?) */
+}
+
+void rtc_set_alarm_time(u32 alarm_time)
+{
+ rtc_enter_config_mode();
+ RTC_ALRL = (alarm_time & 0x0000ffff);
+ RTC_ALRH = (alarm_time & 0xffff0000) >> 16;
+ rtc_exit_config_mode();
+}
+
+void rtc_enable_alarm(void)
+{
+ rtc_enter_config_mode();
+ RTC_CRH |= RTC_CRH_ALRIE;
+ rtc_exit_config_mode();
+}
+
+void rtc_disable_alarm(void)
+{
+ rtc_enter_config_mode();
+ RTC_CRH &= ~RTC_CRH_ALRIE;
+ rtc_exit_config_mode();
+}
+
+void rtc_set_prescale_val(u32 prescale_val)
+{
+ rtc_enter_config_mode();
+ RTC_PRLL = prescale_val & 0x0000ffff; /* PRL[15:0] */
+ RTC_PRLH = (prescale_val & 0x000f0000) >> 16; /* PRL[19:16] */
+ rtc_exit_config_mode();
+}
+
+u32 rtc_get_counter_val(void)
+{
+ return (RTC_CNTH << 16) | RTC_CNTL;
+}
+
+u32 rtc_get_prescale_div_val(void)
+{
+ return (RTC_DIVH << 16) | RTC_DIVL;
+}
+
+u32 rtc_get_alarm_val(void)
+{
+ return (RTC_ALRH << 16) | RTC_ALRL;
+}
+
+void rtc_set_counter_val(u32 counter_val)
+{
+ rtc_enter_config_mode();
+ RTC_CNTH = (counter_val & 0xffff0000) >> 16; /* CNT[31:16] */
+ RTC_CNTL = counter_val & 0x0000ffff; /* CNT[15:0] */
+ rtc_exit_config_mode();
+}
+
+void rtc_interrupt_enable(rtcflag_t flag_val)
+{
+ rtc_enter_config_mode();
+
+ /* Set the correct interrupt enable. */
+ switch(flag_val) {
+ case RTC_SEC:
+ RTC_CRH |= RTC_CRH_SECIE;
+ break;
+ case RTC_ALR:
+ RTC_CRH |= RTC_CRH_ALRIE;
+ break;
+ case RTC_OW:
+ RTC_CRH |= RTC_CRH_OWIE;
+ break;
+ }
+
+ rtc_exit_config_mode();
+}
+
+void rtc_interrupt_disable(rtcflag_t flag_val)
+{
+ rtc_enter_config_mode();
+
+ /* Disable the correct interrupt enable. */
+ switch(flag_val) {
+ case RTC_SEC:
+ RTC_CRH &= ~RTC_CRH_SECIE;
+ break;
+ case RTC_ALR:
+ RTC_CRH &= ~RTC_CRH_ALRIE;
+ break;
+ case RTC_OW:
+ RTC_CRH &= ~RTC_CRH_OWIE;
+ break;
+ }
+
+ rtc_exit_config_mode();
+}
+
+void rtc_clear_flag(rtcflag_t flag_val)
+{
+ /* Configuration mode not needed. */
+
+ /* Clear the correct flag. */
+ switch(flag_val) {
+ case RTC_SEC:
+ RTC_CRL &= ~RTC_CRL_SECF;
+ break;
+ case RTC_ALR:
+ RTC_CRL &= ~RTC_CRL_ALRF;
+ break;
+ case RTC_OW:
+ RTC_CRL &= ~RTC_CRL_OWF;
+ break;
+ }
+}
+
+u32 rtc_check_flag(rtcflag_t flag_val)
+{
+ u32 reg32;
+
+ /* Read correct flag. */
+ switch(flag_val) {
+ case RTC_SEC:
+ reg32 = RTC_CRL & RTC_CRL_SECF;
+ break;
+ case RTC_ALR:
+ reg32 = RTC_CRL & RTC_CRL_ALRF;
+ break;
+ case RTC_OW:
+ reg32 = RTC_CRL & RTC_CRL_OWF;
+ break;
+ default:
+ reg32 = 0;
+ break;
+ }
+
+ return reg32;
+}
+
+void rtc_awake_from_standby(void)
+{
+ u32 reg32;
+
+ /* Enable power and backup interface clocks. */
+ RCC_APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN);
+
+ /* Enable access to the backup registers and the RTC. */
+ PWR_CR |= PWR_CR_DBP;
+
+ /* Wait for the RSF bit in RTC_CRL to be set by hardware. */
+ RTC_CRL &= ~RTC_CRL_RSF;
+ while ((reg32 = (RTC_CRL & RTC_CRL_RSF)) == 0);
+
+ /* Wait for the last write operation to finish. */
+ /* TODO: Necessary? */
+ while ((reg32 = (RTC_CRL & RTC_CRL_RTOFF)) == 0);
+}
+
+void rtc_auto_awake(osc_t clock_source, u32 prescale_val)
+{
+ u32 reg32;
+
+ /* Enable power and backup interface clocks. */
+ RCC_APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN);
+
+ /* Enable access to the backup registers and the RTC. */
+ /* TODO: Not sure if this is necessary to just read the flag. */
+ PWR_CR |= PWR_CR_DBP;
+
+ if ((reg32 = RCC_BDCR & RCC_BDCR_RTCEN) != 0) {
+ rtc_awake_from_standby();
+ } else {
+ rtc_awake_from_off(clock_source);
+ rtc_set_prescale_val(prescale_val);
+ }
+}
diff --git a/lib/stm32/scb.c b/lib/stm32/scb.c
new file mode 100644
index 0000000..d6744a1
--- /dev/null
+++ b/lib/stm32/scb.c
@@ -0,0 +1,30 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/scb.h>
+
+void scb_reset_core(void)
+{
+ SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_VECTRESET;
+}
+
+void scb_reset_system(void)
+{
+ SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ;
+}
diff --git a/lib/stm32/spi.c b/lib/stm32/spi.c
new file mode 100644
index 0000000..0acd392
--- /dev/null
+++ b/lib/stm32/spi.c
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/spi.h>
+
+/*
+ * SPI and I2S code.
+ *
+ * Examples:
+ * spi_init_master(SPI1, 1000000, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
+ * SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_8BIT,
+ * SPI_CR1_LSBFIRST);
+ * spi_write(SPI1, 0x55); // 8-bit write
+ * spi_write(SPI1, 0xaa88); // 16-bit write
+ * reg8 = spi_read(SPI1); // 8-bit read
+ * reg16 = spi_read(SPI1); // 16-bit read
+ */
+
+int spi_init_master(u32 spi, u32 br, u32 cpol, u32 cpha, u32 dff, u32 lsbfirst)
+{
+ u32 reg32 = 0;
+
+ reg32 |= SPI_CR1_MSTR; /* Configure SPI as master. */
+
+ reg32 |= br; /* Set BAUD rate bits. */
+ reg32 |= cpol; /* Set CPOL value. */
+ reg32 |= cpha; /* Set CPHA value. */
+ reg32 |= dff; /* Set data format (8 or 16 bits). */
+ reg32 |= lsbfirst; /* Set frame format (LSB- or MSB-first). */
+
+ /* TODO: NSS pin handling. */
+
+ SPI_CR1(spi) = reg32;
+
+ return 0; /* TODO */
+}
+
+/* TODO: Error handling? */
+void spi_enable(u32 spi)
+{
+ u32 reg32;
+
+ reg32 = SPI_CR1(spi);
+ reg32 |= SPI_CR1_SPE; /* Enable SPI. */
+ SPI_CR1(spi) = reg32;
+}
+
+/* TODO: Error handling? */
+void spi_disable(u32 spi)
+{
+ u32 reg32;
+
+ /* TODO: Follow procedure from section 23.3.8 in the techref manual. */
+ reg32 = SPI_CR1(spi);
+ reg32 &= ~(SPI_CR1_SPE); /* Disable SPI. */
+ SPI_CR1(spi) = reg32;
+}
+
+void spi_write(u32 spi, u16 data)
+{
+ /* Write data (8 or 16 bits, depending on DFF) into DR. */
+ SPI_DR(spi) = data;
+}
+
+void spi_send(u32 spi, u16 data)
+{
+ /* Write data (8 or 16 bits, depending on DFF) into DR. */
+ SPI_DR(spi) = data;
+
+ /* wait for transfer finished */
+ while (SPI_SR(spi) & SPI_SR_BSY )
+ {
+ }
+}
+
+u16 spi_read(u32 spi)
+{
+ /* Read the data (8 or 16 bits, depending on DFF bit) from DR. */
+ return SPI_DR(spi);
+}
+
+void spi_set_bidirectional_mode(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_BIDIMODE;
+}
+
+void spi_set_unidirectional_mode(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_BIDIMODE;
+}
+
+void spi_set_bidirectional_receive_only_mode(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_BIDIMODE;
+ SPI_CR1(spi) &= ~SPI_CR1_BIDIOE;
+}
+
+void spi_set_bidirectional_transmit_only_mode(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_BIDIMODE;
+ SPI_CR1(spi) |= SPI_CR1_BIDIOE;
+}
+
+void spi_enable_crc(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_CRCEN;
+}
+
+void spi_disable_crc(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_CRCEN;
+}
+
+void spi_set_next_tx_from_buffer(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_CRCNEXT;
+}
+
+void spi_set_next_tx_from_crc(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_CRCNEXT;
+}
+
+void spi_set_dff_8bit(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_DFF;
+}
+
+void spi_set_dff_16bit(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_DFF;
+}
+
+void spi_set_full_duplex_mode(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_RXONLY;
+}
+
+void spi_set_receive_only_mode(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_RXONLY;
+}
+
+void spi_disable_software_slave_management(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_SSM;
+}
+
+void spi_enable_software_slave_management(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_SSM;
+}
+
+void spi_set_nss_high(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_SSI;
+}
+
+void spi_set_nss_low(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_SSI;
+}
+
+void spi_send_lsb_first(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_LSBFIRST;
+}
+
+void spi_send_msb_first(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_LSBFIRST;
+}
+
+void spi_set_baudrate_prescaler(u32 spi, u8 baudrate)
+{
+ u32 reg32;
+
+ if (baudrate > 7)
+ return;
+
+ reg32 = ( SPI_CR1(spi) & 0xffc7 ); /* clear bits [5:3] */
+ reg32 |= (baudrate << 3);
+ SPI_CR1(spi) = reg32;
+}
+
+void spi_set_master_mode(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_MSTR;
+}
+
+void spi_set_slave_mode(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_MSTR;
+}
+
+void spi_set_clock_polarity_1(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_CPOL;
+}
+
+void spi_set_clock_polarity_0(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_CPOL;
+}
+
+void spi_set_clock_phase_1(u32 spi)
+{
+ SPI_CR1(spi) |= SPI_CR1_CPHA;
+}
+
+void spi_set_clock_phase_0(u32 spi)
+{
+ SPI_CR1(spi) &= ~SPI_CR1_CPHA;
+}
+
+void spi_enable_tx_buffer_empty_interrupt(u32 spi)
+{
+ SPI_CR2(spi) |= SPI_CR2_TXEIE;
+}
+
+void spi_disable_tx_buffer_empty_interrupt(u32 spi)
+{
+ SPI_CR2(spi) &= ~SPI_CR2_TXEIE;
+}
+
+void spi_enable_rx_buffer_not_empty_interrupt(u32 spi)
+{
+ SPI_CR2(spi) |= SPI_CR2_RXNEIE;
+}
+
+void spi_disable_rx_buffer_not_empty_interrupt(u32 spi)
+{
+ SPI_CR2(spi) &= ~SPI_CR2_RXNEIE;
+}
+
+void spi_enable_error_interrupt(u32 spi)
+{
+ SPI_CR2(spi) |= SPI_CR2_ERRIE;
+}
+
+void spi_disable_error_interrupt(u32 spi)
+{
+ SPI_CR2(spi) &= ~SPI_CR2_ERRIE;
+}
+
+void spi_enable_ss_output(u32 spi)
+{
+ SPI_CR2(spi) |= SPI_CR2_SSOE;
+}
+
+void spi_disable_ss_output(u32 spi)
+{
+ SPI_CR2(spi) &= ~SPI_CR2_SSOE;
+}
+
+void spi_enable_tx_dma(u32 spi)
+{
+ SPI_CR2(spi) |= SPI_CR2_TXDMAEN;
+}
+
+void spi_disable_tx_dma(u32 spi)
+{
+ SPI_CR2(spi) &= ~SPI_CR2_TXDMAEN;
+}
+
+void spi_enable_rx_dma(u32 spi)
+{
+ SPI_CR2(spi) |= SPI_CR2_RXDMAEN;
+}
+
+void spi_disable_rx_dma(u32 spi)
+{
+ SPI_CR2(spi) &= ~SPI_CR2_RXDMAEN;
+}
diff --git a/lib/stm32/systick.c b/lib/stm32/systick.c
new file mode 100644
index 0000000..fad2253
--- /dev/null
+++ b/lib/stm32/systick.c
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Thomas Otto <tommi@viadmin.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/systick.h>
+
+void systick_set_reload(u32 value)
+{
+ STK_LOAD = (value & 0x00FFFFFF);
+}
+
+u32 systick_get_value(void)
+{
+ return STK_VAL;
+}
+
+void systick_set_clocksource(u8 clocksource)
+{
+ if (clocksource < 2)
+ STK_CTRL |= (clocksource << STK_CTRL_CLKSOURCE_LSB);
+}
+
+void systick_interrupt_enable(void)
+{
+ STK_CTRL |= STK_CTRL_TICKINT;
+}
+
+void systick_interrupt_disable(void)
+{
+ STK_CTRL &= ~STK_CTRL_TICKINT;
+}
+
+void systick_counter_enable(void)
+{
+ STK_CTRL |= STK_CTRL_ENABLE;
+}
+
+void systick_counter_disable(void)
+{
+ STK_CTRL &= ~STK_CTRL_ENABLE;
+}
+
+u8 systick_get_countflag(void)
+{
+ if (STK_CTRL & STK_CTRL_COUNTFLAG)
+ return 1;
+ else
+ return 0;
+}
diff --git a/lib/stm32/timer.c b/lib/stm32/timer.c
new file mode 100644
index 0000000..e0044f7
--- /dev/null
+++ b/lib/stm32/timer.c
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2010 Edward Cheeseman <evbuilder@users.sourceforge.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Basic TIMER handling API.
+ *
+ * Examples:
+ * timer_set_mode(TIM1, TIM_CR1_CKD_CK_INT_MUL_2,
+ * TIM_CR1_CMS_CENTRE_3, TIM_CR1_DIR_UP);
+ */
+
+#include <libopenstm32/timer.h>
+
+void timer_set_mode(u32 timer_peripheral, u8 clock_div, u8 alignment,
+ u8 direction)
+{
+ /* Bad, will reset lots of other stuff. */
+ // TIM_CR1(timer_peripheral) = clock_div | alignment | direction;
+}
+
+void timer_set_clock_division(u32 timer_peripheral, u32 clock_div)
+{
+ clock_div &= TIM_CR1_CKD_CK_INT_MASK;
+ TIM_CR1(timer_peripheral) &= !TIM_CR1_CKD_CK_INT_MASK;
+ TIM_CR1(timer_peripheral) |= clock_div;
+}
+
+void timer_enable_preload(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) |= TIM_CR1_ARPE;
+}
+
+void timer_disable_preload(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) &= !TIM_CR1_ARPE;
+}
+
+void timer_set_alignment(u32 timer_peripheral, u32 alignment)
+{
+ alignment &= TIM_CR1_CMS_MASK;
+ TIM_CR1(timer_peripheral) &= ~TIM_CR1_CMS_MASK;
+ TIM_CR1(timer_peripheral) |= alignment;
+}
+
+void timer_direction_up(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) &= ~TIM_CR1_DIR_DOWN;
+}
+
+void timer_direction_down(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) |= TIM_CR1_DIR_DOWN;
+}
+
+void timer_one_shot_mode(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) |= TIM_CR1_OPM;
+}
+
+void timer_continuous_mode(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) &= ~TIM_CR1_OPM;
+}
+
+void timer_update_on_any(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) &= ~TIM_CR1_URS;
+}
+
+void timer_update_on_overflow(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) |= TIM_CR1_URS;
+}
+
+void timer_enable_update_event(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) &= ~TIM_CR1_UDIS;
+}
+
+void timer_disable_update_event(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) |= TIM_CR1_UDIS;
+}
+
+void timer_enable_counter(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) |= TIM_CR1_CEN;
+}
+
+void timer_disable_counter(u32 timer_peripheral)
+{
+ TIM_CR1(timer_peripheral) &= ~TIM_CR1_CEN;
+}
+
+void timer_set_output_idle_state(u32 timer_peripheral, u32 outputs)
+{
+ TIM_CR2(timer_peripheral) |= outputs & TIM_CR2_OIS_MASK;
+}
+
+void timer_reset_output_idle_state(u32 timer_peripheral, u32 outputs)
+{
+ TIM_CR2(timer_peripheral) &= ~(outputs & TIM_CR2_OIS_MASK);
+}
+
+void timer_set_ti1_ch123_xor(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) |= TIM_CR2_TI1S;
+}
+
+void timer_set_ti1_ch1(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) &= ~TIM_CR2_TI1S;
+}
+
+void timer_set_master_mode(u32 timer_peripheral, u32 mode)
+{
+ mode &= mode & TIM_CR2_MASK;
+ TIM_CR2(timer_peripheral) &= ~TIM_CR2_MMS_MASK;
+ TIM_CR2(timer_peripheral) |= mode;
+}
+
+void timer_set_dma_on_compare_event(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) &= ~TIM_CR2_CCDS;
+}
+
+void timer_set_dma_on_update_event(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) |= TIM_CR2_CCDS;
+}
+
+void timer_enable_compare_control_update_on_trigger(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) |= TIM_CR2_CCUS;
+}
+
+void timer_disable_compare_control_update_on_trigger(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) &= ~TIM_CR2_CCUS;
+}
+
+void timer_enable_preload_complementry_enable_bits(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) |= TIM_CR2_CCPC;
+}
+
+void timer_disable_preload_complementry_enable_bits(u32 timer_peripheral)
+{
+ TIM_CR2(timer_peripheral) &= ~TIM_CR2_CCPC;
+}
diff --git a/lib/stm32/usart.c b/lib/stm32/usart.c
new file mode 100644
index 0000000..6f3905f
--- /dev/null
+++ b/lib/stm32/usart.c
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the libopenstm32 project.
+ *
+ * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libopenstm32/usart.h>
+
+void usart_set_baudrate(u32 usart, u32 baud)
+{
+ u32 clock = 72000000; /* FIXME: Don't hardcode this clock! */
+
+ /* TODO: Document and explain calculation. */
+ USART_BRR(usart) = (u16)((clock << 4) / (baud * 16));
+}
+
+void usart_set_databits(u32 usart, u32 bits)
+{
+ if (bits == 8)
+ USART_CR1(usart) &= ~USART_CR1_M; /* 8 data bits */
+ else
+ USART_CR1(usart) |= USART_CR1_M; /* 9 data bits */
+}
+
+void usart_set_stopbits(u32 usart, u32 stopbits)
+{
+ u32 reg32;
+
+ reg32 = USART_CR2(usart);
+ reg32 = (reg32 & ~USART_CR2_STOPBITS_MASK) | stopbits;
+ USART_CR2(usart) = reg32;
+}
+
+void usart_set_parity(u32 usart, u32 parity)
+{
+ u32 reg32;
+
+ reg32 = USART_CR1(usart);
+ reg32 = (reg32 & ~USART_PARITY_MASK) | parity;
+ USART_CR1(usart) = reg32;
+}
+
+void usart_set_mode(u32 usart, u32 mode)
+{
+ u32 reg32;
+
+ reg32 = USART_CR1(usart);
+ reg32 = (reg32 & ~USART_MODE_MASK) | mode;
+ USART_CR1(usart) = reg32;
+}
+
+void usart_set_flow_control(u32 usart, u32 flowcontrol)
+{
+ u32 reg32;
+
+ reg32 = USART_CR3(usart);
+ reg32 = (reg32 & ~USART_FLOWCONTROL_MASK) | flowcontrol;
+ USART_CR3(usart) = reg32;
+}
+
+void usart_enable(u32 usart)
+{
+ USART_CR1(usart) |= USART_CR1_UE;
+}
+
+void usart_disable(u32 usart)
+{
+ USART_CR1(usart) &= ~USART_CR1_UE;
+}
+
+void usart_send(u32 usart, u16 data)
+{
+ /* Send data. */
+ USART_DR(usart) = (data & USART_DR_MASK);
+}
+
+u16 usart_recv(u32 usart)
+{
+ /* Receive data. */
+ return USART_DR(usart) & USART_DR_MASK;
+}
+
+void usart_wait_send_ready(u32 usart)
+{
+ /* Wait until the data has been transferred into the shift register. */
+ while ((USART_SR(usart) & USART_SR_TXE) == 0);
+}
+
+void usart_wait_recv_ready(u32 usart)
+{
+ /* Wait until the data is ready to be received. */
+ while ((USART_SR(usart) & USART_SR_RXNE) == 0);
+}
+
+void usart_send_blocking(u32 usart, u16 data)
+{
+ usart_send(usart, data);
+
+ usart_wait_send_ready(usart);
+}
+
+u16 usart_recv_blocking(u32 usart)
+{
+ usart_wait_recv_ready(usart);
+
+ return usart_recv(usart);
+}