Original Post
I don't know how many of you are familiar with driver code, but I'll leave this little treat here.
#include "hal/mux.h"
#include <msp430.h>
/*
* The following macros define the bits that would enable the connection from
* the MSP430 USS output Tx or input Rx to the sensor.
*
* C\d[AB]\d[RT]
* | | | |
* Channel idx --+ | | |
* Group A or B ---+ | |
* Subsensor index ----+ |
* Transmit or receive --+
*/
/* Vertical channel 0, mapped to port 4 */
#define C0A0T (1u << 0)
#define C0A0R (1u << 1)
#define C0B0T (1u << 2)
#define C0B0R (1u << 5)
/* Sensor group A, channel 1, mapped to port C (ports 5 and 6) */
#define C1A0T (1u << 15)
#define C1A0R (1u << 12)
#define C1A1T (1u << 3)
#define C1A1R (1u << 5)
#define C1A2T (1u << 9)
#define C1A2R (1u << 13)
#define C1A3T (1u << 1)
#define C1A3R (1u << 7)
/* Sensor group B, channel 1, mapped to port C (ports 5 and 6) */
#define C1B0T (1u << 2)
#define C1B0R (1u << 4)
#define C1B1T (1u << 10)
#define C1B1R (1u << 8)
#define C1B2T (1u << 0)
#define C1B2R (1u << 6)
#define C1B3T (1u << 11)
#define C1B3R (1u << 14)
/*
* Maps the entries in the mux_mode_e enum as offsets so table lookup is O(1)
* c - Channel index
* s - Subsensor index
*/
#define CHANNEL_MAPPING(c, s) \
C##c##A##s##T | C##c##B##s##R, /* MUX_UP | MUX_TRANS = 0x00 */ \
C##c##B##s##T | C##c##A##s##R, /* MUX_DOWN | MUX_TRANS = 0x01 */ \
C##c##B##s##T | C##c##B##s##R, /* MUX_UP | MUX_RECV = 0x02 */ \
C##c##A##s##T | C##c##A##s##R /* MUX_DOWN | MUX_RECV = 0x03 */
#define CHANNEL_T_MODES(c, s) \
C##c##A##s##T, /* MUX_UP | MUX_TRANS = 0x00 */ \
C##c##B##s##T, /* MUX_DOWN | MUX_TRANS = 0x01 */ \
C##c##B##s##T, /* MUX_UP | MUX_RECV = 0x02 */ \
C##c##A##s##T /* MUX_DOWN | MUX_RECV = 0x03 */
#define CHANNEL_R_MODES(c, s) \
C##c##B##s##R, /* MUX_UP | MUX_TRANS = 0x00 */ \
C##c##A##s##R, /* MUX_DOWN | MUX_TRANS = 0x01 */ \
C##c##B##s##R, /* MUX_UP | MUX_RECV = 0x02 */ \
C##c##A##s##R /* MUX_DOWN | MUX_RECV = 0x03 */
static const uint16_t map_to_port_4[4] = {
CHANNEL_MAPPING(0, 0)
};
static const uint16_t map_T_to_port_c[16] = {
CHANNEL_T_MODES(1, 0),
CHANNEL_T_MODES(1, 1),
CHANNEL_T_MODES(1, 2),
CHANNEL_T_MODES(1, 3)
};
static const uint16_t map_R_to_port_c[16] = {
CHANNEL_R_MODES(1, 0),
CHANNEL_R_MODES(1, 1),
CHANNEL_R_MODES(1, 2),
CHANNEL_R_MODES(1, 3)
};
/*
* Multiplexer state can be stored in a uint16_t, such that the lower
* byte directly indexes an array offset for the transmitter state and the
* upper byte directly indexes an array offset for the receiver state.
*
* Usage:
* state.data --> Gives you the mux state as a uint16_t
* state.group_a.[mode|subchannel_idx] --> mode and subchannel index access for group A
* state.group_b.[mode|subchannel_idx] --> mode and subchannel index access for group B
* state.group_a.offset --> Array offset for map_T_to_port_c[]
* state.group_b.offset --> Array offset for map_R_to_port_c[]
* state.channel_idx --> Access to channel index
*
* See mux.h for a detailed overview of what is meant by "Group A" and "Group B",
* what subsensors are, and what the different mode bits are supposed to do.
*/
union mux_state_u
{
struct {
union {
struct {
union {
struct {
unsigned mode : 2;
unsigned subchannel_idx : 2;
unsigned pad_1 : 4;
};
struct {
unsigned offset : 4;
unsigned pad_2 : 4;
};
} group_a;
union {
struct {
unsigned mode : 2;
unsigned subchannel_idx : 2;
unsigned pad_1 : 4;
};
struct {
unsigned offset : 4;
unsigned pad_2 : 4;
};
} group_b;
};
struct {
unsigned pad_0 : 4;
unsigned channel_idx : 4;
unsigned pad_1 : 8;
};
};
};
uint16_t data;
};
/* -------------------------------------------------------------------------- */
void mux_init(void)
{
PCDIR = 0xFFFF;
PCOUT = 0x0000;
P4DIR |= 0x27;
P4OUT &= ~0x27;
}
/* -------------------------------------------------------------------------- */
uint8_t mux_channel_count(void)
{
return 2;
}
/* -------------------------------------------------------------------------- */
mux_state_t mux_convert_to_state(uint8_t channel_idx,
uint8_t group_a_subsensor_idx,
uint8_t group_b_subsensor_idx,
uint8_t mode)
{
union mux_state_u state;
state.channel_idx = channel_idx;
state.group_a.subchannel_idx = group_a_subsensor_idx;
state.group_b.subchannel_idx = group_b_subsensor_idx;
state.group_a.mode = mode;
state.group_b.mode = mode;
return state.data;
}
/* -------------------------------------------------------------------------- */
void mux_convert_from_state(mux_state_t state_u16,
uint8_t* channel_idx,
uint8_t* group_a_subsensor_idx,
uint8_t* group_b_subsensor_idx,
uint8_t* mode)
{
union mux_state_u state;
state.data = state_u16;
*channel_idx = state.channel_idx;
*group_a_subsensor_idx = state.group_a.subchannel_idx;
*group_b_subsensor_idx = state.group_b.subchannel_idx;
*mode = state.group_a.mode;
}
/* -------------------------------------------------------------------------- */
void mux_set_state(mux_state_t state_u16)
{
union mux_state_u state;
state.data = state_u16;
if (state.channel_idx == 0)
{
uint8_t bits = map_to_port_4[state.group_a.mode]; /* vertical sensor only has one subsensor */
PCOUT = 0x0000;
P4OUT = (P4OUT & ~bits) | bits;
}
else
{
P4OUT &= ~0x27;
PCOUT = map_T_to_port_c[state.group_a.offset] |
map_R_to_port_c[state.group_b.offset];
}
}
/* -------------------------------------------------------------------------- */
void mux_disable(void)
{
PCOUT = 0x0000;
P4OUT &= ~0x27;
}