aboutsummaryrefslogtreecommitdiff
path: root/lib/microwire_master.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/microwire_master.c')
-rw-r--r--lib/microwire_master.c315
1 files changed, 0 insertions, 315 deletions
diff --git a/lib/microwire_master.c b/lib/microwire_master.c
deleted file mode 100644
index bff991d..0000000
--- a/lib/microwire_master.c
+++ /dev/null
@@ -1,315 +0,0 @@
-/* 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/>.
- *
- */
-/** library to communicate using microwore as master (code)
- * @file microwire_master.c
- * @author King Kévin <kingkevin@cuvoodoo.info>
- * @date 2017
- * @note peripherals used: GPIO @ref microwire_master_gpio, timer @ref microwire_master_timer
- * microwire is a 3-Wire half-duplex synchronous bus. It is very similar to SPI without fixed length messages (bit-wised).
- * @note the user has to handle the slave select pin (high during operations) so to be able to handle multiple slaves.
- * @warning this library implements the M93Cx8 EEPROM operation codes. Other microwire-based ICs might use different ones.
- */
-
-/* standard libraries */
-#include <stdint.h> // standard integer types
-#include <stdlib.h> // general utilities
-
-/* STM32 (including CM3) libraries */
-#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
-#include <libopencm3/stm32/rcc.h> // real-time control clock library
-#include <libopencm3/stm32/gpio.h> // general purpose input output library
-#include <libopencm3/stm32/timer.h> // timer utilities
-
-#include "global.h" // global utilities
-#include "microwire_master.h" // microwire header and definitions
-
-/** @defgroup microwire_master_gpio GPIO peripheral used to communicate
- * @{
- */
-#define MICROWIRE_MASTER_SDO_PORT A /**< SDO output signal port (to be connected on D slave signal) */
-#define MICROWIRE_MASTER_SDO_PIN 0 /**< SDO output signal pin (to be connected on D slave signal) */
-#define MICROWIRE_MASTER_SDI_PORT A /**< SDO input signal port (to be connected on Q slave signal) */
-#define MICROWIRE_MASTER_SDI_PIN 2 /**< SDO input signal pin (to be connected on Q slave signal) */
-#define MICROWIRE_MASTER_SCK_PORT A /**< SCK output signal port (to be connected on C slave signal) */
-#define MICROWIRE_MASTER_SCK_PIN 4 /**< SCK output signal pin (to be connected on C slave signal) */
-/** @} */
-
-/** @defgroup microwire_master_timer timer peripheral used to generate timing for the signal
- * @{
- */
-#define MICROWIRE_MASTER_TIMER 4 /**< timer peripheral */
-/** @} */
-
-/** address size used in operations (slave specific) */
-uint8_t mirowire_master_address_size = 0;
-/** organization used (true=x16, false=x8) */
-bool mirowire_master_organization_x16 = true;
-
-void microwire_master_setup(uint32_t frequency, bool organization_x16, uint8_t address_size)
-{
- // sanity checks
- if (0==frequency || 0==address_size) {
- return;
- }
- mirowire_master_address_size = address_size; // save address size
- mirowire_master_organization_x16 = organization_x16; // save organisation
-
- // setup GPIO
- rcc_periph_clock_enable(RCC_GPIO(MICROWIRE_MASTER_SDO_PORT)); // enable clock for GPIO domain for SDO signal
- gpio_set_mode(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MICROWIRE_MASTER_SDO_PIN)); // set SDO signal as output (controlled by the master)
- gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // SDO is idle low
- rcc_periph_clock_enable(RCC_GPIO(MICROWIRE_MASTER_SDI_PORT)); // enable clock for GPIO domain for SDI signal
- gpio_set_mode(GPIO(MICROWIRE_MASTER_SDI_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(MICROWIRE_MASTER_SDI_PIN)); // set SDI signal as output (controlled by the slave)
- rcc_periph_clock_enable(RCC_GPIO(MICROWIRE_MASTER_SCK_PORT)); // enable clock for GPIO domain for SCK signal
- gpio_set_mode(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MICROWIRE_MASTER_SCK_PIN)); // set SCK signal as output (controlled by the master)
- gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // SCK is idle low
-
- // setup timer to generate timing for the signal
- rcc_periph_clock_enable(RCC_TIM(MICROWIRE_MASTER_TIMER)); // enable clock for timer domain
- timer_reset(TIM(MICROWIRE_MASTER_TIMER)); // reset timer state
- timer_set_mode(TIM(MICROWIRE_MASTER_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
- uint16_t prescaler = rcc_ahb_frequency/(frequency*2)/(uint32_t)(1<<16)+1; // calculate prescaler for most accurate timing for this speed
- timer_set_prescaler(TIM(MICROWIRE_MASTER_TIMER), prescaler-1); // set calculated prescaler
- uint16_t period = (rcc_ahb_frequency/prescaler)/(frequency*2); // calculate period to get most accurate timing based on the calculated prescaler
- timer_set_period(TIM(MICROWIRE_MASTER_TIMER), period-1); // set calculated period
- timer_update_on_overflow(TIM(MICROWIRE_MASTER_TIMER)); // only use counter overflow as UEV source (use overflow as timeout)
- SCB_SCR |= SCB_SCR_SEVEONPEND; // enable wake up on event (instead of using ISR)
- timer_enable_irq(TIM(MICROWIRE_MASTER_TIMER), TIM_DIER_UIE); // enable update interrupt for timer
-}
-
-/** wait for clock tick used to synchronise communication */
-static void microwire_master_wait_clock(void)
-{
- while ( !timer_get_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF)) { // wait for timer overflow event for clock change
- __asm__("wfe"); // go to sleep and wait for event
- }
- timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag
- nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag (else event doesn't wake up)
-
-}
-
-/** send bit over microwire
- * @param[in] bit bit to send (true = '1', false = '0')
- */
-static void microwire_master_send_bit(bool bit)
-{
- if (bit) {
- gpio_set(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // set '1' on output
- } else {
- gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // set '0' on output
- }
- microwire_master_wait_clock(); // wait for clock timing
- gpio_set(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // make rising edge for slave to sample
- microwire_master_wait_clock(); // keep output signal stable while clock is high
- gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // put clock back to idle
-}
-
-/** initialize microwire communication and send header (with leading start bit '1')
- * @param[in] operation operation code to send (2 bits)
- * @param[in] address slave memory address to select
- */
-static void microwire_master_start(uint8_t operation, uint32_t address)
-{
- // to sanity checks
- if (0==mirowire_master_address_size) { // can't send address
- return;
- }
-
- // initial setup
- gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // ensure clock is low (to sample on rising edge)
- timer_set_counter(TIM(MICROWIRE_MASTER_TIMER),0); // reset timer counter
- timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag
- nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag (else event doesn't wake up)
- timer_enable_counter(TIM(MICROWIRE_MASTER_TIMER)); // start timer to generate timing
-
- // send '1' start bit
- microwire_master_send_bit(true); // send start bit
- // send two bits operation code
- if (operation&0x2) { // send first bit (MSb first)
- microwire_master_send_bit(true); // send '1'
- } else {
- microwire_master_send_bit(false); // send '2'
- }
- if (operation&0x1) { // send second bit (LSb last)
- microwire_master_send_bit(true); // send '1'
- } else {
- microwire_master_send_bit(false); // send '2'
- }
-
- // send address
- for (uint8_t bit = mirowire_master_address_size; bit > 0; bit--) {
- if ((address>>(bit-1))&0x01) {
- microwire_master_send_bit(true); // send '1' address bit
- } else {
- microwire_master_send_bit(false); // send '0' address bit
- }
- }
- gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // ensure output is idle low (could be floating)
-}
-
-/** stop microwire communication and end all activities */
-static void microwire_master_stop(void)
-{
- timer_disable_counter(TIM(MICROWIRE_MASTER_TIMER)); // disable timer
- timer_set_counter(TIM(MICROWIRE_MASTER_TIMER),0); // reset timer counter
- timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag
- nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag
- gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // ensure clock is idle low
- gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // ensure output is idle low
-}
-
-
-/** read bit from microwire communication
- * @return bit value (true = '1', false = '0')
- */
-static bool microwire_master_read_bit(void)
-{
- microwire_master_wait_clock(); // wait for clock timing
- gpio_set(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // make rising edge for slave to output data
- microwire_master_wait_clock(); // wait for signal to be stable
- gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // set clock low again
- return 0!=gpio_get(GPIO(MICROWIRE_MASTER_SDI_PORT), GPIO(MICROWIRE_MASTER_SDI_PIN)); // read input signal
-}
-
-void microwire_master_read(uint32_t address, uint16_t* data, size_t length)
-{
- // to sanity checks
- if (NULL==data || 0==length || 0==mirowire_master_address_size) { // can't save data
- return;
- }
-
- microwire_master_start(0x02, address); // send '10' READ instruction and memory address
-
- // there should already be a '0' dummy bit
- if (0!=gpio_get(GPIO(MICROWIRE_MASTER_SDI_PORT), GPIO(MICROWIRE_MASTER_SDI_PIN))) { // the dummy bit wasn't '0'
- goto clean;
- }
-
- // read data
- for (size_t i=0; i<length; i++) {
- for (uint8_t b=(mirowire_master_organization_x16 ? 16 : 8); b>0; b--) {
- if (microwire_master_read_bit()) { // read bit, MSb first
- data[i] |= (1<<(b-1)); // set bit
- } else {
- data[i] &= ~(1<<(b-1)); // clear bit
- }
- }
- }
-
-clean:
- microwire_master_stop(); // stop communication and clean up
-}
-
-void microwire_master_write_enable(void)
-{
- // to sanity checks
- if (mirowire_master_address_size<2) { // can't send '11...' address
- return;
- }
-
- microwire_master_start(0x0, 0x3<<(mirowire_master_address_size-2)); // send '00' WEN operation code and '11...' address
- microwire_master_stop(); // clean up
-}
-
-void microwire_master_write_disable(void)
-{
- // to sanity checks
- if (mirowire_master_address_size<2) { // can't send '00...' address
- return;
- }
-
- microwire_master_start(0x0, 0); // send '00' WDS operation code and '00...' address
- microwire_master_stop(); // clean up
-}
-
-void microwire_master_write(uint32_t address, uint16_t data)
-{
- // to sanity checks
- if (0==mirowire_master_address_size) { // can't send address
- return;
- }
-
- microwire_master_start(0x01, address); // send '01' WRITE operation code and memory address
-
- // write data (MSb first)
- for (uint8_t b=(mirowire_master_organization_x16 ? 16 : 8); b>0; b--) {
- if (data&(1<<(b-1))) { // bit is set
- microwire_master_send_bit(true); // send '1' data bit
- } else {
- microwire_master_send_bit(false); // send '0' data bit
- }
- }
-
- microwire_master_stop(); // clean up
-}
-
-void microwire_master_wait_ready(void)
-{
-
- // initial setup
- gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // ensure clock is low (to sample on rising edge)
- timer_set_counter(TIM(MICROWIRE_MASTER_TIMER),0); // reset timer counter
- timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag
- nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag (else event doesn't wake up)
- timer_enable_counter(TIM(MICROWIRE_MASTER_TIMER)); // start timer to generate timing
-
-
- // SDI low on busy, high on ready, clock is ignored
- while (!microwire_master_read_bit()); // wait until slave is ready
-
- microwire_master_stop(); // clean up
-}
-
-void microwire_master_erase(uint32_t address)
-{
- // sanity checks
- if (0==mirowire_master_address_size) { // can't send address
- return;
- }
-
- microwire_master_start(0x03, address); // send '11' ERASE operation code and memory address
- microwire_master_stop(); // clean up
-}
-
-void microwire_master_erase_all(void)
-{
- // sanity checks
- if (mirowire_master_address_size<2) { // can't send '11...' address
- return;
- }
-
- microwire_master_start(0x00, 0x2<<(mirowire_master_address_size-2)); // send '00' ERAL operation code and '10...' address
- microwire_master_stop(); // clean up
-}
-
-void microwire_master_write_all(uint16_t data)
-{
- // sanity checks
- if (0==mirowire_master_address_size) { // can't send address
- return;
- }
-
- microwire_master_start(0x00, 0x1<<(mirowire_master_address_size-2)); // send '00' WRAL operation code and '01...' address
- // write data (MSb first)
- for (uint8_t b=(mirowire_master_organization_x16 ? 16 : 8); b>0; b--) {
- if (data&(1<<(b-1))) { // bit is set
- microwire_master_send_bit(true); // send '1' data bit
- } else {
- microwire_master_send_bit(false); // send '0' data bit
- }
- }
-
- microwire_master_stop(); // clean up
-}