Real-Time Workshop | ![]() ![]() |
Source Code for Inlined ADC Driver
These files are described in Example: An Inlined ADC Driver.
adc.c
/* * File : adc.c * Abstract: * Example S-function device driver (analog to digital convertor) for use * with Simulink and Real-Time Workshop. * This S-function contains simulation code only (except mdlRTW, used * only during code generation.) An error will be generated if * this code is compiled without MATLAB_MEX_FILE defined. That * is,it must be compiled via the MATLAB mex utility. * * DEPENDENCIES: * (1) This S-function is intended for use in conjunction with adc.tlc, * a Target Language Compiler program that generates inlined, real-time code that * implements the real-time I/O functions required by mdlOutputs, etc. * * (2) device.h defines hardware-specific macros, etc. that implement * actual I/O to the board * * (3) This file contains a mdlRTW function that writes parameters to * the model.rtw file during code generation. * * Copyright (c) 1994-2000 by The MathWorks, Inc. All Rights Reserved. * */ /********************* * Required defines * *********************/ #define S_FUNCTION_NAME adc #define S_FUNCTION_LEVEL 2 /********************* * Required includes * *********************/ #include "simstruc.h" /* The Simstruct API, definitions and macros */ /* * Generate a fatal error if this file is (by mistake) used by Real-Time * Workshop. There is a target file corresponding to this S-function: adc.tlc, * which should be used to generate inlined code for this S-funciton. */ #ifndef MATLAB_MEX_FILE # error "Fatal Error: adc.c can only be used to create C-MEX S-Function" #endif /* * Define the number of S-function parameters and set up convenient macros to * access the parameter values. */ #define NUM_S_FUNCTION_PARAMS (4) #define N_CHANNELS (2) /* For this example, num. of channels is fixed */ /* 1. Base Address */ #define BASE_ADDRESS_PARAM (ssGetSFcnParam(S,0)) /* 2. Analog Signal Range */ #define SIGNAL_RANGE_PARAM (ssGetSFcnParam(S,1)) #define MIN_SIGNAL_VALUE ((real_T) (mxGetPr(SIGNAL_RANGE_PARAM)[0])) #define MAX_SIGNAL_VALUE ((real_T) (mxGetPr(SIGNAL_RANGE_PARAM)[1])) /* 3. Hardware Gain */ #define HARDWARE_GAIN_PARAM (ssGetSFcnParam(S,2)) #define HARDWARE_GAIN ((real_T) (mxGetPr(HARDWARE_GAIN_PARAM)[0])) /* 4. Sample Time */ #define SAMPLE_TIME_PARAM (ssGetSFcnParam(S,3)) #define SAMPLE_TIME ((real_T) (mxGetPr(SAMPLE_TIME_PARAM)[0])) /* * Hardware specific information pertaining to the A/D board. This information * should be provided with the documentation that comes with the board. */ #include "device.h" /*====================* * S-function methods * *====================*/ /* Function: mdlCheckParameters ================================================ * Abstract: * Check that the parameters passed to this S-function are valid. */ #define MDL_CHECK_PARAMETERS static void mdlCheckParameters(SimStruct *S) { static char_T errMsg[256]; boolean_T allParamsOK = 1; /* * Base I/O Address */ if (!mxIsChar(BASE_ADDRESS_PARAM)) { sprintf(errMsg, "Base address parameter must be a string.\n"); allParamsOK = 0; goto EXIT_POINT; } /* * Signal Range */ if (mxGetNumberOfElements(SIGNAL_RANGE_PARAM) != 2) { sprintf(errMsg, "Signal Range must be a two element vector [minInp maxInp]\n"); allParamsOK = 0; goto EXIT_POINT; } if ( !adcIsSignalRangeParamOK(MIN_SIGNAL_VALUE, MAX_SIGNAL_VALUE) ) { sprintf(errMsg, "The specified Signal Range is not supported by I/O board.\n"); allParamsOK = 0; goto EXIT_POINT; } /* * Hardware Gain */ if (mxGetNumberOfElements(HARDWARE_GAIN_PARAM) != 1) { sprintf(errMsg, "Hardware Gain must be a scalar valued real number\n"); allParamsOK = 0; goto EXIT_POINT; } if (!adcIsHardwareGainParamOK(HARDWARE_GAIN)) { sprintf(errMsg, "The specified hardware gain is not supported.\n"); allParamsOK = 0; goto EXIT_POINT; } /* * Sample Time */ if (mxGetNumberOfElements(SAMPLE_TIME_PARAM) != 1) { sprintf(errMsg, "Sample Time must be a positive scalar.\n"); allParamsOK = 0; goto EXIT_POINT; } EXIT_POINT: if ( !allParamsOK ) { ssSetErrorStatus(S, errMsg); } } /* end: mdlCheckParameters */ /* Function: mdlInitializeSizes ================================================ * Abstract: * Validate parameters,set number and width of ports. */ static void mdlInitializeSizes(SimStruct *S) { /* Set the number of parameters expected. */ ssSetNumSFcnParams(S, NUM_S_FUNCTION_PARAMS); if ( ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S) ) { /* * If the number of parameter passed in is equal to the number of * parameters expected, then check that the specified parameters * are valid. */ mdlCheckParameters(S); if ( ssGetErrorStatus(S) != NULL ) { return; /* Error was reported in mdlCheckParameters. */ } } else { return; /* Parameter mismatch. Error will be reported by Simulink. */ } /* * This S-functions's parameters cannot be changed in the middle of a * simulation, hence set them to be nontunable. */ { int_T i; for (i=0; i < NUM_S_FUNCTION_PARAMS; i++) { ssSetSFcnParamNotTunable(S, i); } } /* Has no input ports */ if ( !ssSetNumInputPorts(S, 0) ) return; /* Number of output ports = number of channels specified */ if ( !ssSetNumOutputPorts(S, N_CHANNELS) ) return; /* Set the width of each output ports to be one. */ { int_T oPort; for (oPort = 0; oPort < ssGetNumOutputPorts(S); oPort++) { ssSetOutputPortWidth(S, oPort, 1); } } ssSetNumSampleTimes( S, 1); } /* end: mdlInitializeSizes */ /* Function: mdlInitializeSampleTimes ========================================== * Abstract: * Set the sample time of this block as specified via the sample time * parameter. */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); } /* end: mdlInitializeSampleTimes */ /* Function: mdlStart ========================================================== * Abstract: * At the start of simulation in Simulink, print a message to the MATLAB * command window indicating that output of this block will be zero during * simulation. */ #define MDL_START static void mdlStart(SimStruct *S) { if (ssGetSimMode(S) == SS_SIMMODE_NORMAL) { mexPrintf("\n adc.c: The output of the A/D block '%s' will be set " "to zero during simulation in Simulink.\n", ssGetPath(S)); } } /* end: mdlStart */ /* Function: mdlOutputs ======================================================== * Abstract: * Set the output to zero. */ static void mdlOutputs(SimStruct *S, int_T tid) { int oPort; for (oPort = 0; oPort < ssGetNumOutputPorts(S); oPort++) { real_T *y = ssGetOutputPortRealSignal(S, oPort); y[0] = 0.0; } } /* end: mdlOutputs */ /* Function: mdlTerminate ====================================================== * Abstract: * Required S-function method that gets called at the end of simulation * and code generation. Nothing to do in simulation. */ static void mdlTerminate(SimStruct *S) { } /* end: mdlTerminate */ /* Function: mdlRTW ============================================================ * Abstract: * Evaluate parameter data and write it to the model.rtw file. */ #define MDL_RTW static void mdlRTW(SimStruct *S) { boolean_T polarity = adcIsUnipolar(MIN_SIGNAL_VALUE, MAX_SIGNAL_VALUE); real_T offset = polarity ? 0.0 : MIN_SIGNAL_VALUE/HARDWARE_GAIN; real_T resolution = (((MAX_SIGNAL_VALUE-MIN_SIGNAL_VALUE)/HARDWARE_GAIN)/ ADC_NUM_LEVELS); char_T baseAddrStr[128]; if ( mxGetString(BASE_ADDRESS_PARAM, baseAddrStr, 128) ) { ssSetErrorStatus(S, "Error reading Base Address parameter, " "need to increase string buffer size."); return; } if ( !ssWriteRTWParamSettings(S, 4, SSWRITE_VALUE_QSTR, "BaseAddress", baseAddrStr, SSWRITE_VALUE_NUM, "HardwareGain", HARDWARE_GAIN, SSWRITE_VALUE_NUM, "Resolution", resolution, SSWRITE_VALUE_NUM, "Offset", offset) ) { return; /* An error occured, which will be reported by Simulink. */ } } /* end: mdlRTW */ /* * Required include for Simulink-MEX interface mechanism */ #include "simulink.c" /* EOF: adc.c */
adc.tlc
%% File : adc.tlc %% Abstract: %% Target file for the C-Mex S-function adc.c %% %% Copyright (c) 1994-2000 by The MathWorks, Inc. All Rights Reserved. %% %implements "adc" "C" %% Function: BlockTypeSetup =========================================== %% Abstract: %% This function is called once for all instance of the S-function %% "dac" in the model. Since this block requires hardware specific %% information about the I/O board, we generate code to include %% "device.h" in the generated model.h file. %% %function BlockTypeSetup(block, system) void %% %% Use the Target Language Ccompiler global variable INCLUDE_DEVICE_H to make sure that %% the line "#include device.h" gets generated into the model.h %%file only once. %% %if !EXISTS("INCLUDE_DEVICE_H") %assign ::INCLUDE_DEVICE_H = 1 %openfile buffer /* Include information about the I/O board */ #include "device.h" %closefile buffer %<LibCacheIncludes(buffer)> %endif %endfunction %% BlockTypeSetup %% Function: Start ==================================================== %% Abstract: %% Generate code to set the number of channels and the hardware gain %% mask in the start function. %% %function Start(block, system) Output /* %<Type> Block: %<Name> (%<ParamSettings.FunctionName>) */ %% %assign numChannels = block.NumDataOutputPorts %assign baseAddr = SFcnParamSettings.BaseAddress %assign hwGain = SFcnParamSettings.HardwareGain %% %% Initialize the Mux Scan Register to scan from 0 to NumChannels-1. %% Also set the Gain Select Register to the appropriate value. %% adcSetLastChannel(%<baseAddr>, %<numChannels-1>); adcSetHardwareGain(%<baseAddr>, adcGetGainMask(%<hwGain>)); %endfunction %% Start %% Function: Outputs ================================================= %% Abstract: %% Generate inlined code to perform one A/D conversion on the enabled %% channels. %% %function Outputs(block, system) Output %% %assign offset = SFcnParamSettings.Offset %assign resolution = SFcnParamSettings.Resolution %assign baseAddr = SFcnParamSettings.BaseAddress %% /* %<Type> Block: %<Name> (%<ParamSettings.FunctionName>) */ { int_T chIdx; uint_T adcValues[%<NumDataOutputPorts>]; for (chIdx = 0; chIdx < %<NumDataOutputPorts>; chIdx++) { adcStartConversion(%<baseAddr>); while (adcIsBusy(%<baseAddr>)) { /* wait for conversion */ } adcValues[chIdx] = adcGetValue(%<baseAddr>); } %foreach oPort = NumDataOutputPorts %assign y = LibBlockOutputSignal(oPort, "", "", 0) %<y> = %<offset> + %<resolution>*adcValues[%<oPort>]; %endforeach } %endfunction %% Outputs %% EOF: adc.tlc
device.h
/* * File : device.h * * Copyright (c) 1994-2000 by The MathWorks, Inc. All Rights * Reserved. * */ /* * Operating system utilities to read and write to hardware * registers. */ #define ReadByte(addr) inp(addr) #define WriteByte(addr,val) outp(addr,val) /*=============================================================* * Specification of the Analog Input Section of the I/O board * (used in the ADC device driver S-function, adc.c and *adc.tlc) *=======================================================*/ /* * Define macros for the attributes of the A/D board, such as the * number of A/D channels and bits per channel. */ #define ADC_MAX_CHANNELS (16) #define ADC_BITS_PER_CHANNEL (12) #define ADC_NUM_LEVELS ((uint_T) (1 << ADC_BITS_PER_CHANNEL)) /* * Macros to check if the specified parameters are valid. * These macros are used by the C-Mex S-function, adc.c */ #define adcIsUnipolar(lo,hi) (lo == 0.0 && 0.0 < hi) #define adcIsBipolar(lo,hi) (lo + hi == 0.0 && 0.0 < hi) #define adcIsSignalRangeParamOK(l,h) (adcIsUnipolar(l,h) || adcIsBipolar(l,h)) #define adcGetGainMask(g) ( (g==1.0) ? 0x0 : \ ( (g==10.0) ? 0x1 : \ ( (g==100.0) ? 0x2 : \ ( (g==500.0) ? 0x3 : 0x4 ) ) ) ) #define adcIsHardwareGainParamOK(g) (adcGetGainMask(g) != 0x4) #define adcIsNumChannelsParamOK(n) (1 <= n && n <= ADC_MAX_CHANNELS) /* Hardware registers used by the A/D section of the I/O board */ #define ADC_START_CONV_REG(bA) (bA) #define ADC_LO_BYTE_REG(bA) (bA) #define ADC_HI_BYTE_REG(bA) (bA + 0x1) #define ADC_MUX_SCAN_REG(bA) (bA + 0x2) #define ADC_STATUS_REG(bA) (bA + 0x8) #define ADC_GAIN_SELECT_REG(bA) (bA + 0xB) /* * Macros for the A/D section of the I/O board */ #define adcSetLastChannel(bA,n) WriteByte(ADC_MUX_SCAN_REG(bA), n<<4) #define adcSetHardwareGain(bA,gM) WriteByte(ADC_GAIN_SELECT_REG(bA), gM) #define adcStartConversion(bA) WriteByte(ADC_START_CONV_REG(bA), 0x00) #define adcIsBusy(bA) (ReadByte(ADC_STATUS_REG(bA)) & 0x80) #define adcGetLoByte(bA) ReadByte(ADC_LO_BYTE_REG(bA)) #define adcGetHiByte(bA) ReadByte(ADC_HI_BYTE_REG(bA)) #define adcGetValue(bA) ((adcGetLoByte(bA)>>4) | (adcGetHiByte(bA)<<4)) /*============================================================* * Specification of the Analog Output Section of the I/O board * (used in the DAC device driver S-function, adc.c and adc.tlc) *============================================================*/ #define DAC_BITS_PER_CHANNEL (12) #define DAC_UNIPOLAR_ZERO ( 0) #define DAC_BIPOLAR_ZERO (1 << (DAC_BITS_PER_CHANNEL-1)) #define DAC_MIN_OUTPUT (0.0) #define DAC_MAX_OUTPUT ((real_T) ((1 << DAC_BITS_PER_CHANNEL)-1)) #define DAC_NUM_LEVELS ((uint_T) (1 << DAC_BITS_PER_CHANNEL)) /* * Macros to check if the specified parameters are valid. * These macros are used by the C-Mex S-function,dac.c. */ #define dacIsUnipolar(lo,hi) (lo == 0.0 && 0.0 < hi) #define dacIsBipolar(lo,hi) (lo+hi == 0.0 && 0.0 < hi) #define dacIsSignalRangeParamOK(l,h) (dacIsUnipolar(l,h) || dacIsBipolar(l,h)) /* Hardware registers */ #define DAC_LO_BYTE_REG(bA) (bA + 0x4) #define DAC_HI_BYTE_REG(bA) (bA + 0x5) #define dacSetLoByte(bA,c) WriteByte(DAC_LO_BYTE_REG(bA),(c & 0x00f)<<4) #define dacSetHiByte(bA,c) WriteByte(DAC_HI_BYTE_REG(bA),(c & 0xff0)>>4) #define dacSetValue(bA,c) dacSetLoByte(bA,c); dacSetHiByte(bA,c) /* EOF: device.h */
![]() | Building the MEX-File and the Driver Block | Interfacing Parameters and Signals | ![]() |