giuliomoro@354: /*
giuliomoro@354:  * DigitalStream.h
giuliomoro@354:  *
giuliomoro@354:  *  Created on: 7 Jun 2016
giuliomoro@354:  *      Author: giulio
giuliomoro@354:  */
giuliomoro@354: 
giuliomoro@354: #ifndef DIGITALCHANNELMANAGER_H_
giuliomoro@354: #define DIGITALCHANNELMANAGER_H_
giuliomoro@354: #include <Bela.h>
giuliomoro@354: 
giuliomoro@354: /**
giuliomoro@354:  * This class manages the digital channels.
giuliomoro@354:  *
giuliomoro@354:  * Each channel can be set as either signal or message rate.
giuliomoro@354:  * When set as message rate:
giuliomoro@354:  * inputs are parsed when calling processInput() and a callback
giuliomoro@354:  * is invoked when they change state, outputs are set via calls to setValue() and then written
giuliomoro@354:  * to the output array when calling processOutput().
giuliomoro@354:  *
giuliomoro@354:  * When set at signal rate:
giuliomoro@354:  * the DigitalChannelManager only sets the pin direction.
giuliomoro@354:  *
giuliomoro@354:  * Throughout the class we keep track of message/signal rate
giuliomoro@354:  * and input/output in separate variables, so that we
giuliomoro@354:  * always know if a channel is managed by the DigitalChannelManager
giuliomoro@354:  * or not. This way, managed and unmanaged channels can be freely mixed in the code.
giuliomoro@354:  *
giuliomoro@354:  * For the reasons above, isSignalRate(channel) is not the same as !isMessageRate(channel)
giuliomoro@354:  * and isInput(channel) is not the same as !isOutput(channel)
giuliomoro@354:  *
giuliomoro@354:  */
giuliomoro@354: 
giuliomoro@354: class DigitalChannelManager {
giuliomoro@354: public:
giuliomoro@354: 	DigitalChannelManager();
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Set the callback.
giuliomoro@354: 	 *
giuliomoro@354: 	 * The callback will be called when one of the managed message-rate input bits
giuliomoro@354: 	 * changes state. The callback's signature is
giuliomoro@354: 	 *   void (*stateChangedCallback)(bool value, unsigned int delay, void* arg)
giuliomoro@354: 	 * where \c value is the new value of the channel, \c delay is the sample in the most
giuliomoro@354: 	 * recent block when the status changed, and \c arg is an additional argument passed
giuliomoro@354: 	 * to the callback, which can be set for each channel using setCallbackArgument()
giuliomoro@354: 	 *
giuliomoro@354: 	*/
giuliomoro@354: 
giuliomoro@354: 	void setCallback(void (*newCallback)(bool, unsigned int, void*)){
giuliomoro@354: 		stateChangedCallback = newCallback;
giuliomoro@354: 		if(newCallback != NULL){
giuliomoro@354: 			callbackEnabled = true;
giuliomoro@354: 		} else {
giuliomoro@354: 			callbackEnabled = false;
giuliomoro@354: 		}
giuliomoro@354: 	};
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Sets the argument for the callback.
giuliomoro@354: 	 *
giuliomoro@354: 	 * Typically an argument will allow the callback to identify the channel
giuliomoro@354: 	 * that changed state.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel is the channel for which the argument is set
giuliomoro@354: 	 * @param arg is the argument that will be passed to the callback for that channel
giuliomoro@354: 	 */
giuliomoro@354: 	void setCallbackArgument(unsigned int channel, void* arg){
giuliomoro@354: 		callbackArguments[channel] = arg;
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/** Process the input signals.
giuliomoro@354: 	 *
giuliomoro@354: 	 * Parses the input array and looks for state changes in the bits
giuliomoro@354: 	 * managed as message-rate inputs and invokes the
giuliomoro@354: 	 * appropriate callbacks.
giuliomoro@354: 	 *
giuliomoro@361: 	 * @param array the array of input values
giuliomoro@361: 	 * @param length the length of the array
giuliomoro@354: 	 *
giuliomoro@354: 	 */
giuliomoro@354: 	void processInput(uint32_t* array, unsigned int length){
giuliomoro@354: 		if(callbackEnabled == false){
giuliomoro@354: 			return;
giuliomoro@354: 		}
giuliomoro@354: 		// DIGITAL_FORMAT_ASSUMPTION
giuliomoro@354: 		static uint16_t lastDigitalInputValues = 0;
giuliomoro@354: 		for (unsigned int frame = 0; frame < length; ++frame) {
giuliomoro@354: 			uint32_t inWord = array[frame];
giuliomoro@354: 			uint16_t direction = inWord & 0Xffff;
giuliomoro@354: 			uint16_t inputValues = (inWord >> 16);
giuliomoro@354: 			// note: even though INPUT == 0 and OUTPUT == 0, this is actually reversed in the binary word,
giuliomoro@354: 			// so it actually is 1 for input and 0 for output
giuliomoro@354: 
giuliomoro@354: 			// mask out outputValues from the half-word by setting them to zero
giuliomoro@354: 			// if a bit of direction is 1, then it is an input.
giuliomoro@354: 			// ANDing this with the inputValues gets rid of the output values
giuliomoro@354: 			inputValues &= direction;
giuliomoro@354: 			uint16_t changed = inputValues ^ lastDigitalInputValues;
giuliomoro@354: 			//mask out channels that are not set at message rate
giuliomoro@354: 			changed &= messageRate;
giuliomoro@354: 			if(changed){
giuliomoro@354: //				rt_printf("changed: 0x%x, messageRate: 0x%x, ANDed: 0x%x\n", changed, messageRate, changed&messageRate);
giuliomoro@354: 				for(int n = 0; n < 16; ++n){
giuliomoro@354: 					if(changed & (1 << n)){ //if state for this channel has changed, invoke the callback
giuliomoro@354: 						stateChangedCallback(inputValues & (1 << n), frame, callbackArguments[n]);
giuliomoro@354: 					}
giuliomoro@354: 				}
giuliomoro@354: 			}
giuliomoro@354: 			lastDigitalInputValues = inputValues;
giuliomoro@354: 		}
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/** Process the output signals.
giuliomoro@354: 	 *
giuliomoro@354: 	 * Processes the output array and appropriately sets the
giuliomoro@354: 	 * bits managed as message-rate outputs.
giuliomoro@354: 	 * Bits marked as input and unmanaged bits are left alone.
giuliomoro@354: 	 *
giuliomoro@354: 	 * It also updates the channel directions.
giuliomoro@354: 	 *
giuliomoro@361: 	 * @param array the array of input values
giuliomoro@361: 	 * @param length the length of the array
giuliomoro@354: 	 *
giuliomoro@354: 	 */
giuliomoro@354: 	void processOutput(uint32_t* array, unsigned int length){
giuliomoro@354: 		uint32_t orWord = ((setDataOut << 16) | modeInput);
giuliomoro@354: 		uint32_t andWord = ~((clearDataOut << 16) | modeOutput);
giuliomoro@354: 		uint32_t outWord;
giuliomoro@354: 		for (unsigned int frame = 0; frame < length; ++frame) {
giuliomoro@354: 			outWord = array[frame];
giuliomoro@354: 			outWord = outWord | orWord;
giuliomoro@354: 			outWord = outWord & andWord;
giuliomoro@354: 			array[frame] = outWord;
giuliomoro@354: 		}
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Inquires if the channel is managed as signal-rate.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel which is inquired.
giuliomoro@354: 	 */
giuliomoro@354: 	bool isSignalRate(unsigned int channel){
giuliomoro@354: 		return (bool)((1 << channel) & signalRate);
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Inquires if the channel is managed as message-rate.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel which is inquired.
giuliomoro@354: 	 * @return true if the channel is managed at message-rate, false otherwise
giuliomoro@354: 	 */
giuliomoro@354: 	bool isMessageRate(unsigned int channel){
giuliomoro@354: 		return (bool)((1 << channel) & messageRate);
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Inquires if the channel is managed as input.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel which is inquired.
giuliomoro@354: 	 * @return true if the channel is managed as input, false otherwise
giuliomoro@354: 	 */
giuliomoro@354: 	bool isInput(unsigned int channel){
giuliomoro@354: 		return (bool)((1 << channel) & modeInput);
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Inquires if the channel is managed as output.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel which is inquired.
giuliomoro@354: 	 * @return true if the channel is managed as output, false otherwise
giuliomoro@354: 	 */
giuliomoro@354: 	bool isOutput(unsigned int channel){
giuliomoro@354: 		return (bool)((1 << channel) & modeOutput);
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Sets the output value for a channel.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel to set.
giuliomoro@354: 	 * @param value the value to set.
giuliomoro@354: 	 */
giuliomoro@354: 	void setValue(unsigned int channel, bool value){
giuliomoro@354: 		if(value == 0){
giuliomoro@354: 			clearDataOut = setBit(clearDataOut, channel);
giuliomoro@354: 			setDataOut = clearBit(setDataOut, channel);
giuliomoro@354: 		} else {
giuliomoro@354: 			setDataOut = setBit(setDataOut, channel);
giuliomoro@354: 			clearDataOut = clearBit(clearDataOut, channel);
giuliomoro@354: 		}
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Stops managing a channel.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel number
giuliomoro@354: 	 */
giuliomoro@354: 	void unmanage(unsigned int channel){
giuliomoro@354: 		messageRate = clearBit(messageRate, channel);
giuliomoro@354: 		signalRate = clearBit(signalRate, channel);
giuliomoro@354: 		modeInput = clearBit(modeInput, channel);
giuliomoro@354: 		modeOutput = clearBit(modeOutput, channel);
giuliomoro@354: 		clearDataOut = clearBit(clearDataOut, channel);
giuliomoro@354: 		setDataOut = clearBit(setDataOut, channel);
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	/**
giuliomoro@354: 	 * Manages a channel.
giuliomoro@354: 	 *
giuliomoro@354: 	 * Starts managing a channel with given direction and rate.
giuliomoro@354: 	 * It can be called repeatedly on a given channel to change
giuliomoro@354: 	 * direction and/or rate.
giuliomoro@354: 	 *
giuliomoro@354: 	 * @param channel the channel to manage.
giuliomoro@354: 	 * @param direction
giuliomoro@354: 	 */
giuliomoro@354: 	void manage(unsigned int channel, bool direction, bool isMessageRate){
giuliomoro@354: 		// direction is expected to be one of INPUT or OUTPUT
giuliomoro@354: 		messageRate = changeBit(messageRate, channel, isMessageRate);
giuliomoro@354: 		signalRate = changeBit(signalRate, channel, !isMessageRate);
giuliomoro@354: 		if(direction == OUTPUT){
giuliomoro@354: 			modeOutput = setBit(modeOutput, channel);
giuliomoro@354: 			modeInput = clearBit(modeInput, channel);
giuliomoro@354: 		} else { // direction == INPUT
giuliomoro@354: 			modeInput = setBit(modeInput, channel);
giuliomoro@354: 			modeOutput = clearBit(modeOutput, channel);
giuliomoro@354: 		}
giuliomoro@354: 		rt_printf("Bela digital: channel %d is set as %s at %s rate\n", channel,
giuliomoro@354: 				isInput(channel) ? "input" : "output", isSignalRate(channel) ? "signal" : "message");
giuliomoro@354: 	}
giuliomoro@354: 
giuliomoro@354: 	virtual ~DigitalChannelManager();
giuliomoro@354: private:
giuliomoro@354: 	bool callbackEnabled;
giuliomoro@354: 	void* callbackArg;
giuliomoro@354: 	void* callbackArguments[16];
giuliomoro@354: 	void (*stateChangedCallback)(bool value, unsigned int delay, void* arg);
giuliomoro@354: 	uint32_t clearDataOut;
giuliomoro@354: 	uint32_t setDataOut;
giuliomoro@354: 	uint16_t modeOutput;
giuliomoro@354: 	uint16_t modeInput;
giuliomoro@354: 	uint16_t messageRate;
giuliomoro@354: 	uint16_t signalRate;
giuliomoro@354: };
giuliomoro@354: 
giuliomoro@354: #endif /* DIGITALCHANNELMANAGER_H_ */