Jump to content

leomar01

Members
  • Content Count

    42
  • Joined

  • Last visited

  1. Hello @@JapiMostert, I just read this thread and your questions. Since I just finished my masters degree, I can well imagine how overwhelmed you feel facing the requirements of your project. As it's already been said, try to draw a graph of the components, how they interact and which functions you want to accomplish. Don't try to overaccomplish what's asked. Try to find the easiest ways. Don't be afraid of the term "data logging". If you just send the time of the stopwatch over UART, that's everything you need. On the MSP430G2 launchpad you have a UART->USB bridge (just in case you didn't know that already). On your computer you just open a serial console / terminal program like "hterm". Everything your MSP430 sends over UART will be on your computer screen. Actually that's allready some sort of "data logging". Then, you'll just save the output to a text file (hterm has a buttong "save output") and you're done. You don't need a dedicated RTC module. The launchpad has a watch crystal on boad (actually you maybe have to solder it on). I think that's the reason why your professor thinks this wohle project is logical and easy to do. Regarding the button debuncing: adding a capacitor would do the trick and will propably save you some headache with software debouncing. Since you are using CCS you could also use "Grace" to configure things like UART peripheral, timers, button interrupts, clocks etc. Regards, Leo
  2. It executes the c-files that are being generated out of the grace.cfg during the compile process. Looking through the project settings I can't find where this gets done during compiling...
  3. I didn't know the grace config only depends on the raw text in that single cfg file, thanks @@roadrunner84 :-) I just copied the default main.cfg and renamed the second file. How does CCS determine what cfg file to use (when renamed)? Or does it just use the first *.cfg file it finds?
  4. that's a good point, thanks @@roadrunner84 If I change the check to "eventArray[i].nextTime <= millis()" the problem is also gone and saves the 4 bytes for another variable ;-) I just had a look at Energia source code. Energia spends quite some time to do the millis(); alone: __attribute__((interrupt(WDT_VECTOR))) void watchdog_isr (void) { // copy these to local variables so they can be stored in registers // (volatile variables must be read from memory on every access) unsigned long m = wdt_millis; unsigned int f = wdt_fract; m += sleeping ? SMILLIS_INC:MILLIS_INC; f += sleeping ? SFRACT_INC:FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1; } wdt_fract = f; wdt_millis = m; wdt_overflow_count++; /* Exit from LMP3 on reti (this includes LMP0) */ __bic_status_register_on_exit(LPM3_bits); } I measured my current implementation to be 97,7% of the time in sleep state. Not optimal, but ok. Actually, in my real use case low power won't matter that much, rather the ability to do other things in the meantime without having to worry about the timings. Hence, the ability to stop the whole event timer engine and go completely down to LPM4 until a external interrupt from the wireless module fires. To sum it all up, I'm aiming on compiling my own library of portable code snippets to get such things done the easier way in future (see my portable ringbuffer approach).
  5. Erm, I think I've got it: EventTimer.h /* * EventTimer.h * * Created on: 07.07.2014 * Author: Leo_2 */ #include <msp430.h> #include "../types.h" #ifndef EVENTTIMER_H_ #define EVENTTIMER_H_ #define MAXEVENTS 10 #define EVENTTIMER_TxR TBR // Timer_B counter #define EVENTTIMER_CTL TBCTL // Timer_B control #define EVENTTIMER_MC0 MC0 // Mode control 0 #define EVENTTIMER_MC1 MC1 // Mode control 1 int AddTimerEvent(void (*function)(void), unsigned int millis, bool infinite); void initEventTimer(void); void startEventTimer(void); void stopEventTimer(void); bool getTimerRunning(void); unsigned long millis(void); void incMillis(void); void workEvents(void); unsigned int deleteEvent(void (*function)(void)); #endif /* EVENTTIMER_H_ */ EventTimer.c #include "EventTimer.h" int getFreeArrayIndex(void); unsigned long milliseconds; // 2^32 -> max. ~49 Days // struct that holds events struct timedEvent{ void (*function)(void); // pointer to function to be executed unsigned long nextTime; // time when function will be executed next bool infinite; // will it be executed over and over? unsigned int interval; // time interval between executions }eventArray[MAXEVENTS]; // array of timedEvent-structs void initEventTimer(void) { milliseconds = 0; for(int i=0; i<MAXEVENTS; i++) { eventArray[i].function = NULL; eventArray[i].infinite = false; eventArray[i].nextTime = 0; eventArray[i].interval = 0; } } void incMillis(void) { milliseconds++; } void startEventTimer(void) { // reset counter of timer EVENTTIMER_TxR = 0; // start timer in up mode EVENTTIMER_CTL |= (EVENTTIMER_MC0); } void stopEventTimer(void) { // switch off timer to conserve power EVENTTIMER_CTL &= ~(EVENTTIMER_MC0 | EVENTTIMER_MC1); } unsigned long millis(void) { return milliseconds; } int getFreeArrayIndex(void) { for(int i=0; i < MAXEVENTS; i++) { if(eventArray[i].function == NULL) { return i; } } // we didn't find a free spot return -1; } void workEvents(void) { for(int i=0; i < MAXEVENTS; i++) { if(eventArray[i].nextTime == millis()) { eventArray[i].function(); if(eventArray[i].infinite == true) { eventArray[i].nextTime = millis() + eventArray[i].interval; } else { eventArray[i].function = NULL; eventArray[i].nextTime = 0; } } } } unsigned int deleteEvent(void (*function)(void)) { for(int i=0; i < MAXEVENTS; i++) { if(eventArray[i].function == function) { eventArray[i].function = NULL; eventArray[i].nextTime = 0; return 0; } } return 1; } int AddTimerEvent(void (*function)(void), unsigned int mils, bool infinite) { int arrayIndex = getFreeArrayIndex(); if(arrayIndex < 0) { return -1; } eventArray[arrayIndex].function = function; eventArray[arrayIndex].interval = mils; eventArray[arrayIndex].nextTime = millis() + mils; eventArray[arrayIndex].infinite = infinite; return 0; } bool getTimerRunning(void) { return (EVENTTIMER_CTL & (EVENTTIMER_MC0 | EVENTTIMER_MC1)); } I configured Timer B in Grace like this: The corresponding ISR looks like this: /* * ======== Timer_B3 Interrupt Service Routine ======== */ #pragma vector=TIMERB0_VECTOR __interrupt void TIMERB0_ISR_HOOK(void) { /* USER CODE START (section: TIMERB0_ISR_HOOK) */ incMillis(); workEvents(); /* USER CODE END (section: TIMERB0_ISR_HOOK) */ } Usage: int main(void) { Grace_init(); // Activate Grace-generated configuration initEventTimer(); startEventTimer(); AddTimerEvent(tuerAuf_toggle,500,true); AddTimerEvent(tuerZu_toggle,650,true); AddTimerEvent(blinkerLinks_toggle,730,true); AddTimerEvent(blinkerRechts_toggle,970,true); _bis_SR_register(LPM0_bits|GIE); } These 4 functions just toggle output pins like so: Don't know how embedding a Youtube video works, here's the link. Regards, Leo PS: maybe worth moving this into the code vault?
  6. Hi there, I'm in the process of developing some wireless system. I want to have more than one transmitter - behaving just a little bit different from each other. Using Code Composer Studio I've discovered the ability to have different "build configurations". I made three different "main.c" files: main_transmitter1.c main_transmitter2.c main_transmitter3.c In build config 1 the second and third are excluded from build. In build config 2 the first and third are excluded... I think you get it. Now my question: To configure the peripherals I used Grace. Now I have a new idea where I need to change some of that Grace configuration in transmitter1 but not in the others. Is there a possibility to make the Grace config dependent of the current build config? Regards, Leo
  7. Hi there, I've got something in my mind and need your advice Let's imagine we've got 8 LED's and 8 buttons attached to MSP430. (actually, it doesn't matter how many LED's or buttons, or which port they're attached to) We want a solution where a press of button one causes the first LED to light up for 1 second. After that time it'll switch off automatically. For now that's nothing special. Now, let's say every LED lights up when the corresponding button has been pressed, but every one of these LEDs switches off after a different amount of time. Moreover this whole mechanism should be independent of when which button has been pressed. It should be somehow interrupt-driven and low power (using low power modes between the interrupts). OR: use the spare processing power inbetween status changes for something else. My first thoughts were: 1. functions for each LED like "LED1_on();", "LED1_off();" etc. 2. a timer function like "millis();" in Arduino IDE 3. a array or struct that will hold a function pointer and the amount of time (millis) it should take until the function will be executed 4. a function to allocate i.e. "LED1_off();" to this array with the desired amount of time 5. timer-ISR checks every ms if there exists a function in the array that needs to be serviced and if so, executes it my program could look something like this: if(button1 pressed) { LED1_on(); AddTimerEvent(LED1_off, 500); // after 500ms it should "fire" } // move on doing other stuff Adding this to a port ISR makes it completely independet of my main program code. What do you think about my approach? Any advice? Does it already exist and I missed it? Regards, Leo PS: I have to admit I never used function pointers. I only read "The C programming language" (Kernighan & Richie) some days ago and thought this would be a good match.
  8. Thanks @@RobG, I added that throughout my library. Up until now I'm happy with my ringbuffer implementation. Didn't encounter any issues by now Leo
  9. Thanks @@zlalanne, actually he has a lot of fifo implementations, most of them are hard to understand to me, but here's one that's pretty close to mine: http://users.ece.utexas.edu/~valvano/arm/FIFO.c I think I'll leave my implementation like it is ... until I discover a new problem ;-) Leo
  10. Hello @@igor, I had another night of trial and error As you correctly pointed out, the first byte got discarded by BufferOut. That was my solution to overcome the problem caused by directly transmitting the first byte in buffered_send_SPI. I have corrected this behaviour and again, had the problem of first byte getting sent double. Then I discovered the UCBUSY bit in UCxSTAT. Instead of directly sending the first byte to get the peripheral running... else if(in == send_toSPI_buffer.index_reading) { //IFG2 |= UCB0TXIFG; UCB0TXBUF = ch; } ... I fired an artificial interrupt to get things going: if(!(UCA0STAT & UCBUSY)) { // ...initiate an artificial interrupt to get things going IFG2 |= UCB0TXIFG; } This seemed to work well until I tested this ISR: #pragma vector=USCIAB0RX_VECTOR __interrupt void USCI0RX_ISR_HOOK(void) { /* USER CODE START (section: USCI0RX_ISR_HOOK) */ DEBUGPIN1_HIGH unsigned char receivedByte; // UART RX Interrupt if (IFG2 & UCA0RXIFG) { receivedByte = UCA0RXBUF; buffered_send_byte_over_UART(receivedByte); buffered_send_byte_over_SPI(receivedByte); IFG2 &= ~UCA0RXIFG; } } Main thing in this example is: The through UART received char gets directly transmitted through that whole ringbuffer logic. The result looked like this: Sometimes it even looked more weird: This happens because UCBUSY is 1 while the peripheral is TXing OR RXing .... This led me to add another variable to the RINGBUFFER struct to indicate if currently a transmission is running: typedef struct ringbuffer{ unsigned char ringbuffer[BUFFERLENGTH]; unsigned int index_reading; // indicates the field to be read next unsigned int index_writing; // indicates field to be written to next bool ringbuffer_running; // indicates if output peripheral is allready running }RINGBUFFER; The corresponding part in buffered_send_SPI changed to this (here I think is the first time I clearly have to disable the interrupts): if(!send_toSPI_buffer.ringbuffer_running) { // ...initiate an artificial interrupt to get things going IFG2 |= UCB0TXIFG; send_toSPI_buffer.ringbuffer_running = true; } In BufferOut it now looks like this: if(buffer->index_reading == buffer->index_writing) { buffer->ringbuffer_running = false; // buffer is empty, nothing to read return 1; } Now I get the intended behaviour: Now the ringbuffer logic is working with a interrupt triggered peripheral and also "standalone" in code. As you mentioned I've also added a few accompanying functions: unsigned char CountFreeBytesInBuffer(RINGBUFFER *buffer); bool IsBufferEmpty(RINGBUFFER *buffer); int printstrBuffer(unsigned char *str, RINGBUFFER *buffer); int ReadBuffer(unsigned char *dest, RINGBUFFER *buffer); I'm getting towards a more portable solution ... as I intended in first place Comments and suggestions welcome Regards Leo PS: I've attached source files ringbuffer.c ringbuffer.h types.h SPI.c SPI.h UART.c InterruptVectors_init_clean.c
  11. Hello @@igor, up until now I only used it to send data over a peripheral like SPI or UART. In this case only the ISR triggers BufferOut(). If the buffer is empty no new value gets written in the output buffer register of the peripheral. Thereby no ISR will fire anymore and the two indices will remain the same. Thats the reason why BufferIn() checks if the two indices were the same and if so, writes the current char directly to the register of the peripheral. If only one char has been sent to the buffer the ISR again will do nothing except increasing the reading index the thereby leading to same indices. If on the other hand the buffer is full, BufferIn() will discard the char. At this point actually on char of the buffer gets wasted if I'm not mistaken Thanks for the wiki link, I've read it in the beginning, but maybe it's worth a reread with my in the meantime gained knowledge Leo
  12. I read that wikipedia article - I just can't make the transition to my actual case I'm now adding my current code (please excuse the german comments). First I have a layer that abstracts the ringbuffer logic: ringbuffer.h: #ifndef RINGBUFFER_H_#define RINGBUFFER_H_#define BUFFERLENGTH 64 // Must be 2^n#define BUFFER_MASK (BUFFERLENGTH-1) // Klammern auf keinen Fall vergessentypedef struct ringbuffer{ unsigned char ringbuffer[BUFFERLENGTH]; unsigned int index_reading; // zeigt auf das Feld das zuletzt gelesen wurde unsigned int index_writing; // zeigt auf das Feld in welches das n
  13. Hello, the last two days I've implemented a portable ringbuffer for use with UART, SPI or even different microcontrollers (minor changes required). I grabbed some ideas from here and there and mixed it all together in my own solution. I tested the functionality with some code that writes in unregular time distances into the buffer while the SPI TX interrupt is reading from it. It _seems_ to work well, but a friend of mine told me I definately have to deactivate the interrupt in "critical sections" of my code. Now I'm a little bit stuck. How do I identify what a critical section is? I don't want you guys to review my code. I just wanted to ask if there's a general approach on identifying such possible problems. My friend also said I should draw a timing diagram on what's going on, but again, I have no idea how you do that. Any advice? Best regards, Leo
  14. Wow thanks for the quick answer oPossum I think I've misunderstood the purpose of UCxSTE. On the eZ430-RF2500T target board UCB0STE (Port 3.0) is connected to CSn of the CC2500. Therefore I thought UCxSTE is some kind of auto hardware controlled chip select that's being driven low when transmitting. Ok, if got it right, I have to use 3-pin mode and use P3.0 as a normal output that I manually drive low during communication with the attached cc2500, right? Leo
  15. Hello, after years of abstinence I'm back to playing with TI hardware. I'm using the eZ430-RF2500 Development Tool with CCS and Grace. While implementing a ringbuffer for SPI I ran into an unexpected behaviour. (using USCI_B ) By writing a byte to UCB0TXBUF the UCB0TXIFG Interrupt should fire, right? Ok, I made a minimal example with Grace, only clocks and the SPI interface configured. I placed a _NOP(); in the ISR and a breakpoint on it. In main I add the line UCB0TXBUF = 'a'; When the SPI interface is configured as 3-Pin it does halt at the breakpoint. If I configure it as 4-Pin it never reaches the ISR. Can someone explain that behaviour? Regards, Leo PS: I've attached my testproject Testprojekt3.zip
×
×
  • Create New...