brians | 0ab60bb | 2013-01-31 02:21:51 +0000 | [diff] [blame^] | 1 | /* |
| 2 | FreeRTOS V6.0.5 - Copyright (C) 2010 Real Time Engineers Ltd. |
| 3 | |
| 4 | This file is part of the FreeRTOS distribution. |
| 5 | |
| 6 | FreeRTOS is free software; you can redistribute it and/or modify it under |
| 7 | the terms of the GNU General Public License (version 2) as published by the |
| 8 | Free Software Foundation AND MODIFIED BY the FreeRTOS exception. |
| 9 | ***NOTE*** The exception to the GPL is included to allow you to distribute |
| 10 | a combined work that includes FreeRTOS without being obliged to provide the |
| 11 | source code for proprietary components outside of the FreeRTOS kernel. |
| 12 | FreeRTOS is distributed in the hope that it will be useful, but WITHOUT |
| 13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 15 | more details. You should have received a copy of the GNU General Public |
| 16 | License and the FreeRTOS license exception along with FreeRTOS; if not it |
| 17 | can be viewed here: http://www.freertos.org/a00114.html and also obtained |
| 18 | by writing to Richard Barry, contact details for whom are available on the |
| 19 | FreeRTOS WEB site. |
| 20 | |
| 21 | */ |
| 22 | |
| 23 | /* Standard includes. */ |
| 24 | #include "stdio.h" |
| 25 | |
| 26 | /* Scheduler includes. */ |
| 27 | #include "FreeRTOS.h" |
| 28 | #include "queue.h" |
| 29 | #include "task.h" |
| 30 | |
| 31 | /* Demo app includes. */ |
| 32 | #include "flash.h" |
| 33 | #include "partest.h" |
| 34 | #include "analog.h" |
| 35 | #include "spi.h" |
| 36 | #include "LPCUSB/usbapi.h" |
| 37 | |
| 38 | /*-----------------------------------------------------------*/ |
| 39 | |
| 40 | /* The time between cycles of the 'check' functionality (defined within the |
| 41 | tick hook. */ |
| 42 | #define mainCHECK_DELAY ((portTickType) 5000 / portTICK_RATE_MS) |
| 43 | |
| 44 | /* Task priorities. */ |
| 45 | #define mainQUEUE_POLL_PRIORITY (tskIDLE_PRIORITY + 2) |
| 46 | #define mainSEM_TEST_PRIORITY (tskIDLE_PRIORITY + 1) |
| 47 | #define mainBLOCK_Q_PRIORITY (tskIDLE_PRIORITY + 2) |
| 48 | #define mainUIP_TASK_PRIORITY (tskIDLE_PRIORITY + 3) |
| 49 | #define mainINTEGER_TASK_PRIORITY (tskIDLE_PRIORITY) |
| 50 | #define mainGEN_QUEUE_TASK_PRIORITY (tskIDLE_PRIORITY) |
| 51 | #define mainFLASH_TASK_PRIORITY (tskIDLE_PRIORITY + 2) |
| 52 | |
| 53 | /* The WEB server has a larger stack as it utilises stack hungry string |
| 54 | handling library calls. */ |
| 55 | #define mainBASIC_WEB_STACK_SIZE (configMINIMAL_STACK_SIZE * 4) |
| 56 | |
| 57 | int32_t goal = 0; |
| 58 | int64_t gyro_angle = 0; |
| 59 | |
| 60 | /*-----------------------------------------------------------*/ |
| 61 | |
| 62 | /* |
| 63 | * Configure the hardware for the demo. |
| 64 | */ |
| 65 | static void prvSetupHardware(void); |
| 66 | |
| 67 | /* |
| 68 | * The task that handles the USB stack. |
| 69 | */ |
| 70 | extern void vUSBTask(void *pvParameters); |
| 71 | |
| 72 | extern int VCOM_getchar(void); |
| 73 | |
| 74 | int VCOM_putchar(int c); |
| 75 | |
| 76 | inline int32_t encoder() |
| 77 | { |
| 78 | return (int32_t)QEI->QEIPOS; |
| 79 | } |
| 80 | |
| 81 | static portTASK_FUNCTION(vPrintPeriodic, pvParameters) |
| 82 | { |
| 83 | portTickType xLastFlashTime; |
| 84 | |
| 85 | /* We need to initialise xLastFlashTime prior to the first call to |
| 86 | vTaskDelayUntil(). */ |
| 87 | xLastFlashTime = xTaskGetTickCount(); |
| 88 | |
| 89 | analog_init(); |
| 90 | |
| 91 | encoder_init(); |
| 92 | |
| 93 | // Wait 100 ms for it to boot. |
| 94 | vTaskDelayUntil(&xLastFlashTime, 100 / portTICK_RATE_MS); |
| 95 | spi_init(); |
| 96 | |
| 97 | // Enable USB. The PC has probably disconnected it now. |
| 98 | USBHwAllowConnect(); |
| 99 | |
| 100 | // TODO(aschuh): Write this into a gyro calibration function, and check all the outputs. |
| 101 | vTaskDelayUntil(&xLastFlashTime, 50 / portTICK_RATE_MS); |
| 102 | enable_gyro_csel(); |
| 103 | printf("SPI Gyro Second Response 0x%x %x\n", transfer_spi_bytes(0x2000), transfer_spi_bytes(0x0000)); |
| 104 | disable_gyro_csel(); |
| 105 | |
| 106 | vTaskDelayUntil(&xLastFlashTime, 50 / portTICK_RATE_MS); |
| 107 | enable_gyro_csel(); |
| 108 | printf("SPI Gyro Third Response 0x%x %x\n", transfer_spi_bytes(0x2000), transfer_spi_bytes(0x0000)); |
| 109 | disable_gyro_csel(); |
| 110 | |
| 111 | vTaskDelayUntil(&xLastFlashTime, 10 / portTICK_RATE_MS); |
| 112 | enable_gyro_csel(); |
| 113 | printf("SPI Gyro Fourth Response 0x%x %x\n", transfer_spi_bytes(0x2000), transfer_spi_bytes(0x0000)); |
| 114 | disable_gyro_csel(); |
| 115 | const int hz = 200; |
| 116 | const int flash_hz = 10; |
| 117 | const int startup_cycles = hz * 2; |
| 118 | const int zeroing_cycles = hz * 6; |
| 119 | int32_t zero_bias = 0; |
| 120 | int32_t startup_cycles_left = startup_cycles; |
| 121 | int32_t zeroing_cycles_left = zeroing_cycles; |
| 122 | int32_t full_units_offset = 0; |
| 123 | int32_t remainder_offset = 0; |
| 124 | int32_t remainder_sum = 0; |
| 125 | int32_t led_flash = 0; |
| 126 | vParTestSetLED(0, 0); |
| 127 | |
| 128 | for (;;) { |
| 129 | led_flash ++; |
| 130 | if (led_flash < hz / flash_hz / 2) { |
| 131 | vParTestSetLED(1, 0); |
| 132 | } else { |
| 133 | vParTestSetLED(1, 1); |
| 134 | } |
| 135 | if (led_flash >= hz / flash_hz) { |
| 136 | led_flash = 0; |
| 137 | } |
| 138 | /* Delay for half the flash period then turn the LED on. */ |
| 139 | vTaskDelayUntil(&xLastFlashTime, 1000 / hz / portTICK_RATE_MS); |
| 140 | enable_gyro_csel(); |
| 141 | uint16_t high_value = transfer_spi_bytes(0x2000); |
| 142 | uint16_t low_value = transfer_spi_bytes(0x0000); |
| 143 | disable_gyro_csel(); |
| 144 | int16_t gyro_value = -((int16_t)((((uint32_t)high_value << 16) | (uint32_t)low_value) >> 10)); |
| 145 | |
| 146 | if (startup_cycles_left) { |
| 147 | vParTestSetLED(2, 0); |
| 148 | --startup_cycles_left; |
| 149 | } else if (zeroing_cycles_left) { |
| 150 | vParTestSetLED(2, 1); |
| 151 | //printf("Zeroing "); |
| 152 | --zeroing_cycles_left; |
| 153 | zero_bias -= gyro_value; |
| 154 | if (zeroing_cycles_left == 0) { |
| 155 | // Do all the nice math |
| 156 | full_units_offset = zero_bias / zeroing_cycles; |
| 157 | remainder_offset = zero_bias % zeroing_cycles; |
| 158 | if (remainder_offset < 0) { |
| 159 | remainder_offset += zeroing_cycles; |
| 160 | --full_units_offset; |
| 161 | } |
| 162 | } |
| 163 | } else { |
| 164 | vParTestSetLED(2, 0); |
| 165 | int64_t new_angle = gyro_angle + gyro_value + full_units_offset; |
| 166 | if (remainder_sum >= zeroing_cycles) { |
| 167 | remainder_sum -= zeroing_cycles; |
| 168 | new_angle += 1; |
| 169 | } |
| 170 | NVIC_DisableIRQ(USB_IRQn); |
| 171 | gyro_angle = new_angle; |
| 172 | NVIC_EnableIRQ(USB_IRQn); |
| 173 | remainder_sum += remainder_offset; |
| 174 | } |
| 175 | //printf("Angle %d Rate %d\n", (int)(gyro_angle / 16), (int)(gyro_value + full_units_offset)); |
| 176 | |
| 177 | //printf("time: %d analog %d encoder %d goal %d\n", (int)i, (int)analog(5), (int)encoder(), (int)goal); |
| 178 | |
| 179 | //printf("time: %d encoder %d goal %d\n", (int)i, (int)encoder(), (int)goal); |
| 180 | /* |
| 181 | for(i = 0; i < 4; i++){ |
| 182 | printf("analog(%d) => %d\n",i,analog(i)); |
| 183 | } |
| 184 | for(i = 1; i < 13; i++){ |
| 185 | printf("digital(%d) => %d\n",i,digital(i)); |
| 186 | } |
| 187 | for(i = 0; i < 4; i++){ |
| 188 | printf("dip(%d) => %d\n",i,dip(i)); |
| 189 | } |
| 190 | for(i = 0; i < 4; i++){ |
| 191 | printf("encoder(%d) => %d\n",i,encoder_bits(i)); |
| 192 | } |
| 193 | for(i = 0; i < 4; i++){ |
| 194 | printf("encoder_val(%d) => %d\n",i,(int)encoder_val(i)); |
| 195 | }*/ |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | #include "CAN.h" |
| 200 | |
| 201 | |
| 202 | |
| 203 | void motor(int32_t speed) |
| 204 | { |
| 205 | if (speed > 2047) speed = 2047; |
| 206 | if (speed < -2047) speed = -2047; |
| 207 | return; |
| 208 | if (speed > 0) { |
| 209 | MCPWM->MCMAT1 = 2047 - speed; |
| 210 | MCPWM->MCMAT0 = 2048; |
| 211 | } else { |
| 212 | MCPWM->MCMAT1 = 2048; |
| 213 | MCPWM->MCMAT0 = speed + 2047; |
| 214 | } |
| 215 | } |
| 216 | |
| 217 | |
| 218 | |
| 219 | /*-----------------------------------------------------------*/ |
| 220 | |
| 221 | int main(void) |
| 222 | { |
| 223 | // Configure the hardware |
| 224 | prvSetupHardware(); |
| 225 | |
| 226 | /* Start the standard demo tasks. These are just here to exercise the |
| 227 | kernel port and provide examples of how the FreeRTOS API can be used. */ |
| 228 | //vStartLEDFlashTasks(mainFLASH_TASK_PRIORITY); |
| 229 | |
| 230 | /* Create the USB task. */ |
| 231 | xTaskCreate(vUSBTask, (signed char *) "USB", configMINIMAL_STACK_SIZE + 1020, (void *) NULL, tskIDLE_PRIORITY + 3, NULL); |
| 232 | |
| 233 | xTaskCreate(vPrintPeriodic, (signed char *) "PRINTx", configMINIMAL_STACK_SIZE + 100, NULL, tskIDLE_PRIORITY + 2, NULL); |
| 234 | |
| 235 | initCAN(); |
| 236 | |
| 237 | // Start the scheduler. |
| 238 | vTaskStartScheduler(); |
| 239 | |
| 240 | /* Will only get here if there was insufficient memory to create the idle |
| 241 | task. The idle task is created within vTaskStartScheduler(). */ |
| 242 | for (;;); |
| 243 | } |
| 244 | /*-----------------------------------------------------------*/ |
| 245 | |
| 246 | |
| 247 | void vApplicationTickHook(void) |
| 248 | { |
| 249 | static unsigned long ulTicksSinceLastDisplay = 0; |
| 250 | |
| 251 | /* Called from every tick interrupt as described in the comments at the top |
| 252 | of this file. |
| 253 | |
| 254 | Have enough ticks passed to make it time to perform our health status |
| 255 | check again? */ |
| 256 | |
| 257 | ulTicksSinceLastDisplay++; |
| 258 | |
| 259 | if (ulTicksSinceLastDisplay >= mainCHECK_DELAY) { |
| 260 | /* Reset the counter so these checks run again in mainCHECK_DELAY |
| 261 | ticks time. */ |
| 262 | ulTicksSinceLastDisplay = 0; |
| 263 | } |
| 264 | } |
| 265 | /*-----------------------------------------------------------*/ |
| 266 | |
| 267 | void prvSetupHardware(void) |
| 268 | { |
| 269 | // Setup the peripherals. |
| 270 | // The CPU will be running at 100 MHz with a 12 MHz clock input. |
| 271 | |
| 272 | // Setup GPIO power. |
| 273 | SC->PCONP = PCONP_PCGPIO; |
| 274 | // Disable TPIU. |
| 275 | PINCON->PINSEL10 = 0; |
| 276 | |
| 277 | // Setup PLL0 so that the CPU runs at 100 MHz. |
| 278 | if (SC->PLL0STAT & (1 << 25)) { |
| 279 | /* Enable PLL, disconnected. */ |
| 280 | SC->PLL0CON = 1; |
| 281 | SC->PLL0FEED = PLLFEED_FEED1; |
| 282 | SC->PLL0FEED = PLLFEED_FEED2; |
| 283 | } |
| 284 | |
| 285 | // Disable PLL, disconnected. |
| 286 | SC->PLL0CON = 0; |
| 287 | SC->PLL0FEED = PLLFEED_FEED1; |
| 288 | SC->PLL0FEED = PLLFEED_FEED2; |
| 289 | |
| 290 | // Enable main OSC and wait until it's ready. |
| 291 | SC->SCS |= 0x20; |
| 292 | while (!(SC->SCS & 0x40)); |
| 293 | |
| 294 | // select main OSC, 12MHz, as the PLL clock source. |
| 295 | SC->CLKSRCSEL = 0x1; |
| 296 | |
| 297 | SC->PLL0CFG = 0x20031; |
| 298 | SC->PLL0FEED = PLLFEED_FEED1; |
| 299 | SC->PLL0FEED = PLLFEED_FEED2; |
| 300 | |
| 301 | // Enable PLL, disconnected. |
| 302 | SC->PLL0CON = 1; |
| 303 | SC->PLL0FEED = PLLFEED_FEED1; |
| 304 | SC->PLL0FEED = PLLFEED_FEED2; |
| 305 | |
| 306 | // Set clock divider. |
| 307 | SC->CCLKCFG = 0x03; |
| 308 | |
| 309 | // Configure flash accelerator. |
| 310 | SC->FLASHCFG = 0x403a; |
| 311 | |
| 312 | // Check lock bit status. |
| 313 | while (((SC->PLL0STAT & (1 << 26)) == 0)); |
| 314 | |
| 315 | // Enable and connect. |
| 316 | SC->PLL0CON = 3; |
| 317 | SC->PLL0FEED = PLLFEED_FEED1; |
| 318 | SC->PLL0FEED = PLLFEED_FEED2; |
| 319 | |
| 320 | while (((SC->PLL0STAT & (1 << 25)) == 0)); |
| 321 | |
| 322 | // Configure the clock for the USB. |
| 323 | if (SC->PLL1STAT & (1 << 9)) { |
| 324 | // Enable PLL, disconnected. |
| 325 | SC->PLL1CON = 1; |
| 326 | SC->PLL1FEED = PLLFEED_FEED1; |
| 327 | SC->PLL1FEED = PLLFEED_FEED2; |
| 328 | } |
| 329 | |
| 330 | // Disable PLL, disconnected. |
| 331 | SC->PLL1CON = 0; |
| 332 | SC->PLL1FEED = PLLFEED_FEED1; |
| 333 | SC->PLL1FEED = PLLFEED_FEED2; |
| 334 | |
| 335 | SC->PLL1CFG = 0x23; |
| 336 | SC->PLL1FEED = PLLFEED_FEED1; |
| 337 | SC->PLL1FEED = PLLFEED_FEED2; |
| 338 | |
| 339 | /* Enable PLL, disconnected. */ |
| 340 | SC->PLL1CON = 1; |
| 341 | SC->PLL1FEED = PLLFEED_FEED1; |
| 342 | SC->PLL1FEED = PLLFEED_FEED2; |
| 343 | while (((SC->PLL1STAT & (1 << 10)) == 0)); |
| 344 | |
| 345 | /* Enable and connect. */ |
| 346 | SC->PLL1CON = 3; |
| 347 | SC->PLL1FEED = PLLFEED_FEED1; |
| 348 | SC->PLL1FEED = PLLFEED_FEED2; |
| 349 | while (((SC->PLL1STAT & (1 << 9)) == 0)); |
| 350 | |
| 351 | // Setup the peripheral bus to be the same as the CCLK, 100 MHz. |
| 352 | // Set CAN to run at CCLK/6, which should have it running about 1 Mbit (1.042) |
| 353 | SC->PCLKSEL0 = 0xff555555; |
| 354 | |
| 355 | /* Configure the LEDs. */ |
| 356 | vParTestInitialise(); |
| 357 | } |
| 358 | /*-----------------------------------------------------------*/ |
| 359 | |
| 360 | void vApplicationStackOverflowHook(xTaskHandle *pxTask, signed char *pcTaskName) |
| 361 | { |
| 362 | /* This function will get called if a task overflows its stack. */ |
| 363 | |
| 364 | (void) pxTask; |
| 365 | (void) pcTaskName; |
| 366 | |
| 367 | for (;;); |
| 368 | } |
| 369 | /*-----------------------------------------------------------*/ |
| 370 | |
| 371 | void vConfigureTimerForRunTimeStats(void) |
| 372 | { |
| 373 | const unsigned long TCR_COUNT_RESET = 2, CTCR_CTM_TIMER = 0x00, TCR_COUNT_ENABLE = 0x01; |
| 374 | |
| 375 | /* This function configures a timer that is used as the time base when |
| 376 | collecting run time statistical information - basically the percentage |
| 377 | of CPU time that each task is utilising. It is called automatically when |
| 378 | the scheduler is started (assuming configGENERATE_RUN_TIME_STATS is set |
| 379 | to 1). */ |
| 380 | |
| 381 | /* Power up and feed the timer. */ |
| 382 | SC->PCONP |= 0x02UL; |
| 383 | SC->PCLKSEL0 = (SC->PCLKSEL0 & (~(0x3 << 2))) | (0x01 << 2); |
| 384 | |
| 385 | /* Reset Timer 0 */ |
| 386 | TIM0->TCR = TCR_COUNT_RESET; |
| 387 | |
| 388 | /* Just count up. */ |
| 389 | TIM0->CTCR = CTCR_CTM_TIMER; |
| 390 | |
| 391 | /* Prescale to a frequency that is good enough to get a decent resolution, |
| 392 | but not too fast so as to overflow all the time. */ |
| 393 | TIM0->PR = (configCPU_CLOCK_HZ / 10000UL) - 1UL; |
| 394 | |
| 395 | /* Start the counter. */ |
| 396 | TIM0->TCR = TCR_COUNT_ENABLE; |
| 397 | } |
| 398 | |