Note: This tutorial assumes you have done the previous tutorials as it builds upon them. If you have not done the previous ones, please go back and do so.
-----------------------------------------------------------------------------------------------------
IntroNow that you have successfully set up the basic BLE connection from the previous tutorial, let’s do something a bit more complex.
This guide will show you how to build upon the previous project. If you want to keep the previous project AND also build upon its progress, follow the following steps:
- In the Projects tab in the upper LHS, right-click on your previous project and select Copy
- Name the new project and select the project location
- Click Copy
Click the Clean & Build button to check there are no errors before proceeding further.
If you get the warning: <filename>.hex does not exist or is not an executable
Right-click on the project again and select Properties, which will open the following popup.
Check the following are selected
- Part number (PIC16LF18456)
- Hardware Tool (will be the Serial Number of your PIC-BLE)
- Compiler is set to XC8
Click Apply and then click OK. Then click the Clean & Build button before proceeding further.
Configure Device Driver APIsTo operate the Buzzer Click board, you will need to use a PWM (Pulse Width Modulation) peripheral.
Open MCC and in the Device Resources pane (middle pane on LHS) double-click on PWM. Now click on the green ‘+’ next to PWM6 as shown below.
This will add a PWM peripheral to your Project Resources pane.
Now in the bottom pane, you should see in the Notifications tab a warning stating: Configure Timer2 for PWM Module.
In the Device Resources pane add a TMR2 peripheral. This is needed to set up the PWM module.
The Buzzer Click board uses a Piezoelectric speaker to create sound. This piezo has a resonant frequency of 3.8kHz. To get maximum volume from it the frequency generated by the PWM module should be close to the resonant frequency.
So, the period of that signal needs to be calculated. You may recall that the period of a signal is the inverse of its frequency as shown below.
where T is the period of the signal
wheref is the frequency of the signal
Now substituting in the value of the frequency into the equation gives the following equation and solution.
In the TMR2 tab in the main pane, you will be able to set the Timer Period.
For the Clock Source dropdown box, select FOSC/4. Then in the Timer Period field type “263 us” and hit the Tab key on your keyboard.
Your TMR2 setup should look like the following picture.
Note: The actual period that will be used (264 us) is indicated by the green arrow, which correlates to a frequency of 3.788 kHz, which is fine for the purposes of this project.
First to decide where the PWM signal needs to be routed to, on the Click Buzzer board find the PWM pin as shown below.
PIC devices allow the user to route signals to the pin they need using PPS (Peripheral Pin Select). This is one of the most powerful aspects of these devices. It cannot be understated how useful this is.
Lining up the Click boards pins with the MikroBus on the PIC-BLE board, identify the pin that will connect to the Buzzer Click PWM pin as shown in Figure 5 below. An easy way to do this is to check the GND pins are on the same side for both the PIC-BLE and the Buzzer Click.
So, you can see that the pin required for the Buzzer Click is RA4.
Now in MPLABX, in the lower pane select the Pin Manager:Grid View tab. As shown below you will see that the PWM6 output is not available for the PORT A pins.
So now you will get to see how powerful the CLC (Configurable Logic Cell) is.
In the Device Resources pane on the LHS, click on CLC and then click the green ‘+’ next to CLC1 as shown below.
In the main pane you will see a logic circuit appear. Set up the CLC1 peripheral as shown below.
1 Select 4-input AND from the dropdown box
2 Select PWM6_OUT
3 Click on the input pin of the OR gate to connect the PWM6_OUT to the gate
4 Click on the output of the 2nd OR gate so a bubble appears
5 Click on the output of the 3rd OR gate so a bubble appears
6 Click on the output of the 4th OR gate so a bubble appears
Now in the lower pane click on the Pin Manager : Grid View tab and click on the Lock symbol corresponding to CLC1OUT and pin RA4. The lock square for that pin/peripheral combination will turn green as shown below.
Now click on the Pin Module tab in the main pane. Your setup should look like the picture below.
Finally, in the upper LHS pane, click on the Resource Management tab and click the Generate button as shown below.
This will automatically generate code for the peripherals you have just set up. Wait until you see the Generation Complete message in the lower pane as shown below.
Now you are ready to write a little code!
Write the CodeIn the upper LHS pane, click the arrow next to Source Files and then double clickon main.c
This will open the main file in the main pane. Since you copied the Hello World project you will see the code you wrote for that. Start by commenting out (or deleting) the code you wrote from Hello World. Then in the void main(void) function after the SYSTEM_Initialize(); function call, start typing the following:
PWM6_
Then hold down the CTRL key and press SPACEBAR. You will see some options as shown below.
Select the circled entry and the function will autocomplete for you, leaving the cursor between the parentheses.
Select a value, I chose 131 and enter that inside the parentheses. Then don’t forget a semicolon at the end of the line!
So, where did I get the value 131?Good question!If you click on the Projects tab in the upper left pane, then double-click on Header Files, then double-click on MCC Generated Files, then double-click on pwm6.h the header file for the PWM peripheral will open. Before the functions are defined you should see a line:
#define PWM6_INITIALIZE_DUTY_VALUE 131
That value was generated in the MCC setup of the PWM6 peripheral from the Duty Cycle field as shown below. You will see the calculated value of 131 just below the Duty Cycle field.
So instead of having to dive into the datasheet and find the equations to calculate the number of bits that the PWM6 peripheral would have at that frequency and then calculate what a 50% duty cycle value would be, MCC takes care of it for you.
Pretty neat huh?!Plug the Buzzer Click onto the PIC-BLE board taking care to ensure correct orientation. The GND pins of the PIC-BLE should connect to the GND pins of the Buzzer Click.
Click the Clean & Build button on the toolbar and if no errors are found, click the Make & Program Device button on the toolbar as shown below.
Note: You can just click the Make and Program Device button as it builds the project first and will stop if it finds any errors.
Once it programs the device, you should hear the buzzer emitting a continuous tone.
Now you are ready to make it beep when sent commands across the Serial Terminal app!
In the main.c file, first you need to modify the line you added earlier.
Change the line:
PWM6_LoadDutyValue(131);
To:
PWM6_LoadDutyValue(0);
This will ensure that when the program starts the buzzer is in the OFF state.
Now inside the while(1) loop we are going to read from the EUSART1 peripheral and depending on what is received, the buzzer will either turn on or off.
Add the following code:
uint8_t byte_recvd = EUSART1_Read();
switch (byte_recvd) {
case 0x1:
PWM6_LoadDutyValue(131);
__delay_ms(5000);
break;
case 0x0:
PWM6_LoadDutyValue(0);
break;
default:
PWM6_LoadDutyValue(0);
break;
}
What does the above code do?
First it creates an unsigned 8-bit integer variable called byte_recvd and stores in it the data read from the EUSART1 peripheral. Then it performs a switch case statement on the variable byte_recvd. If it is a ‘1’ in HEX, then turn the buzzer on for 5000 ms (5 seconds), if it is a ‘0’ in HEX turn it off. There is also a default condition to catch anything else that might be sent to the PIC-BLE. In those cases it turns the buzzer off.
Now click the Clean & Build button to ensure there are no errors, then click the Make & Program Device button to program the PIC-BLE with the new code. If after downloading the code onto the PIC-BLE the buzzer sounds continuously, unplug the PIC-BLE and plug it back in.
Before you start sending 1’s and 0’s, you need to set up the app to send HEX values since that’s what the switch case statement is looking for.
For the Android app refer to the picture below, tap on the 3 horizontal lines in the top left of the app, select Settings and then Send. Tap on Edit Mode and click the Radio Button for HEX.
For the iOS app refer to the picture below. To the right of the textfield is a blue button labelled Send ASCII. Long press on the button and then tap the radio button next to HEX, then click Save.
Once you have set up your phone’s app for sending HEX values, enter a ‘1’ in the send textfield and tap the Send button. You should hear it beep continuously for 5 seconds. If you send a ‘0’, it will not beep. If you send anything other than ‘1’ or ‘0’, the default case in the switch case statement will catch that and not beep.
Both the Android and iOS apps have programmable buttons (at the bottom of the screen) that you can set up to send either a ‘1’ or a ‘0’. You can also give them custom names like ON and OFF. Just long press on the buttons to set them up.
Bonus Round!You can set up a custom beeping pattern when the PIC-BLE receives a ‘1’ in HEX. Here’s an example where it will send “SOS” in Morse code. Morse code for SOS is: 3 shorts beeps, 3 long beeps, 3 short beeps.
So, you can create a function that does 3 short beeps, a function that does 3 long beeps, and a function that puts them together to send “SOS”.
After the end of the main function in the main.c file, create a new function as shown below:
// Does 3 long beeps of 1/2 second each with 0.1 second gap in between
// Adds extra gap at the end of 0.1 second to make it easier to hear
void longBeeps() {
for (int i = 0; i < 3; i++) {
PWM6_LoadDutyValue(131);
__delay_ms(500);
PWM6_LoadDutyValue(0);
__delay_ms(100);
}
__delay_ms(100);
}
The above code has a for loop that runs 3 times. In each loop it beeps for ½ a second (500 ms), then silences for 0.1 second (100 ms). After the 3 loops have run, it then adds an extra 0.1 second of silence.
Now create another function below the one you just created as shown below.
// Does 3 short beeps of 0.1 sec each with 0.1 second gap in between
// Adds extra gap at the end of 0.1 second to make it easier to hear
void shortBeeps() {
for (int i = 0; i < 3; i++) {
PWM6_LoadDutyValue(131);
__delay_ms(100);
PWM6_LoadDutyValue(0);
__delay_ms(100);
}
__delay_ms(100);
}
This function is very similar to the previous one, except it does 3 short beeps. Once it has looped 3 times it adds an extra 0.1 second of silence so you can detect the gap between the long beeps and short beeps.
Now that you have done that, create one last function that will put them together into the “SOS”pattern as shown below.
// sends 3 shortBeeps, 3 longBeeps, 3 shortBeeps
void morseSOS() {
shortBeeps();
longBeeps();
shortBeeps();
}
Now you need to create function prototypes near the top of the main.c file so the compiler knows what the functions are.
Near the top of the file where the #includestatements are, add the following lines just below that:
// function prototypes
void shortBeeps(void);
void longBeeps(void);
void morseSOS(void);
Now all you have to do is call the function morseSOS() when a ‘1’ in HEX is sent from your phone to the PIC-BLE.
To do that, go to the while(1) loop and change your switch case statement so it matches the code below.
switch (byte_recvd) {
case 0x0:
PWM6_LoadDutyValue(0);
break;
case 0x1:
morseSOS();
break;
default:
PWM6_LoadDutyValue(0);
break;
}
So, now your main.c file code should look like the following:
#include "mcc_generated_files/mcc.h"
// function prototypes
void shortBeep(void);
void longBeep(void);
void morseSOS(void);
void main(void) {
SYSTEM_Initialize(); // initialize the device
PWM6_LoadDutyValue(0); // ensure buzzer is off at the start
while (1) {
uint8_t byte_recvd = EUSART1_Read(); // read from the BLE USART
switch (byte_recvd) {
case 0x0:
PWM6_LoadDutyValue(0);
break;
case 0x1:
morseSOS();
break;
default:
PWM6_LoadDutyValue(0);
break;
}
}
}
// function does 3 long beeps of 1/2 second each with 0.1 second gap in between
// Adds extra gap at the end of 0.1 second to make it easier to hear
void longBeep() {
for (int i = 0; i < 3; i++) {
PWM6_LoadDutyValue(131);
__delay_ms(500);
PWM6_LoadDutyValue(0);
__delay_ms(100);
}
__delay_ms(100);
}
// function does 3 short beeps of 0.1 second each with 0.1 second gap in between
// Adds extra gap at the end of 0.1 second to make it easier to hear
void shortBeep() {
for (int i = 0; i < 3; i++) {
PWM6_LoadDutyValue(131);
__delay_ms(100);
PWM6_LoadDutyValue(0);
__delay_ms(100);
}
__delay_ms(100);
}
// function does 3 shortBeeps, 3 longBeeps, 3 shortBeeps
void morseSOS() {
shortBeep();
longBeep();
shortBeep();
}
There is a lot more that you can do with this device, this is just a small taste!
If you make this (or something based on this) please post about it and use the following tags: @MicrochipTech #PIC-BLE
We'd love to see what you come up with!
Comments
Please log in or sign up to comment.