Mercurial > ecos-v3_0-branch
comparison packages/io/adc/current/src/adc.c @ 2512:be7755f32fd0
Add Generic ADC support. See Changelogs for details.
| author | nickg |
|---|---|
| date | Fri, 08 Aug 2008 10:47:04 +0000 |
| parents | |
| children | 74dbf4c3f2e1 |
comparison
equal
deleted
inserted
replaced
| 2511:1f18f909a18e | 2512:be7755f32fd0 |
|---|---|
| 1 /*========================================================================== | |
| 2 // | |
| 3 // adc.c | |
| 4 // | |
| 5 // Generic ADC driver layer | |
| 6 // | |
| 7 //========================================================================== | |
| 8 //####ECOSGPLCOPYRIGHTBEGIN#### | |
| 9 // ------------------------------------------- | |
| 10 // This file is part of eCos, the Embedded Configurable Operating System. | |
| 11 // Copyright (C) 2008 eCosCentric Limited | |
| 12 // | |
| 13 // eCos is free software; you can redistribute it and/or modify it under | |
| 14 // the terms of the GNU General Public License as published by the Free | |
| 15 // Software Foundation; either version 2 or (at your option) any later version. | |
| 16 // | |
| 17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY | |
| 18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 20 // for more details. | |
| 21 // | |
| 22 // You should have received a copy of the GNU General Public License along | |
| 23 // with eCos; if not, write to the Free Software Foundation, Inc., | |
| 24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. | |
| 25 // | |
| 26 // As a special exception, if other files instantiate templates or use macros | |
| 27 // or inline functions from this file, or you compile this file and link it | |
| 28 // with other works to produce a work based on this file, this file does not | |
| 29 // by itself cause the resulting work to be covered by the GNU General Public | |
| 30 // License. However the source code for this file must still be made available | |
| 31 // in accordance with section (3) of the GNU General Public License. | |
| 32 // | |
| 33 // This exception does not invalidate any other reasons why a work based on | |
| 34 // this file might be covered by the GNU General Public License. | |
| 35 // ------------------------------------------- | |
| 36 //####ECOSGPLCOPYRIGHTEND#### | |
| 37 //========================================================================== | |
| 38 //#####DESCRIPTIONBEGIN#### | |
| 39 // | |
| 40 // Author(s): nickg | |
| 41 // Date: 2008-03-31 | |
| 42 // Description: Implements generic layer of ADC drivers. | |
| 43 // | |
| 44 //####DESCRIPTIONEND#### | |
| 45 // | |
| 46 //========================================================================*/ | |
| 47 | |
| 48 #include <cyg/io/adc.h> | |
| 49 | |
| 50 //========================================================================== | |
| 51 // Diagnostic support | |
| 52 // | |
| 53 // Switch the #if to 1 to generate some diagnostic messages. | |
| 54 | |
| 55 #if 0 | |
| 56 | |
| 57 #include <cyg/infra/diag.h> | |
| 58 | |
| 59 #define adc_diag( __fmt, ... ) diag_printf("ADC: %30s[%4d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ ); | |
| 60 #define adc_dump_buf( __buf, __size ) diag_dump_buf( __buf, __size ) | |
| 61 #else | |
| 62 #define adc_diag( __fmt, ... ) | |
| 63 #define adc_dump_buf( __buf, __size ) | |
| 64 #endif | |
| 65 | |
| 66 //========================================================================== | |
| 67 // Main device table entry functions | |
| 68 | |
| 69 static Cyg_ErrNo adc_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len); | |
| 70 static Cyg_ErrNo adc_read(cyg_io_handle_t handle, void *buf, cyg_uint32 *len); | |
| 71 static cyg_bool adc_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info); | |
| 72 static Cyg_ErrNo adc_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *buf, cyg_uint32 *len); | |
| 73 static Cyg_ErrNo adc_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *buf, cyg_uint32 *len); | |
| 74 | |
| 75 DEVIO_TABLE(cyg_io_adc_devio, | |
| 76 adc_write, | |
| 77 adc_read, | |
| 78 adc_select, | |
| 79 adc_get_config, | |
| 80 adc_set_config | |
| 81 ); | |
| 82 | |
| 83 //========================================================================== | |
| 84 // Device interface functions | |
| 85 | |
| 86 //-------------------------------------------------------------------------- | |
| 87 // Device write | |
| 88 // | |
| 89 // Not supported | |
| 90 | |
| 91 static Cyg_ErrNo adc_write(cyg_io_handle_t handle, const void *buf, cyg_uint32 *len) | |
| 92 { | |
| 93 adc_diag("write not supported\n"); | |
| 94 return -EDEVNOSUPP; | |
| 95 } | |
| 96 | |
| 97 //-------------------------------------------------------------------------- | |
| 98 // Device Read | |
| 99 // | |
| 100 // Returns a whole number of samples from a channel | |
| 101 | |
| 102 static Cyg_ErrNo adc_read(cyg_io_handle_t handle, void *abuf, cyg_uint32 *len) | |
| 103 { | |
| 104 Cyg_ErrNo res = ENOERR; | |
| 105 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 106 cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; | |
| 107 cyg_adc_device *dev = chan->device; | |
| 108 | |
| 109 cyg_adc_sample_t *buf = (cyg_adc_sample_t *)abuf; | |
| 110 cyg_uint32 size = 0; | |
| 111 | |
| 112 adc_diag("chan %d buf %p len %u\n", chan->channel, abuf, *len); | |
| 113 | |
| 114 // Check that supplied buffer address is aligned to sample size. | |
| 115 if( (((CYG_ADDRESS)buf) & (sizeof(cyg_adc_sample_t)-1)) != 0 ) | |
| 116 return -EIO; | |
| 117 | |
| 118 // Check that channel is enabled | |
| 119 if( !chan->enabled ) | |
| 120 return -EIO; | |
| 121 | |
| 122 cyg_drv_mutex_lock( &dev->lock ); | |
| 123 cyg_drv_dsr_lock(); | |
| 124 { | |
| 125 while( (*len-size) >= sizeof(cyg_adc_sample_t) ) | |
| 126 { | |
| 127 if( chan->put != chan->get ) | |
| 128 { | |
| 129 // A sample is available, transfer it to the buffer. | |
| 130 | |
| 131 int next = chan->get+1; | |
| 132 cyg_drv_isr_lock(); | |
| 133 { | |
| 134 *buf++ = chan->buf[chan->get]; | |
| 135 if( next == chan->len ) | |
| 136 next = 0; | |
| 137 chan->get = next; | |
| 138 } | |
| 139 cyg_drv_isr_unlock(); | |
| 140 size += sizeof(cyg_adc_sample_t); | |
| 141 | |
| 142 adc_diag("chan %d sample %d -> %04x\n", chan->channel, size, buf[-1] ); | |
| 143 } | |
| 144 else | |
| 145 { | |
| 146 // If there is no data available, either wait or | |
| 147 // return EAGAIN. | |
| 148 | |
| 149 if( !chan->blocking ) | |
| 150 { | |
| 151 // If non-blocking, return what we have. If | |
| 152 // nothing, return EAGAIN. | |
| 153 *len = size; | |
| 154 if( size == 0 ) | |
| 155 res = -EAGAIN; | |
| 156 break; | |
| 157 } | |
| 158 // Otherwise, we must wait for data to arrive. | |
| 159 | |
| 160 adc_diag("wait for sample\n"); | |
| 161 chan->waiting = true; | |
| 162 if( !cyg_drv_cond_wait( &chan->wait ) ) | |
| 163 { | |
| 164 // Abort the wait, return EINTR | |
| 165 adc_diag("abort\n"); | |
| 166 *len = size; | |
| 167 res = -EINTR; | |
| 168 break; | |
| 169 } | |
| 170 | |
| 171 adc_diag("wake up\n"); | |
| 172 // Loop back round to pick up any samples now | |
| 173 // available. | |
| 174 } | |
| 175 } | |
| 176 } | |
| 177 cyg_drv_dsr_unlock(); | |
| 178 cyg_drv_mutex_unlock( &dev->lock ); | |
| 179 | |
| 180 return res; | |
| 181 } | |
| 182 | |
| 183 //-------------------------------------------------------------------------- | |
| 184 // Select support | |
| 185 // | |
| 186 // If select support is enabled, check for pending samples. | |
| 187 | |
| 188 static cyg_bool adc_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info) | |
| 189 { | |
| 190 #ifdef CYGPKG_IO_ADC_SELECT_SUPPORT | |
| 191 | |
| 192 cyg_bool res = false; | |
| 193 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 194 cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; | |
| 195 cyg_adc_device *dev = chan->device; | |
| 196 | |
| 197 // Check that channel is enabled. If not, return false. | |
| 198 if( !chan->enabled ) | |
| 199 return false; | |
| 200 | |
| 201 // Only read select is supported | |
| 202 if( which == CYG_FREAD ) | |
| 203 { | |
| 204 cyg_drv_mutex_lock( &dev->lock ); | |
| 205 cyg_drv_dsr_lock(); | |
| 206 { | |
| 207 cyg_drv_isr_lock(); | |
| 208 if( chan->put == chan->get ) | |
| 209 { | |
| 210 // There is no data in the buffer, register the select. | |
| 211 | |
| 212 cyg_selrecord( info, &chan->selinfo ); | |
| 213 } | |
| 214 else | |
| 215 { | |
| 216 res = true; | |
| 217 } | |
| 218 cyg_drv_isr_unlock(); | |
| 219 } | |
| 220 cyg_drv_dsr_unlock(); | |
| 221 cyg_drv_mutex_unlock( &dev->lock ); | |
| 222 | |
| 223 } | |
| 224 | |
| 225 return res; | |
| 226 | |
| 227 #else | |
| 228 | |
| 229 return true; | |
| 230 | |
| 231 #endif | |
| 232 } | |
| 233 | |
| 234 //-------------------------------------------------------------------------- | |
| 235 // Get config | |
| 236 // | |
| 237 // Return various configuration options. | |
| 238 | |
| 239 static Cyg_ErrNo adc_get_config(cyg_io_handle_t handle, cyg_uint32 key, void *xbuf, cyg_uint32 *len) | |
| 240 { | |
| 241 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 242 cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; | |
| 243 cyg_adc_info_t *buf = (cyg_adc_info_t *)xbuf; | |
| 244 Cyg_ErrNo res = ENOERR; | |
| 245 | |
| 246 adc_diag("chan %d\n", chan->channel ); | |
| 247 switch( key ) | |
| 248 { | |
| 249 case CYG_IO_GET_CONFIG_ADC_RATE: | |
| 250 *buf = chan->device->config; | |
| 251 break; | |
| 252 | |
| 253 case CYG_IO_GET_CONFIG_READ_BLOCKING: | |
| 254 if (*len < sizeof(cyg_uint32)) | |
| 255 return -EINVAL; | |
| 256 *(cyg_uint32*)xbuf = (chan->blocking) ? 1 : 0; | |
| 257 break; | |
| 258 | |
| 259 | |
| 260 default: | |
| 261 res = -EINVAL; | |
| 262 } | |
| 263 | |
| 264 return res; | |
| 265 } | |
| 266 | |
| 267 //-------------------------------------------------------------------------- | |
| 268 // Set config | |
| 269 // | |
| 270 // Set configuration options. | |
| 271 | |
| 272 static Cyg_ErrNo adc_set_config(cyg_io_handle_t handle, cyg_uint32 key, const void *xbuf, cyg_uint32 *len) | |
| 273 { | |
| 274 | |
| 275 cyg_devtab_entry_t *t = (cyg_devtab_entry_t *)handle; | |
| 276 cyg_adc_channel *chan = (cyg_adc_channel *)t->priv; | |
| 277 cyg_adc_info_t *buf = (cyg_adc_info_t *)xbuf; | |
| 278 Cyg_ErrNo res = ENOERR; | |
| 279 | |
| 280 adc_diag("chan %d\n", chan->channel ); | |
| 281 | |
| 282 switch( key ) | |
| 283 { | |
| 284 case CYG_IO_SET_CONFIG_ADC_RATE: | |
| 285 chan->device->config = *buf; | |
| 286 chan->device->funs->set_rate( chan, buf->rate ); | |
| 287 break; | |
| 288 | |
| 289 case CYG_IO_SET_CONFIG_ADC_ENABLE: | |
| 290 chan->enabled = true; | |
| 291 chan->device->funs->enable( chan ); | |
| 292 break; | |
| 293 | |
| 294 case CYG_IO_SET_CONFIG_ADC_DISABLE: | |
| 295 chan->enabled = false; | |
| 296 chan->device->funs->disable( chan ); | |
| 297 break; | |
| 298 | |
| 299 case CYG_IO_SET_CONFIG_READ_BLOCKING: | |
| 300 if (*len < sizeof(cyg_uint32)) | |
| 301 return -EINVAL; | |
| 302 | |
| 303 chan->blocking = (1 == *(cyg_uint32*)xbuf) ? true : false; | |
| 304 break; | |
| 305 | |
| 306 default: | |
| 307 res = -EINVAL; | |
| 308 } | |
| 309 | |
| 310 return res; | |
| 311 } | |
| 312 | |
| 313 //========================================================================== | |
| 314 // Callbacks from hardware driver to generic layer | |
| 315 | |
| 316 //-------------------------------------------------------------------------- | |
| 317 // Initialize generic device structure | |
| 318 | |
| 319 __externC void cyg_adc_device_init( cyg_adc_device *device ) | |
| 320 { | |
| 321 if( device->init ) | |
| 322 return; | |
| 323 | |
| 324 adc_diag("\n"); | |
| 325 | |
| 326 device->init = true; | |
| 327 cyg_drv_mutex_init( &device->lock ); | |
| 328 } | |
| 329 | |
| 330 //-------------------------------------------------------------------------- | |
| 331 // Initialize generic channel structure | |
| 332 | |
| 333 __externC void cyg_adc_channel_init(cyg_adc_channel *chan) | |
| 334 { | |
| 335 if( chan->init ) | |
| 336 return; | |
| 337 | |
| 338 adc_diag("chan %d\n", chan->channel ); | |
| 339 | |
| 340 chan->init = true; | |
| 341 cyg_drv_cond_init( &chan->wait, &chan->device->lock ); | |
| 342 chan->enabled = false; | |
| 343 chan->waiting = false; | |
| 344 chan->wakeup = false; | |
| 345 chan->blocking = true; | |
| 346 chan->overflow = 0; | |
| 347 | |
| 348 #ifdef CYGPKG_IO_ADC_SELECT_SUPPORT | |
| 349 cyg_selinit( &chan->selinfo ); | |
| 350 #endif | |
| 351 | |
| 352 } | |
| 353 | |
| 354 //-------------------------------------------------------------------------- | |
| 355 // Add a new sample to the buffer | |
| 356 // | |
| 357 // This function is called from the ISR to store a sample in the | |
| 358 // buffer. | |
| 359 | |
| 360 __externC cyg_uint32 cyg_adc_receive_sample(cyg_adc_channel *chan, cyg_adc_sample_t sample) | |
| 361 { | |
| 362 cyg_uint32 res = 0; | |
| 363 int next = chan->put+1; | |
| 364 | |
| 365 adc_diag("chan %d sample %04x\n", chan->channel, sample ); | |
| 366 | |
| 367 // Ignore this sample if the channel is not enabled. | |
| 368 if( !chan->enabled ) | |
| 369 return 0; | |
| 370 | |
| 371 if( next == chan->len ) | |
| 372 next = 0; | |
| 373 | |
| 374 if( next == chan->get ) | |
| 375 { | |
| 376 // The buffer is full. Record an overflow and arrange to | |
| 377 // replace the oldest sample with the new one. | |
| 378 | |
| 379 adc_diag("chan %d overflow\n", chan->channel); | |
| 380 chan->overflow++; | |
| 381 | |
| 382 chan->get++; | |
| 383 if( chan->get == chan->len ) | |
| 384 chan->get = 0; | |
| 385 } | |
| 386 | |
| 387 // If this is the first sample into the buffer, wake any waiting | |
| 388 // selectors. Also do this if there are any threads waiting for | |
| 389 // data. | |
| 390 if( chan->put == chan->get || chan->waiting ) | |
| 391 res |= CYG_ISR_CALL_DSR, chan->wakeup = true; | |
| 392 | |
| 393 // Store the new sample | |
| 394 chan->buf[chan->put] = sample; | |
| 395 chan->put = next; | |
| 396 | |
| 397 return res; | |
| 398 } | |
| 399 | |
| 400 //-------------------------------------------------------------------------- | |
| 401 // Wakeup a channel | |
| 402 // | |
| 403 // This function is called from the DSR if the channel wakeup field is | |
| 404 // set. It satisfies any select operations or any waiting readers. | |
| 405 | |
| 406 __externC void cyg_adc_wakeup(cyg_adc_channel *chan ) | |
| 407 { | |
| 408 adc_diag("chan %d\n", chan->channel ); | |
| 409 | |
| 410 #ifdef CYGPKG_IO_ADC_SELECT_SUPPORT | |
| 411 cyg_selwakeup( &chan->selinfo ); | |
| 412 #endif | |
| 413 | |
| 414 chan->waiting = false; | |
| 415 cyg_drv_cond_signal( &chan->wait ); | |
| 416 | |
| 417 chan->wakeup = false; | |
| 418 } | |
| 419 | |
| 420 //========================================================================== | |
| 421 // End of adc.c |
