2020-02-17 14:04:41 +01:00
/** library to communicate using microwore as master
* @ file
2017-04-15 13:59:01 +02:00
* @ author King Kévin < kingkevin @ cuvoodoo . info >
2020-06-06 14:35:55 +02:00
* @ copyright SPDX - License - Identifier : GPL - 3.0 - or - later
2020-02-17 14:04:41 +01:00
* @ date 2017 - 2020
2017-04-15 13:59:01 +02:00
* @ 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
* @ {
*/
2020-02-17 14:04:41 +01:00
# define MICROWIRE_MASTER_SDO_PIN PA0 /**< SDO output signal pin (to be connected on D slave signal) */
# define MICROWIRE_MASTER_SDI_PIN PA2 /**< SDO input signal pin (to be connected on Q slave signal) */
# define MICROWIRE_MASTER_SCK_PIN PA4 /**< SCK output signal pin (to be connected on C slave signal) */
2017-04-15 13:59:01 +02:00
/** @} */
/** @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
2020-02-17 14:06:15 +01:00
if ( 0 = = frequency | | 0 = = address_size ) {
2017-04-15 13:59:01 +02:00
return ;
}
mirowire_master_address_size = address_size ; // save address size
mirowire_master_organization_x16 = organization_x16 ; // save organisation
// setup GPIO
2020-02-17 14:04:41 +01:00
rcc_periph_clock_enable ( GPIO_RCC ( MICROWIRE_MASTER_SDO_PIN ) ) ; // enable clock for GPIO domain for SDO signal
gpio_set_mode ( GPIO_PORT ( MICROWIRE_MASTER_SDO_PIN ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( MICROWIRE_MASTER_SDO_PIN ) ) ; // set SDO signal as output (controlled by the master)
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SDO_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDO_PIN ) ) ; // SDO is idle low
rcc_periph_clock_enable ( GPIO_RCC ( MICROWIRE_MASTER_SDI_PIN ) ) ; // enable clock for GPIO domain for SDI signal
gpio_set_mode ( GPIO_PORT ( MICROWIRE_MASTER_SDI_PIN ) , GPIO_MODE_INPUT , GPIO_CNF_INPUT_FLOAT , GPIO_PIN ( MICROWIRE_MASTER_SDI_PIN ) ) ; // set SDI signal as output (controlled by the slave)
rcc_periph_clock_enable ( GPIO_RCC ( MICROWIRE_MASTER_SCK_PIN ) ) ; // enable clock for GPIO domain for SCK signal
gpio_set_mode ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_MODE_OUTPUT_10_MHZ , GPIO_CNF_OUTPUT_PUSHPULL , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // set SCK signal as output (controlled by the master)
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // SCK is idle low
2017-04-15 13:59:01 +02:00
// setup timer to generate timing for the signal
rcc_periph_clock_enable ( RCC_TIM ( MICROWIRE_MASTER_TIMER ) ) ; // enable clock for timer domain
2020-02-17 13:59:49 +01:00
rcc_periph_reset_pulse ( RST_TIM ( MICROWIRE_MASTER_TIMER ) ) ; // reset timer state
2017-04-15 13:59:01 +02:00
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
2020-02-17 14:06:15 +01:00
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
2017-04-15 13:59:01 +02:00
timer_update_on_overflow ( TIM ( MICROWIRE_MASTER_TIMER ) ) ; // only use counter overflow as UEV source (use overflow as timeout)
2020-02-17 14:05:34 +01:00
SCB_SCR | = SCB_SCR_SEVONPEND ; // enable wake up on event (instead of using ISR)
2017-04-15 13:59:01 +02:00
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 ) {
2020-02-17 14:04:41 +01:00
gpio_set ( GPIO_PORT ( MICROWIRE_MASTER_SDO_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDO_PIN ) ) ; // set '1' on output
2017-04-15 13:59:01 +02:00
} else {
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SDO_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDO_PIN ) ) ; // set '0' on output
2017-04-15 13:59:01 +02:00
}
microwire_master_wait_clock ( ) ; // wait for clock timing
2020-02-17 14:04:41 +01:00
gpio_set ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // make rising edge for slave to sample
2017-04-15 13:59:01 +02:00
microwire_master_wait_clock ( ) ; // keep output signal stable while clock is high
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // put clock back to idle
2017-04-15 13:59:01 +02:00
}
/** 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
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // ensure clock is low (to sample on rising edge)
timer_set_counter ( TIM ( MICROWIRE_MASTER_TIMER ) , 0 ) ; // reset timer counter
2017-04-15 13:59:01 +02:00
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
2020-02-17 14:06:15 +01:00
if ( operation & 0x2 ) { // send first bit (MSb first)
2017-04-15 13:59:01 +02:00
microwire_master_send_bit ( true ) ; // send '1'
} else {
microwire_master_send_bit ( false ) ; // send '2'
}
2020-02-17 14:06:15 +01:00
if ( operation & 0x1 ) { // send second bit (LSb last)
2017-04-15 13:59:01 +02:00
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 - - ) {
2020-02-17 14:06:15 +01:00
if ( ( address > > ( bit - 1 ) ) & 0x01 ) {
2017-04-15 13:59:01 +02:00
microwire_master_send_bit ( true ) ; // send '1' address bit
} else {
microwire_master_send_bit ( false ) ; // send '0' address bit
}
}
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SDO_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDO_PIN ) ) ; // ensure output is idle low (could be floating)
2017-04-15 13:59:01 +02:00
}
/** stop microwire communication and end all activities */
static void microwire_master_stop ( void )
{
timer_disable_counter ( TIM ( MICROWIRE_MASTER_TIMER ) ) ; // disable timer
2020-02-17 14:06:15 +01:00
timer_set_counter ( TIM ( MICROWIRE_MASTER_TIMER ) , 0 ) ; // reset timer counter
2017-04-15 13:59:01 +02:00
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
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // ensure clock is idle low
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SDO_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDO_PIN ) ) ; // ensure output is idle low
2017-04-15 13:59:01 +02:00
}
/** 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
2020-02-17 14:04:41 +01:00
gpio_set ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // make rising edge for slave to output data
2017-04-15 13:59:01 +02:00
microwire_master_wait_clock ( ) ; // wait for signal to be stable
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // set clock low again
return 0 ! = gpio_get ( GPIO_PORT ( MICROWIRE_MASTER_SDI_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDI_PIN ) ) ; // read input signal
2017-04-15 13:59:01 +02:00
}
void microwire_master_read ( uint32_t address , uint16_t * data , size_t length )
{
// to sanity checks
2020-02-17 14:06:15 +01:00
if ( NULL = = data | | 0 = = length | | 0 = = mirowire_master_address_size ) { // can't save data
2017-04-15 13:59:01 +02:00
return ;
}
microwire_master_start ( 0x02 , address ) ; // send '10' READ instruction and memory address
// there should already be a '0' dummy bit
2020-02-17 14:04:41 +01:00
if ( 0 ! = gpio_get ( GPIO_PORT ( MICROWIRE_MASTER_SDI_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SDI_PIN ) ) ) { // the dummy bit wasn't '0'
2019-03-26 19:27:40 +01:00
goto clean ;
2017-04-15 13:59:01 +02:00
}
// read data
2020-02-17 14:06:15 +01:00
for ( size_t i = 0 ; i < length ; i + + ) {
for ( uint8_t b = ( mirowire_master_organization_x16 ? 16 : 8 ) ; b > 0 ; b - - ) {
2017-04-15 13:59:01 +02:00
if ( microwire_master_read_bit ( ) ) { // read bit, MSb first
2020-02-17 14:06:15 +01:00
data [ i ] | = ( 1 < < ( b - 1 ) ) ; // set bit
2017-04-15 13:59:01 +02:00
} else {
2020-02-17 14:06:15 +01:00
data [ i ] & = ~ ( 1 < < ( b - 1 ) ) ; // clear bit
2017-04-15 13:59:01 +02:00
}
}
}
2019-03-26 19:27:40 +01:00
2017-04-15 13:59:01 +02:00
clean :
microwire_master_stop ( ) ; // stop communication and clean up
}
void microwire_master_write_enable ( void )
{
// to sanity checks
2020-02-17 14:06:15 +01:00
if ( mirowire_master_address_size < 2 ) { // can't send '11...' address
2017-04-15 13:59:01 +02:00
return ;
}
2020-02-17 14:06:15 +01:00
microwire_master_start ( 0x0 , 0x3 < < ( mirowire_master_address_size - 2 ) ) ; // send '00' WEN operation code and '11...' address
2017-04-15 13:59:01 +02:00
microwire_master_stop ( ) ; // clean up
}
void microwire_master_write_disable ( void )
{
// to sanity checks
2020-02-17 14:06:15 +01:00
if ( mirowire_master_address_size < 2 ) { // can't send '00...' address
2017-04-15 13:59:01 +02:00
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
}
}
2019-03-26 19:27:40 +01:00
2017-04-15 13:59:01 +02:00
microwire_master_stop ( ) ; // clean up
}
void microwire_master_wait_ready ( void )
{
// initial setup
2020-02-17 14:04:41 +01:00
gpio_clear ( GPIO_PORT ( MICROWIRE_MASTER_SCK_PIN ) , GPIO_PIN ( MICROWIRE_MASTER_SCK_PIN ) ) ; // ensure clock is low (to sample on rising edge)
timer_set_counter ( TIM ( MICROWIRE_MASTER_TIMER ) , 0 ) ; // reset timer counter
2017-04-15 13:59:01 +02:00
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
2020-02-17 14:06:15 +01:00
if ( mirowire_master_address_size < 2 ) { // can't send '11...' address
2017-04-15 13:59:01 +02:00
return ;
}
2020-02-17 14:06:15 +01:00
microwire_master_start ( 0x00 , 0x2 < < ( mirowire_master_address_size - 2 ) ) ; // send '00' ERAL operation code and '10...' address
2017-04-15 13:59:01 +02:00
microwire_master_stop ( ) ; // clean up
}
void microwire_master_write_all ( uint16_t data )
{
// sanity checks
2020-02-17 14:06:15 +01:00
if ( 0 = = mirowire_master_address_size ) { // can't send address
2017-04-15 13:59:01 +02:00
return ;
}
2020-02-17 14:06:15 +01:00
microwire_master_start ( 0x00 , 0x1 < < ( mirowire_master_address_size - 2 ) ) ; // send '00' WRAL operation code and '01...' address
2017-04-15 13:59:01 +02:00
// write data (MSb first)
2020-02-17 14:06:15 +01:00
for ( uint8_t b = ( mirowire_master_organization_x16 ? 16 : 8 ) ; b > 0 ; b - - ) {
if ( data & ( 1 < < ( b - 1 ) ) ) { // bit is set
2017-04-15 13:59:01 +02:00
microwire_master_send_bit ( true ) ; // send '1' data bit
} else {
microwire_master_send_bit ( false ) ; // send '0' data bit
}
}
2019-03-26 19:27:40 +01:00
2017-04-15 13:59:01 +02:00
microwire_master_stop ( ) ; // clean up
}