Good day!
I barely reconciled myself to the idea that I will not get a development board very soon it suddenly arrived Today we finally get to know the hero of my project the FRDM-KW41ZFRDM-KW41Z board
Items received
Let's start with the announcement of the list of items that I ordered:
- Development board FRDM_KW41Z [NXP].
- Hot air gun [Bosch].
- Electric screwdriver [Bosch].
- LCD display [Adafruit].
Due to some circumstances, I have not received the last two items from this list yet (I'm sure that their receipt is just a matter of time), but this will not prevent me from continuing the project.
The hot air gun I ordered is necessary for me to work with plastic. When choosing I was guided by the following criteria:
- a well-known and reliable brand (in Russia everybody knows and loves Bosch);
- low price (this hot air gun cost less than $50);
- relatively high power (1600 W);
- possibility of power adjustment;
- possibility of installing additional attachments.
I was a little surprised by the fact that one of our contest participants fvan uses exactly the same hot air gun, it turns out that the engineers ideas converge!
Now for the development board For me it was a surprise that FRDM-KW41ZFRDM-KW41Z is not a development board but two development boards in one package!
In this regard I have several ways to further develop the project:
- Use only one KW41Z in the computer.
- Use one KW41Z to work with Bluetooth and another KW41Z for working with LCD and sensors.
- Use KW41Z to work with Bluetooth and K64F for working with LCD and sensors.
First experiment with KW41Z: Keil
To implement solutions (1) and (2), it is required to write software for KW41Z using the Keil environment. As I said Keil does not have a full SDK from NXP, for this reason I needed to integrate the SDK components manually. The most difficult aspect (as though it did not seem strange) of this task was writing the custom linker configuration file:
#! armcc -E // ----------------------------------------------- Custom linker for FRDM-KW41Z by Yuri Tikhonov // Created on NXP samples // ----------------------------------------------- #if (defined(__ram_vector_table__)) #define __ram_vector_table_size__ 0x00000200 #else #define __ram_vector_table_size__ 0x00000000 #endif #define m_interrupts_start 0x00000000 #define m_interrupts_size 0x00000200 #define m_flash_config_start 0x00000400 #define m_flash_config_size 0x00000010 #define m_text_start 0x00000410 #define m_text_size 0x0007FBF0 #define m_interrupts_ram_start 0x1FFF8000 #define m_interrupts_ram_size __ram_vector_table_size__ #define m_data_start m_interrupts_ram_start + __ram_vector_table_size__ #define m_data_size 0x20018000 - m_data_start /* Sizes */ #if (defined(__stack_size__)) #define Stack_Size __stack_size__ #else #define Stack_Size 0x0400 #endif #if (defined(__heap_size__)) #define Heap_Size __heap_size__ #else #define Heap_Size 0x0400 #endif LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_start ; load region size_region { VECTOR_ROM m_interrupts_start m_interrupts_size ; load address = execution address { * (RESET,+First) } ER_m_flash_config m_flash_config_start FIXED m_flash_config_size ; load address = execution address { .ANY (FlashConfig) } ER_m_text m_text_start m_text_size ; load address = execution address { * (InRoot$$Sections) .ANY (+RO) } VECTOR_RAM m_interrupts_ram_start m_interrupts_ram_size ; edition by Yuri { .ANY (.m_interrupts_ram) } RW_m_data m_data_start m_data_size ; RW data { .ANY (+RW +ZI) } }
Another thing I had to tinker with just like in the case of [K64F] – was a problem with the DAP-Link Debugger, but today everything was much simpler: you just had to choose J-LINK instead of DAP Debugger in Keil's settings and it all worked right away!
Having coped with all the problems, I was able to write my first program for KW41Z, the main function of which is listed below, and the full listings are available in the [official repository] of the project in [KW41Z] branch.
// main function int main(void) { // initialization of peripheral gpio::init(); BOARD_BootClockRUN(); // infinity loop for(;;) { delay(); gpio::write(GREEN, true); delay(); gpio::write(GREEN, false); } }
Having written this simple program, I realized that porting the SDK is not a quick thing and it will take me a long time to implement the full operation with Bluetooth. In order to overcome this problem at the current stage, I decided to follow the way (3) and continue to use the already practical ready-made project for K64F together with KW41Z, while implementing in KW41Z only a small set of functions that support Bluetooth on the basis of a demo project from SDK.
The problem is that I can compile this project (from KW41Z) only in MCUExpresso IDE or IAR. Given that I did not use IAR before, my choice fell on MCUExpresso.
Second experiment with KW41Z: MCUExpresso IDE
As a basis, I used a demo-project of a bike speedometer program called cycling_speed_cadence_sensor. A little deeper into the research, I found out that in this application there are four parameters that I need to set in accordance with the actual values of training process:
- measurement.cumulativeCrankRevs – total number of turns of pedals;
- measurement.lastCrankEventTime – time of the last measurement of pedal rotation [ms];
- measurement.cumulativeWheelRevs – the total number of wheel turns;
- measurement.lastWheelEventTime – time of last measurement of wheel rotation [ms].
The last two values I decided not to fill in yet and focus only on the first two. The parameter measurement.lastCrankEventTime in the demo program is already being processed: it is incremented by 1024 each time the corresponding function is called (i.e., pedal turns are determined approximately 1 time per second).
It remains to define measurement.cumulativeCrankRevs and here the problem arises: if the number of revolutions is an integer, then the measurement accuracy will be horrendous ±60 RPM! In order to cope with this problem, I decided to record in this variable not just the speed of pedal rotation, but the speed of pedal rotation multiplied by 100, so the measurement accuracy is ±0.6 RPM. Another idea occurred to me that instead of speed (N) I can output the speed in km/h to the program, for this, assuming [as we did before] that the rider is traveling in the 8th gear (G = 8), the following calculation is required:
measurement.cumulativeCrankRevs = N * 100 / 2.5
There remains only one question: where do we take the variable N from I decided to transfer it from the FRDM-K64FFRDM-K64F board via UART while ensuring the synchronization of receiving and sending packets I will not go into details but I'll just show you the code of my solution
// timer interrupt handler static void TimerMeasurementCallback(void * pParam) { cscsMeasurement_t measurement; if ( cscsServiceConfig.calibrationInProgress) { Cscs_FinishSensorCalibration(&cscsServiceConfig, TRUE); } // ---------------------------------------------------------------------------- static unsigned char last_value = 0, count = 0; static long long time = 0; unsigned char value = 0xff; // led indication Led3Toggle(); // synchronization LPUART_WriteBlocking(DEMO_LPUART, &value, 1); // read data excluding invalid packets while (value == 0xff) LPUART_ReadBlocking(DEMO_LPUART, &value, 1); // data recognition and correction if (value < last_value) time += 0xff; if (value == last_value) count ++; if (value != last_value || count > 3) { count = 0; lastEventTime += 1024; // time variable [ms] } // turns count variable mCscsUserData.cumulativeCrankRevs = time + (long long)value; last_value = value; Led3Toggle(); // ---------------------------------------------------------------------------- // save data measurement.crankRevDataPresent = TRUE; measurement.cumulativeCrankRevs = mCscsUserData.cumulativeCrankRevs; measurement.lastCrankEventTime = lastEventTime; measurement.wheelRevDataPresent = TRUE; measurement.cumulativeWheelRevs = mCscsUserData.cumulativeWheelRevs; measurement.lastWheelEventTime = lastEventTime; Cscs_RecordMeasurement(service_csc, &measurement); }
Adjustment of K64F firmware
In order to combine KW41Z and K64F, it took me a little bit of work to improve the bike computer's program: I added to the program an additional RTOS thread called interconnect, which is responsible for communicating with the KW41Z board via the UART interface.
I got the following algorithm for working with UART:
- In the sensors thread in real time, the calculation of the variable N (which is called tcnt) is performed, the calculation is performed at each turn of the pedals.
- The interconnect thread waits for a UART request from KW41Z.
- As soon as the request for data arrives, a value generated on the basis of tcnt is transferred to KW41Z, the specified conversion is necessary to reduce the data flow between the MCUs.
Simplified interconnect and sensors threads are listed below
// second thread: for work with sensors static void sensors(void const *args); osThreadDef(sensors, osPriorityNormal, 1, 0); static void sensors(void const *args) { static char tcnt_flag = 0; // flag for Bluetooth feature char state = 0; // current state of reed float clc_speed = 0; // calculated speed // calculation of the distance (rotation count) and wavelength for (;;) { if (!state && gpio::read(REED)) { // .. code ... tcnt_flag = 1; } else if (state && !gpio::read(REED)) { // .. code ... } // .. code ... // if it's a new turn then we convert speed to KW41Z format if (tcnt_flag == 1) { tcnt += clc_speed * 20.5f / 60.0f * 5.0f; tcnt_flag = 0; } } } // forth thread: for work with interconnection static void interconnect(void const *args); osThreadDef(interconnect, osPriorityNormal, 1, 0); static void interconnect(void const *args) { for(;;) { unsigned char value; // we waiting for request UART_ReadBlocking(UART3, &value, 1); // convert tcnt to 'short' format value = tcnt % 0xff; // send data to KW41Z UART_WriteBlocking(UART3, &value, 1); } }
Testing of the device
To conduct the tests, I collected all the components of the bike computer on my desk and as experience I applied to this system by simulating a reed sensors signal with a frequency of ~1 Hz:
For today's experiments, I installed the NXP IoT Toolbox on my smartphone Samsung Galaxy A5. This application almost immediately started working with my bike computer, so I did not need any further work on this part:
As you can see, the discrepancy between the speed values on the bike computer screen (22.97 km/h) and the mobile application screen (23.40 km/h) is 0.43 km/h, which is below the maximum permissible error, which we mentioned above.
Conclusion
Today I finally got my FRDM-KW41ZFRDM-KW41Z and in a very short time I managed to do a lot of work to integrate Bluetooth into my program! Now I'm a little worried about the appearance of my computer: because of the abundance of details, I just can not fix it on exercise bike, so next week I plan to tackle this issue.
Nevertheless, the most important part of the project is finally completed and now I have no doubt that the project will be completed on time, and if within the next two weeks I receive all the missing items of my order, things will go even better!
Thanks for reading and have a nice day!
Top Comments