[ FORK ] I2S library for FRDM 64F, forked from p07gbar/I2S

Fork of I2S by Giles Barton-Owen

Committer:
K4zuki
Date:
Thu Mar 24 01:19:01 2016 +0900
Revision:
3:5bb7f0625fc9
Parent:
2:dd2c3c0ec223
Child:
5:d2062a747673
better variable naming

Who changed what in which revision?

UserRevisionLine numberNew contents of line
k4zuki 2:dd2c3c0ec223 1 /**
k4zuki 2:dd2c3c0ec223 2 * @author Giles Barton-Owen
k4zuki 2:dd2c3c0ec223 3 *
k4zuki 2:dd2c3c0ec223 4 * @section LICENSE
k4zuki 2:dd2c3c0ec223 5 *
k4zuki 2:dd2c3c0ec223 6 * Copyright (c) 2012 mbed
k4zuki 2:dd2c3c0ec223 7 *
k4zuki 2:dd2c3c0ec223 8 * Permission is hereby granted, free of charge, to any person obtaining a copy
k4zuki 2:dd2c3c0ec223 9 * of this software and associated documentation files (the "Software"), to deal
k4zuki 2:dd2c3c0ec223 10 * in the Software without restriction, including without limitation the rights
k4zuki 2:dd2c3c0ec223 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
k4zuki 2:dd2c3c0ec223 12 * copies of the Software, and to permit persons to whom the Software is
k4zuki 2:dd2c3c0ec223 13 * furnished to do so, subject to the following conditions:
k4zuki 2:dd2c3c0ec223 14 *
k4zuki 2:dd2c3c0ec223 15 * The above copyright notice and this permission notice shall be included in
k4zuki 2:dd2c3c0ec223 16 * all copies or substantial portions of the Software.
k4zuki 2:dd2c3c0ec223 17 *
k4zuki 2:dd2c3c0ec223 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
k4zuki 2:dd2c3c0ec223 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
k4zuki 2:dd2c3c0ec223 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
k4zuki 2:dd2c3c0ec223 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
k4zuki 2:dd2c3c0ec223 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
k4zuki 2:dd2c3c0ec223 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
k4zuki 2:dd2c3c0ec223 24 * THE SOFTWARE.
k4zuki 2:dd2c3c0ec223 25 *
k4zuki 2:dd2c3c0ec223 26 * @section DESCRIPTION
k4zuki 2:dd2c3c0ec223 27 * A I2S library for the LPC1768's built-in I2S peripheral
k4zuki 2:dd2c3c0ec223 28 *
k4zuki 2:dd2c3c0ec223 29 */
k4zuki 2:dd2c3c0ec223 30
k4zuki 2:dd2c3c0ec223 31 #include "mbed.h"
k4zuki 2:dd2c3c0ec223 32 #include "math.h"
k4zuki 2:dd2c3c0ec223 33
k4zuki 2:dd2c3c0ec223 34 #ifndef I2S_H
k4zuki 2:dd2c3c0ec223 35 #define I2S_H
k4zuki 2:dd2c3c0ec223 36
k4zuki 2:dd2c3c0ec223 37 #define I2S_TRANSMIT 0
k4zuki 2:dd2c3c0ec223 38 #define I2S_RECEIVE 1
k4zuki 2:dd2c3c0ec223 39
k4zuki 2:dd2c3c0ec223 40 #define I2S_MASTER 0
k4zuki 2:dd2c3c0ec223 41 #define I2S_SLAVE 1
k4zuki 2:dd2c3c0ec223 42
k4zuki 2:dd2c3c0ec223 43 #define I2S_STEREO 0
k4zuki 2:dd2c3c0ec223 44 #define I2S_MONO 1
k4zuki 2:dd2c3c0ec223 45
k4zuki 2:dd2c3c0ec223 46 #define I2S_MUTED 1
k4zuki 2:dd2c3c0ec223 47 #define I2S_UNMUTED 0
k4zuki 2:dd2c3c0ec223 48
k4zuki 2:dd2c3c0ec223 49 #define I2S_4WIRE 1
k4zuki 2:dd2c3c0ec223 50 #define I2S_3WIRE 0
k4zuki 2:dd2c3c0ec223 51
k4zuki 2:dd2c3c0ec223 52 /** A class to play give access to the I2S library
k4zuki 2:dd2c3c0ec223 53 */
k4zuki 2:dd2c3c0ec223 54
k4zuki 2:dd2c3c0ec223 55 class I2S
k4zuki 2:dd2c3c0ec223 56 {
k4zuki 2:dd2c3c0ec223 57
k4zuki 2:dd2c3c0ec223 58 public:
k4zuki 2:dd2c3c0ec223 59
k4zuki 2:dd2c3c0ec223 60 /** Create a I2S instance
k4zuki 2:dd2c3c0ec223 61 *
k4zuki 2:dd2c3c0ec223 62 * @param rxtx Set the I2S instance to be transmit or recieve (I2S_TRANSMIT/I2S_RECEIVE)
K4zuki 3:5bb7f0625fc9 63 * @param SerialData The serial data pin
K4zuki 3:5bb7f0625fc9 64 * @param WordSelect The word select pin
K4zuki 3:5bb7f0625fc9 65 * @param BitClk The clock pin
k4zuki 2:dd2c3c0ec223 66 */
K4zuki 3:5bb7f0625fc9 67 I2S(bool rxtx, PinName SerialData, PinName WordSelect, PinName BitClk);
k4zuki 2:dd2c3c0ec223 68
k4zuki 2:dd2c3c0ec223 69 /** Create a I2S instance: Only with the serial data line set. Won't really do much.
k4zuki 2:dd2c3c0ec223 70 *
k4zuki 2:dd2c3c0ec223 71 * @param rxtx Set the I2S instance to be transmit or recieve (I2S_TRANSMIT/I2S_RECEIVE)
K4zuki 3:5bb7f0625fc9 72 * @param SerialData The serial data pin
k4zuki 2:dd2c3c0ec223 73 */
K4zuki 3:5bb7f0625fc9 74 I2S(bool rxtx, PinName SerialData);
k4zuki 2:dd2c3c0ec223 75
k4zuki 2:dd2c3c0ec223 76
k4zuki 2:dd2c3c0ec223 77 /** Create a I2S instance: Only with serial data line and word select.
k4zuki 2:dd2c3c0ec223 78 *
k4zuki 2:dd2c3c0ec223 79 * @param rxtx Set the I2S instance to be transmit or recieve (I2S_TRANSMIT/I2S_RECEIVE)
K4zuki 3:5bb7f0625fc9 80 * @param SerialData The serial data pin
K4zuki 3:5bb7f0625fc9 81 * @param WordSelect The word select pin
k4zuki 2:dd2c3c0ec223 82 */
K4zuki 3:5bb7f0625fc9 83 I2S(bool rxtx, PinName SerialData, PinName WordSelect);
k4zuki 2:dd2c3c0ec223 84
k4zuki 2:dd2c3c0ec223 85
k4zuki 2:dd2c3c0ec223 86
k4zuki 2:dd2c3c0ec223 87 /** Create a I2S instance: Only with serial data line. Four wire mode means this is functional
k4zuki 2:dd2c3c0ec223 88 *
k4zuki 2:dd2c3c0ec223 89 * @param rxtx Set the I2S instance to be transmit or recieve (I2S_TRANSMIT/I2S_RECEIVE)
K4zuki 3:5bb7f0625fc9 90 * @param SerialData The serial data pin
K4zuki 3:5bb7f0625fc9 91 * @param fourwiremode True means the peripheral is in 4-wire mode. It borroWordSelect WS and CLK from the other half
k4zuki 2:dd2c3c0ec223 92 */
K4zuki 3:5bb7f0625fc9 93 I2S(bool rxtx, PinName SerialData, bool fourwiremode);
k4zuki 2:dd2c3c0ec223 94
k4zuki 2:dd2c3c0ec223 95 /** Create a I2S instance: Only with serial data line and word select line. Four wire mode means this is functional
k4zuki 2:dd2c3c0ec223 96 *
k4zuki 2:dd2c3c0ec223 97 * @param rxtx Set the I2S instance to be transmit or recieve (I2S_TRANSMIT/I2S_RECEIVE)
K4zuki 3:5bb7f0625fc9 98 * @param SerialData The serial data pin
K4zuki 3:5bb7f0625fc9 99 * @param WordSelect The word select pin
K4zuki 3:5bb7f0625fc9 100 * @param fourwiremode True means the peripheral is in 4-wire mode. It borroWordSelect WS and CLK from the other half
k4zuki 2:dd2c3c0ec223 101 */
K4zuki 3:5bb7f0625fc9 102 I2S(bool rxtx, PinName SerialData, PinName WordSelect, bool fourwiremode);
k4zuki 2:dd2c3c0ec223 103
k4zuki 2:dd2c3c0ec223 104 /** Destroy the I2S instance
k4zuki 2:dd2c3c0ec223 105 */
k4zuki 2:dd2c3c0ec223 106 ~I2S();
k4zuki 2:dd2c3c0ec223 107
k4zuki 2:dd2c3c0ec223 108 /** Write to the FIFO
k4zuki 2:dd2c3c0ec223 109 *
k4zuki 2:dd2c3c0ec223 110 * @param buf[] The buffer of values to write: are bit stuffed in fours
k4zuki 2:dd2c3c0ec223 111 * @param len The number of chars to write
k4zuki 2:dd2c3c0ec223 112 */
k4zuki 2:dd2c3c0ec223 113 void write(char buf[], int len);
k4zuki 2:dd2c3c0ec223 114
k4zuki 2:dd2c3c0ec223 115 /** Write to the FIFO
k4zuki 2:dd2c3c0ec223 116 *
k4zuki 2:dd2c3c0ec223 117 * @param buf[] The buffer of values to write: are bit stuffed automatically
k4zuki 2:dd2c3c0ec223 118 * @param len The number of chars to write
k4zuki 2:dd2c3c0ec223 119 */
k4zuki 2:dd2c3c0ec223 120 void write(int buf[], int len);
k4zuki 2:dd2c3c0ec223 121
k4zuki 2:dd2c3c0ec223 122 /** Read the FIFOs contents
k4zuki 2:dd2c3c0ec223 123 *
k4zuki 2:dd2c3c0ec223 124 * @return The buffers value.
k4zuki 2:dd2c3c0ec223 125 */
k4zuki 2:dd2c3c0ec223 126 int read();
k4zuki 2:dd2c3c0ec223 127
k4zuki 2:dd2c3c0ec223 128 /** Read from the FIFO
k4zuki 2:dd2c3c0ec223 129 *
k4zuki 2:dd2c3c0ec223 130 * @param buf[] The buffer of values to read: raw bit shifted
k4zuki 2:dd2c3c0ec223 131 * @param len The number of chars to read
k4zuki 2:dd2c3c0ec223 132 */
k4zuki 2:dd2c3c0ec223 133 void read(char buf[], int len);
k4zuki 2:dd2c3c0ec223 134
k4zuki 2:dd2c3c0ec223 135 /** Read from the FIFO
k4zuki 2:dd2c3c0ec223 136 *
k4zuki 2:dd2c3c0ec223 137 * @param buf[] The buffer of values to read: sorted to just values
k4zuki 2:dd2c3c0ec223 138 * @param len The number of chars to read
k4zuki 2:dd2c3c0ec223 139 */
k4zuki 2:dd2c3c0ec223 140 void read(int buf[], int len);
k4zuki 2:dd2c3c0ec223 141
k4zuki 2:dd2c3c0ec223 142 /** Get the maximum number of points of data the FIFO could store
k4zuki 2:dd2c3c0ec223 143 *
k4zuki 2:dd2c3c0ec223 144 * @return The number of points
k4zuki 2:dd2c3c0ec223 145 */
k4zuki 2:dd2c3c0ec223 146 int max_fifo_points();
k4zuki 2:dd2c3c0ec223 147 /** Switch the peripheral on and off
k4zuki 2:dd2c3c0ec223 148 *
k4zuki 2:dd2c3c0ec223 149 * @param pwr Power status
k4zuki 2:dd2c3c0ec223 150 */
k4zuki 2:dd2c3c0ec223 151 void power(bool pwr);
k4zuki 2:dd2c3c0ec223 152
k4zuki 2:dd2c3c0ec223 153 /** Switch the peripheral between master and slave
k4zuki 2:dd2c3c0ec223 154 *
k4zuki 2:dd2c3c0ec223 155 * @param mastermode The peripherals master/slave status (I2S_MASTER/I2S_SLAVE)
k4zuki 2:dd2c3c0ec223 156 */
k4zuki 2:dd2c3c0ec223 157 void masterslave(bool mastermode);
k4zuki 2:dd2c3c0ec223 158
k4zuki 2:dd2c3c0ec223 159 /** Switch the peripheral between different wordsizes
k4zuki 2:dd2c3c0ec223 160 *
k4zuki 2:dd2c3c0ec223 161 * @param words The number of bits per word: 8,16,32
k4zuki 2:dd2c3c0ec223 162 */
k4zuki 2:dd2c3c0ec223 163 void wordsize(int words);
k4zuki 2:dd2c3c0ec223 164
K4zuki 3:5bb7f0625fc9 165 /** Define the MasterClk frequency
k4zuki 2:dd2c3c0ec223 166 *
K4zuki 3:5bb7f0625fc9 167 * @param freq The frequency desired for the MasterClk
k4zuki 2:dd2c3c0ec223 168 */
k4zuki 2:dd2c3c0ec223 169 void mclk_freq(int freq);
k4zuki 2:dd2c3c0ec223 170
k4zuki 2:dd2c3c0ec223 171 /** Define the sample rate
k4zuki 2:dd2c3c0ec223 172 *
k4zuki 2:dd2c3c0ec223 173 * @param freq The desired sample rate frequency
k4zuki 2:dd2c3c0ec223 174 */
k4zuki 2:dd2c3c0ec223 175 void frequency(int freq);
k4zuki 2:dd2c3c0ec223 176
k4zuki 2:dd2c3c0ec223 177 /** Set the level that the fifo interrupts at
k4zuki 2:dd2c3c0ec223 178 *
k4zuki 2:dd2c3c0ec223 179 * @param level A number between 0 and 7 at which the fifo interrupts
k4zuki 2:dd2c3c0ec223 180 */
k4zuki 2:dd2c3c0ec223 181 void set_interrupt_fifo_level(int level);
k4zuki 2:dd2c3c0ec223 182
k4zuki 2:dd2c3c0ec223 183 /** Get the current FIFO level
k4zuki 2:dd2c3c0ec223 184 *
k4zuki 2:dd2c3c0ec223 185 * @return A number between 0 and 7 the FIFO is currently at
k4zuki 2:dd2c3c0ec223 186 */
k4zuki 2:dd2c3c0ec223 187 int fifo_level();
k4zuki 2:dd2c3c0ec223 188
k4zuki 2:dd2c3c0ec223 189 /** Get the current number of samples in the FIFO
k4zuki 2:dd2c3c0ec223 190 *
k4zuki 2:dd2c3c0ec223 191 * @return A number showing how many samples are in the FIFO
k4zuki 2:dd2c3c0ec223 192 */
k4zuki 2:dd2c3c0ec223 193 int fifo_points();
k4zuki 2:dd2c3c0ec223 194
k4zuki 2:dd2c3c0ec223 195 /** Set whether the peripheral is in stereo or mono mode: in mono the perifpheral sends out the same data twice
k4zuki 2:dd2c3c0ec223 196 *
k4zuki 2:dd2c3c0ec223 197 * @param stereomode Whether the peripheral is in stereo or mono: I2S_STEREO/I2S_MONO
k4zuki 2:dd2c3c0ec223 198 */
k4zuki 2:dd2c3c0ec223 199 void stereomono(bool stereomode);
k4zuki 2:dd2c3c0ec223 200
k4zuki 2:dd2c3c0ec223 201 /** Mute the peripheral
k4zuki 2:dd2c3c0ec223 202 *
k4zuki 2:dd2c3c0ec223 203 */
k4zuki 2:dd2c3c0ec223 204 void mute();
k4zuki 2:dd2c3c0ec223 205
k4zuki 2:dd2c3c0ec223 206 /** Set the mute status of the peripheral
k4zuki 2:dd2c3c0ec223 207 *
k4zuki 2:dd2c3c0ec223 208 * @param mute_en Set whether the mute is enabled
k4zuki 2:dd2c3c0ec223 209 */
k4zuki 2:dd2c3c0ec223 210 void mute(bool mute_en);
k4zuki 2:dd2c3c0ec223 211
k4zuki 2:dd2c3c0ec223 212 /** Stop the peripheral
k4zuki 2:dd2c3c0ec223 213 *
k4zuki 2:dd2c3c0ec223 214 */
k4zuki 2:dd2c3c0ec223 215 void stop();
k4zuki 2:dd2c3c0ec223 216
k4zuki 2:dd2c3c0ec223 217 /** Start the peripheral
k4zuki 2:dd2c3c0ec223 218 *
k4zuki 2:dd2c3c0ec223 219 */
k4zuki 2:dd2c3c0ec223 220 void start();
k4zuki 2:dd2c3c0ec223 221
k4zuki 2:dd2c3c0ec223 222 /** Check the Clock and Pin setup went according to plan
k4zuki 2:dd2c3c0ec223 223 *
k4zuki 2:dd2c3c0ec223 224 * @return Setup okay?
k4zuki 2:dd2c3c0ec223 225 */
k4zuki 2:dd2c3c0ec223 226 bool setup_ok();
k4zuki 2:dd2c3c0ec223 227
k4zuki 2:dd2c3c0ec223 228 /** Attach a function to be called when the FIFO triggers
k4zuki 2:dd2c3c0ec223 229 *
k4zuki 2:dd2c3c0ec223 230 * @param fptr A pointer to the function to be called
k4zuki 2:dd2c3c0ec223 231 */
k4zuki 2:dd2c3c0ec223 232 void attach(void (*fptr)(void))
k4zuki 2:dd2c3c0ec223 233 {
k4zuki 2:dd2c3c0ec223 234 if (_rxtx == I2S_TRANSMIT)
k4zuki 2:dd2c3c0ec223 235 {
k4zuki 2:dd2c3c0ec223 236 I2STXISR.attach(fptr);
k4zuki 2:dd2c3c0ec223 237 txisr = true;
k4zuki 2:dd2c3c0ec223 238 }
k4zuki 2:dd2c3c0ec223 239 else
k4zuki 2:dd2c3c0ec223 240 {
k4zuki 2:dd2c3c0ec223 241 I2SRXISR.attach(fptr);
k4zuki 2:dd2c3c0ec223 242 rxisr = true;
k4zuki 2:dd2c3c0ec223 243 }
k4zuki 2:dd2c3c0ec223 244 }
k4zuki 2:dd2c3c0ec223 245
k4zuki 2:dd2c3c0ec223 246 /** Attach a member function to be called when the FIFO triggers
k4zuki 2:dd2c3c0ec223 247 *
k4zuki 2:dd2c3c0ec223 248 * @param tptr A pointer to the instance of the class
k4zuki 2:dd2c3c0ec223 249 * @param mptr A pointer to the member function
k4zuki 2:dd2c3c0ec223 250 */
k4zuki 2:dd2c3c0ec223 251 template<typename T>
k4zuki 2:dd2c3c0ec223 252 void attach(T *tptr, void (T::*mptr)(void))
k4zuki 2:dd2c3c0ec223 253 {
k4zuki 2:dd2c3c0ec223 254 if (_rxtx == I2S_TRANSMIT)
k4zuki 2:dd2c3c0ec223 255 {
k4zuki 2:dd2c3c0ec223 256 I2STXISR.attach(tptr, mptr);
k4zuki 2:dd2c3c0ec223 257 txisr = true;
k4zuki 2:dd2c3c0ec223 258 }
k4zuki 2:dd2c3c0ec223 259 else
k4zuki 2:dd2c3c0ec223 260 {
k4zuki 2:dd2c3c0ec223 261 I2SRXISR.attach(tptr, mptr);
k4zuki 2:dd2c3c0ec223 262 rxisr = true;
k4zuki 2:dd2c3c0ec223 263 }
k4zuki 2:dd2c3c0ec223 264 }
k4zuki 2:dd2c3c0ec223 265
k4zuki 2:dd2c3c0ec223 266 private:
k4zuki 2:dd2c3c0ec223 267
k4zuki 2:dd2c3c0ec223 268 void _set_clock_112896(void);
k4zuki 2:dd2c3c0ec223 269 void _set_clock_122800(void);
k4zuki 2:dd2c3c0ec223 270 void _i2s_init(void);
k4zuki 2:dd2c3c0ec223 271 void _i2s_set_rate(int smprate);
k4zuki 2:dd2c3c0ec223 272
k4zuki 2:dd2c3c0ec223 273 void mclk_enable(bool mclk_en);
k4zuki 2:dd2c3c0ec223 274
k4zuki 2:dd2c3c0ec223 275 void write_registers();
k4zuki 2:dd2c3c0ec223 276
k4zuki 2:dd2c3c0ec223 277 void pin_setup();
k4zuki 2:dd2c3c0ec223 278
k4zuki 2:dd2c3c0ec223 279 void fraction_estimator(float in, int * num, int * den);
k4zuki 2:dd2c3c0ec223 280
k4zuki 2:dd2c3c0ec223 281 float mod(float in);
k4zuki 2:dd2c3c0ec223 282
k4zuki 2:dd2c3c0ec223 283 void defaulter();
k4zuki 2:dd2c3c0ec223 284
K4zuki 3:5bb7f0625fc9 285 PinName _SerialData, _WordSelect, _BitClk, _MasterClk;
K4zuki 3:5bb7f0625fc9 286 bool WordSelect_d, BitClk_d, MasterClk_d;
k4zuki 2:dd2c3c0ec223 287 bool _rxtx;
k4zuki 2:dd2c3c0ec223 288 bool pwr;
k4zuki 2:dd2c3c0ec223 289 bool master;
k4zuki 2:dd2c3c0ec223 290 int wordwidth;
k4zuki 2:dd2c3c0ec223 291 char wordwidth_code;
k4zuki 2:dd2c3c0ec223 292 bool mclk_en;
k4zuki 2:dd2c3c0ec223 293 int mclk_frequency;
k4zuki 2:dd2c3c0ec223 294 int freq;
k4zuki 2:dd2c3c0ec223 295 bool stereo;
k4zuki 2:dd2c3c0ec223 296 bool muted;
k4zuki 2:dd2c3c0ec223 297 bool stopped;
k4zuki 2:dd2c3c0ec223 298 int interrupt_fifo_level;
k4zuki 2:dd2c3c0ec223 299 int pin_setup_err;
k4zuki 2:dd2c3c0ec223 300 int reg_write_err;
k4zuki 2:dd2c3c0ec223 301 bool deallocating;
k4zuki 2:dd2c3c0ec223 302 int old_freq;
k4zuki 2:dd2c3c0ec223 303
k4zuki 2:dd2c3c0ec223 304 bool fourwire;
k4zuki 2:dd2c3c0ec223 305
k4zuki 2:dd2c3c0ec223 306 int old_pre_num;
k4zuki 2:dd2c3c0ec223 307 int old_pre_den;
k4zuki 2:dd2c3c0ec223 308 int old_bitrate_div;
k4zuki 2:dd2c3c0ec223 309 static void _i2sisr(void);
k4zuki 2:dd2c3c0ec223 310
k4zuki 2:dd2c3c0ec223 311 static FunctionPointer I2STXISR;
k4zuki 2:dd2c3c0ec223 312 static FunctionPointer I2SRXISR;
k4zuki 2:dd2c3c0ec223 313
k4zuki 2:dd2c3c0ec223 314 static bool txisr;
k4zuki 2:dd2c3c0ec223 315 static bool rxisr;
k4zuki 2:dd2c3c0ec223 316
k4zuki 2:dd2c3c0ec223 317 void write(int bufr[], int bufl[], int len);
k4zuki 2:dd2c3c0ec223 318 void read(int bufr[], int bufl[], int len);
k4zuki 2:dd2c3c0ec223 319 };
k4zuki 2:dd2c3c0ec223 320
k4zuki 2:dd2c3c0ec223 321 #endif