First let me start off by saying that if you have the opportunity to go to a local TI event, do it! They are a great way to network and hear about TI products.
For those of you who don't know, I am currently a PhD student at the Rochester Institute of Technology (RIT). This past Friday RIT hosted an ARM developer's day, where TI manned a both and held a workshop on the Stellaris LaunchPad. I not only learned a bit about TI's ARM products, but also met some great people, and managed to get a TI t-shirt! Very cool! Thank you TI!
The workshop was setup very nicely, each attendee had their own computer (for the most part) and a Stellaris LaunchPad to play with. The presentation was quite informative and discussed not only the history of ARM processors, but also the TI products which utilize ARM technology. Much of the information presented is available online at the TI wiki, more specifically under the "Getting Started with the Stellaris EK-LM4F120XL LaunchPad Workshop" page. It's very interesting what you can learn though when you go to these workshops in person.
One of the best parts of these workshops is that you get to hear about new and upcoming booster packs and development boards. The picture below shows the FRAM MSP430 development board with the CC3000 wifi module attached. The wifi module from TI has a built in ARM processor which runs all the stack software, allowing your embedded applications to use wifi via a serial connection (UART or SPI). I for one, am very excited for when TI releases this board.
Now onto the the Stellaris LaunchPad. After the presentation by two TI field application engineers, the workshop attendees were able to power up a Stellaris LaunchPad and use the demo software that came pre-installed on the board. Take a look at the picture below; it even comes with stackable headers (which you can't really see from this angle)!
Now why would you want to use a Stellaris LaunchPad? The main reason is that it is a more powerful processor, which is ARM based. The ARM processor on the Stellaris LaunchPad, the LM4F120, not only can be clocked at 80MHz but also has a full-speed integrated USB interface. This board will be great for project which require a high speed interface with a computer (did someone say RepRap?). Additionally, there are numerous serial ports, PWM pins, and other awesome peripherals including a high-speed 12-bit ADC. Before concluding, it is important to note that the Stellaris LaunchPad is pin compatible with the MSP430 LaunchPad; this means you can use your booster packs on the Stellaris LaunchPad as well!
While I am quite busy these days with my research, I plan on creating a few posts on getting started with the Stellaris Launchpad; more specifically I would like to port over the LaunchScope code to this board. This board has so much potential and I cannot wait to see all the cool projects that will utilize it in the future.
Sunday, December 2, 2012
Wednesday, October 3, 2012
Review: Solar Energy Harvester Booster Pack
In case you have not heard, there is a new Kickstarter for a "Solar Energy Harvester Booster Pack" built for the MSP430 LaunchPad. The people over at Athena Energy were nice enough to send me one of their boards to review. Before starting the review, I want to say how impressed I was with their communication. I received quick and helpful responses to every email I sent their way. Having backed a few Kickstarters, I can't stress how important communication is throughout the whole process.
First Impressions
May I say, thank you; it is about time. Up until now, there has been no battery booster pack for the MSP430, let alone one with solar panels. This is insanity, considering how little power the MSP430 actually uses. Pair this booster pack up with any of these booster packs at the 43oh store, and you have a great project in the making. The board looks great and comes with stackable headers, as any booster pack should. When the board came, the battery was disconnected using a "battery tab" (a piece of paper); a nice touch. Here are a few pictures.
The ability to plug in an external solar panel and an external lithium battery is a very nice feature. While the prototyping area is small, it is large enough to solder on custom connectors for various purposes. The silkscreen is clean and provides useful information, such as pin descriptions and information on the battery. It is obvious to me that great care was taken to ensure the operating current of the shield was as small as possible. Measuring from the battery, the board used only 27uA of current when not plugged into the MSP430 (if my Fluke can be trusted).
Before plugging the board into my LaunchPad, I wanted to run a few tests first with my multimeter. The lithium battery was sitting at 4.02V (fully charged) and the regulated output was 3.37V, as expected. One feature I would like to see in a future version of this board, would be the ability to adjust VCC from 3.3V to a lower voltage.
What else can I say? The board just worked. It really was plug and play. The booster packs dimensions are pretty standard and the solar cell location is also well thought out. The battery also charges as expected when fully drained. The board functions exactly as advertised.
Other Thoughts
The people over at Athena Energy really care about making this board as good as possible. They have listened to all the feedback I sent them and have helped me with any problems I have run into. They really are very knowledgeable. Take a look at the update section in their Kickstarter; they are planning on adding new features to the final design.
I highly recommend signing up for the fully assembled version of this booster pack. While those of you that have a toaster oven "reflow oven" will be able to solder this up yourself, QFN packages are nearly impossible to solder by hand. Although the assembled board is pricey, I believe that it is worth it. This great chip from Texas Instruments is very underutilized in hobby projects due to the package it comes in. Additionally, since the solar cells and battery are included; it literally contains everything you need to get started on a battery powered project.
As of today, there are 16 days left on the Kickstarter. Let's get the word out and help them meet their funding goals. The convenience of this board is well worth it.
First Impressions
May I say, thank you; it is about time. Up until now, there has been no battery booster pack for the MSP430, let alone one with solar panels. This is insanity, considering how little power the MSP430 actually uses. Pair this booster pack up with any of these booster packs at the 43oh store, and you have a great project in the making. The board looks great and comes with stackable headers, as any booster pack should. When the board came, the battery was disconnected using a "battery tab" (a piece of paper); a nice touch. Here are a few pictures.
The ability to plug in an external solar panel and an external lithium battery is a very nice feature. While the prototyping area is small, it is large enough to solder on custom connectors for various purposes. The silkscreen is clean and provides useful information, such as pin descriptions and information on the battery. It is obvious to me that great care was taken to ensure the operating current of the shield was as small as possible. Measuring from the battery, the board used only 27uA of current when not plugged into the MSP430 (if my Fluke can be trusted).
Before plugging the board into my LaunchPad, I wanted to run a few tests first with my multimeter. The lithium battery was sitting at 4.02V (fully charged) and the regulated output was 3.37V, as expected. One feature I would like to see in a future version of this board, would be the ability to adjust VCC from 3.3V to a lower voltage.
What else can I say? The board just worked. It really was plug and play. The booster packs dimensions are pretty standard and the solar cell location is also well thought out. The battery also charges as expected when fully drained. The board functions exactly as advertised.
Other Thoughts
The people over at Athena Energy really care about making this board as good as possible. They have listened to all the feedback I sent them and have helped me with any problems I have run into. They really are very knowledgeable. Take a look at the update section in their Kickstarter; they are planning on adding new features to the final design.
I highly recommend signing up for the fully assembled version of this booster pack. While those of you that have a toaster oven "reflow oven" will be able to solder this up yourself, QFN packages are nearly impossible to solder by hand. Although the assembled board is pricey, I believe that it is worth it. This great chip from Texas Instruments is very underutilized in hobby projects due to the package it comes in. Additionally, since the solar cells and battery are included; it literally contains everything you need to get started on a battery powered project.
As of today, there are 16 days left on the Kickstarter. Let's get the word out and help them meet their funding goals. The convenience of this board is well worth it.
Saturday, September 8, 2012
A New Webstore
Today marks the close of the old NJC's MSP430 Store. The old system which was based on PayPal buttons was very difficult to use and did not lend itself to an aesthetic and functional store. Although the old store has been removed, a new store is ready to go on my other website, Hardware Breakout.
The blog on Hardware Breakout has a long way to go before it is finished, but the Hardware Breakout Webstore is complete! All of the products that you could purchase from the old webstore you will find at Hardware Breakout, except for the DEV.BO which has been discontinued. There are also many new products and breakout boards which I hope you all will love.
Please note there has been one big change, shipping is no longer free on order below $5 USD. For most of you, this will not be an issue. Please read the Hardware Breakout Store shipping policy for more information. If you experience any problems with the new store or have any comments, please send me an email or leave a comment.
The blog on Hardware Breakout has a long way to go before it is finished, but the Hardware Breakout Webstore is complete! All of the products that you could purchase from the old webstore you will find at Hardware Breakout, except for the DEV.BO which has been discontinued. There are also many new products and breakout boards which I hope you all will love.
Please note there has been one big change, shipping is no longer free on order below $5 USD. For most of you, this will not be an issue. Please read the Hardware Breakout Store shipping policy for more information. If you experience any problems with the new store or have any comments, please send me an email or leave a comment.
Tuesday, August 7, 2012
Using a Nokia LCD Library
SparkFun sells a great, low-cost, yet easy to use LCD display breakout board for the Nokia 5110 display. This LCD is perfect for any which requires a user interface. When I made the decision to build a reflow toaster oven, I also decided that I wanted to be able to view the oven's progress on a small display. This post supports the series I am writing about on HardwareBreakout.com which discusses building your own toaster oven "reflow oven".
This specific display has been used in many projects on many different microcontrollers, such as the Arduino. This means that there is already ample information out there on this LCD including libraries written for many different microcontrollers. Since reinventing the wheel is usually a bad idea, this post will discuss using an existing library for the MSP430 to meet our toaster oven needs.
Requirements
My goal for this project is to display real-time information on the reflow process. This includes the current temperature in the toaster oven, the temperature the toaster oven should be at, the current zone the oven is in (e.g. cooldown, reflow, preheat), and how long the process has been going on for. Given that all of the information I would like to display is text based, implementation is fairly straight forward.
There are many libraries written specifically for the MSP430 that interface with this display.
This specific display has been used in many projects on many different microcontrollers, such as the Arduino. This means that there is already ample information out there on this LCD including libraries written for many different microcontrollers. Since reinventing the wheel is usually a bad idea, this post will discuss using an existing library for the MSP430 to meet our toaster oven needs.
Requirements
My goal for this project is to display real-time information on the reflow process. This includes the current temperature in the toaster oven, the temperature the toaster oven should be at, the current zone the oven is in (e.g. cooldown, reflow, preheat), and how long the process has been going on for. Given that all of the information I would like to display is text based, implementation is fairly straight forward.
There are many libraries written specifically for the MSP430 that interface with this display.
These are just three of the many libraries which can be found for the MSP430. For this post, I have decided to use RobG's Nokia 5110 as it uses the hardware SPI rather than a software SPI.
It is VERY important to give credit where credit is due. If you use someone else's code, always include comments in your code linking to the original author. It is great that we have such an awesome community, where code is shared freely.
The Nokia LCD
While it is not necessary to understand how the LCD works when using a fully functioning library, it can still be beneficial if you need to modify the library or if you run into problems with your project. The first step in figuring out how the Nokia 5110 works is to take a look at the datasheet.
The above image shows how each pixel on the screen is organized. There are a total of 84x48 pixels, these pixels are organized into 6 horizontal banks (zero through five), and 84 columns. Each bank contains 8 pixels, which sum up to a total of 48 rows.
The above image shows how the RAM will map to a given pixel in the display. This image shows the whole display, with each row representing one bank. One byte in memory stores the data for each column in a given bank; this makes sense considering there are 8 rows of pixels in each bank.
Given that we are strictly displaying text on the screen using RobG's code, we do not really need to know any of this. That being said, understanding how the memory is mapped to the display is very important if you will be creating your own graphics or characters.
Shows how each pixel is organized in DRAM. Image taken from the Nokia 5110 Datasheet |
Shows how each byte in RAM relates to each pixels on the display. Image taken from the Nokia 5110 Datasheet |
Given that we are strictly displaying text on the screen using RobG's code, we do not really need to know any of this. That being said, understanding how the memory is mapped to the display is very important if you will be creating your own graphics or characters.
Creating an LCD Project in CCS
Since I prefer Code Composer Studio (CCS) over the other methods for programming the MSP430, I will quickly discuss how to use someone else's code in your projects.
The first step is to create a new project for your specific device, for this example I am using the MSP430G2553. While you can import any file to your project, I prefer to just copy and paste the code when using code posted online. For RobG's code you need to create one new file, "PCD8544.h" and copy his code directly into it. The simplest way to start with his library is to also copy his "main.c" and modify it for your project.
While it is possible to change some of the pin assignments around, be careful. Some of the pins need to stay where they are because they are special SPI pins built into the USCIB interface (in the MSP430G2553). To use this library in its most basic form, three functions are needed.
- clearBank(bank) - Clears an entire line on the LCD display and sets the current location to the beginning of the line.
- writeStringToLCD(string) - Write a string to the LCD in the current location.
- writeCharToLCD(char) - Write a character to the LCD in the current location.
In addition to RobB's code I will be using the printf code which I discussed in my previous post. This allows us to display the content of our variables on the LCD display, for example the current temperature of the toaster oven. To use printf, you have to copy the printf.c file into your project and then create the following two functions in your main.c file. By using these functions, the output of printf is displayed on the LCD.
/**
* puts() is used by printf() to display or send a string.. This function
* determines where printf prints to. For this case it outputs a string
* to a LCD, another option could be to send the string out via UART.
**/
void puts(char *s) {
writeStringToLCD(s);
}
/**
* puts() is used by printf() to display or send a character. This function
* determines where printf prints to. For this case it outputs a
* character to a LCD.
**/
void putc(unsigned b) {
writeCharToLCD(b);
}
Code Snippet
The code below is a snippet from my project. This is just a quick example of using RobG's code with my project. I call this function whenever I want to display the current status of the toaster oven on the display. The variables it uses are global variables which will be updated as the program runs. I only call this function when a value is changed, so that the display is not updated unnecessarily.
void updateStatus() {
clearBank(0); // Line 0 shows current temperature
printf("Current: ");
printf("%u", currentTemp);
writeCharToLCD(0x7f); // Degree symbol
writeStringToLCD("C");
clearBank(1); // Line 1 is empty
clearBank(2); // Line 2 shows the desired temperature
writeStringToLCD("Desired: ");
printf("%u", desiredTemp);
writeCharToLCD(0x7f); // Degree symbol
writeStringToLCD("C");
clearBank(3); // Line 3 is empty
clearBank(4); // Line 4 shows which zone the oven is in
printf("Zone: ");
writeStringToLCD(zoneString[zoneIndex]);
if (heatOn) // Line 4 includes an asterisk when the
printf("*"); // heating elements are on
clearBank(5); // Line 5 shows the time the toaster oven
printf("Time: "); // has been running
if (minutes < 10) // Make sure there are always two digits
printf("0");
printf("%u:", minutes);
if (seconds < 10) // Make sure there are always two digits
printf("0");
printf("%u", seconds);
}
Conclusion
Overall, integrating RobG's library into my project was very simple. It went without a hitch. Modifying his code for custom characters is also fairly straight forward. I hope this post helped shed some light on how the Nokia 5110 display works. It has never been easier to use such a cool display in your projects. This post supports my series on Hardware Breakout which discusses building a toaster oven "reflow oven".
Leave a comment and post links of your projects using the Nokia 5110!
Overall, integrating RobG's library into my project was very simple. It went without a hitch. Modifying his code for custom characters is also fairly straight forward. I hope this post helped shed some light on how the Nokia 5110 display works. It has never been easier to use such a cool display in your projects. This post supports my series on Hardware Breakout which discusses building a toaster oven "reflow oven".
Leave a comment and post links of your projects using the Nokia 5110!
Friday, June 8, 2012
printf() for the MSP430
"The output function printf translates internal values to characters."This simple definition comes from the book "C Programming Language" by Kernighan and Ritchie. I highly recommend this book to anyone who is interested in learning more about C, it is a must have for any embedded programmer.
The printf() function will allow us to display any value in your code so that it is human readable. What does this mean? Let's say you would like to tell the computer what temperature your MSP430 has measured; a typical output might be "Temperature: 71°F". We know how to read the internal temperature from a previous post, but how do we get our MSP430 to output the above string over UART? We use the printf() function.
printf("Temperature: %u°F\r\n", tempValue);I will not be explaining how printf() actually works in this post. If you would like more information on how to use this function please see the above mentioned book, or this link. Due to the fact that the MSP430 has limited resources (e.g. memory), a compact version of the standard printf() function must be used.
Since the printf() included in Code Composed Studio is very large and will not fit on many of the value line MSP430s, we must add our own printf() function to our projects. oPossum on the 43oh.com forums has shared his printf() with the community and since reinventing the wheel is rarely a good idea, we will be using oPossum's code.
"This is a tiny printf() function that can be used with the chips that come with the Launchpad. Code size is about 640 bytes with CCS."This is how oPossum describes his function. While this function does not support all of the standard printf() features, it is more than sufficient for use on an MSP430. Using this function, we can format 7 separate data types: character, string, integer, unsigned integer, long, unsigned long, and hexadecimal (16-bit) formatting.
I want to thank oPossum for sharing his code with the community. The code is very well written and works great! Thanks again!
Code
Please scroll down for the rest of the post. One of these days I will figure out how to limit the height of my embedded code.
Customizing the Output
Depending on your project, you will want to have printf() output differently. For this example, printf() will send the formatted string out over the UART to a computer. For a different project, you might want to output the string to an LCD, or through USB. The code provided from oPossum allows you to define two functions, puts() and putc() which determines how printf() will output the formatted string. Without defining these two functions, the printf() will actually not do a thing. printf() uses puts(char *) to send out a string value, and uses putc(unsigned) to send out a character.
In the code above, my puts(char *) and putc(unsigned) functions sends the string and character out over UART using another of my functions, sendByte(unsigned char). The code in these functions is fairly straight forward, and will not be discussed here. If you would like to another output for printf(), you would create your own puts(char *) and putc(unsigned) functions as I have done in the code above.
Testing the printf() Function
I will be using a very similar setup as oPossum for testing the printf() function. The code above will send a test sequence to the computer when the MSP430 receives the character 't' over UART. I recommend using Realterm for interfacing with the MSP430 over UART. To connect, select the correct COM port and set the baud rate to 9600. Once connected, send the character 't' by clicking on the blank window and typing 't'. You should see the following response from the MSP430.
Test sequence from the MSP430 |
Use this code in your projects! It really is a great piece of code. To use this printf() function to your own CCS projects, just add printf.c to your project and create your own puts(char *) and putc(unsigned) in your main c file as I have done in this example.
In the next post, I will expand on the previous post by creating a real-time clock using the MSP430. Stay tuned!
Thursday, March 15, 2012
Using ACLK and the 32kHz Crystal
The other day I decided to build a timer to control some lights in my apartment. As I hope to expand this mini project into a full-fledged home automation system built around the CC430, a simple light timer from Walmart would not do! Obviously, the LaunchPad is the perfect tool for the job.
For example, let's say that I wanted to keep my lights on for 5 hours at night while I am on vacation. The first thing you will need is a way to tell time; this requires some sort of real-time clock. Since the high frequency clock sources on the MSP430 are not accurate enough to keep reliable time over a large period of time (anything greater than 1 minute in my opinion), a lower frequency clock will be needed. Though many of the newer MSP430s have built in 32kHz clocks, such as the the MSP430F5510, the value line series does not!
The 32.768kHz Crystal
The first step for building this project is to install the 32.768kHz crystal onto the LaunchPad. From this point onward I will just be calling this 32kHz for simplicity. You might be wondering why a real time clock is based on 32.768kHz; 32768 is exactly 2^15. This number can be divided down using binary values to give you a frequency of 1Hz, or a period of 1 second. This is why 32kHz is the standard frequency used in real-time clocks.
The above image shows the crystal soldered onto my LaunchPad. There are many methods you can use to solder this on, one of which is nicely documented on Justin's Tech blog. I ended up soldering the base of the crystal first, ensuring that the clock was positioned correctly before I soldered the small leads. Use any method that works for you.
The LED Hello World
The first thing you should do once you have this soldered onto your board, is test it. Making sure that everything works before you start a complicated project is very important. Let's make an LED turn on every two seconds, for one second. Instead of changing the timer output pins directly as we did in an earlier post, lets blink the LED manually so that we can easily expand the functionality of this program in a future post. There are a few things you should notice in the code below.
In this code we divide the clock by 64 (lines 26 and 37) which causes the timer to increment 512 times a second (512Hz). Once the clock counts up to512 511 (this is because we start counting at 0), one second has gone by, and an interrupt is thrown. Now that we are entering an interrupt routine instead of automatically toggling the output, we can use this interrupt routine in the future to expand the functionality of this code.
The last important line of code is line 27, where a capacitance value is set. This value matches the capacitors that come installed on your LaunchPad. In the next section, I will elaborate slightly on what this capacitance is.
Edit: This statement is actually incorrect. A big thank you goes out to Jens-Michael Gross for pointing it out to me.
One thing I wanted to mention before this post comes to a close, is how you can take this design off the LaunchPad and make it your own. Many projects work out so well that you just want to create a PCB or make it permanent in some other way.
Working with crystals can be tricky for beginners, as there is one thing you must look out for. All crystals require a load capacitance to remain stable, Wikipedia (Pierce Oscillator) and Texas Instruments both have some quality information on the topic. The value of these two capacitors depend on which crystal you use. Even two crystals with the same frequency which are made by the same manufacture might require different load capacitor values. Please check the crystal's datasheet for this information.For example, the LaunchPad uses 12pF capacitors to load the crystal we just installed, yet many crystals require 22pF.
Edit: This statement is actually incorrect. Another big thank you goes out to Jens-Michael for pointing it out to me. Thanks for reading!
Hopefully you enjoyed this post and found it informative. I am going to try to keep things a bit more bite-sized from now on. Let me know what you think.
Post links to your projects which use the ACLK or the 32kHz crystal in the comment section below!
For example, let's say that I wanted to keep my lights on for 5 hours at night while I am on vacation. The first thing you will need is a way to tell time; this requires some sort of real-time clock. Since the high frequency clock sources on the MSP430 are not accurate enough to keep reliable time over a large period of time (anything greater than 1 minute in my opinion), a lower frequency clock will be needed. Though many of the newer MSP430s have built in 32kHz clocks, such as the the MSP430F5510, the value line series does not!
The 32.768kHz Crystal
The first step for building this project is to install the 32.768kHz crystal onto the LaunchPad. From this point onward I will just be calling this 32kHz for simplicity. You might be wondering why a real time clock is based on 32.768kHz; 32768 is exactly 2^15. This number can be divided down using binary values to give you a frequency of 1Hz, or a period of 1 second. This is why 32kHz is the standard frequency used in real-time clocks.
The above image shows the crystal soldered onto my LaunchPad. There are many methods you can use to solder this on, one of which is nicely documented on Justin's Tech blog. I ended up soldering the base of the crystal first, ensuring that the clock was positioned correctly before I soldered the small leads. Use any method that works for you.
The LED Hello World
The first thing you should do once you have this soldered onto your board, is test it. Making sure that everything works before you start a complicated project is very important. Let's make an LED turn on every two seconds, for one second. Instead of changing the timer output pins directly as we did in an earlier post, lets blink the LED manually so that we can easily expand the functionality of this program in a future post. There are a few things you should notice in the code below.
In this code we divide the clock by 64 (lines 26 and 37) which causes the timer to increment 512 times a second (512Hz). Once the clock counts up to
"The capacitance setting is an internal switch that enables some silicon capacitors on the MSP die. The selection has to match the required load capacitance of the used watch crystal. You can set it to minimum (plain parasitic pin capacitance) and apply external capacitors of the proper value, if you want. However, the available options are sufficient for the most watch crystals, so external capacitors are unnecessary, even counterproductive. And external capacitors have a large tolerance that affects the crystal frequency. The LaunchPad I just got has no capacitors installed (the C21 and C22 pads are empty, as it should be if the XCAP options are used."Custom Design
One thing I wanted to mention before this post comes to a close, is how you can take this design off the LaunchPad and make it your own. Many projects work out so well that you just want to create a PCB or make it permanent in some other way.
Working with crystals can be tricky for beginners, as there is one thing you must look out for. All crystals require a load capacitance to remain stable, Wikipedia (Pierce Oscillator) and Texas Instruments both have some quality information on the topic. The value of these two capacitors depend on which crystal you use. Even two crystals with the same frequency which are made by the same manufacture might require different load capacitor values. Please check the crystal's datasheet for this information.
"12pF is the typical load for most crystals I've ever seen. But due to the electric connection, a 12pF load means [there will be 2] 24pF capacitance on each of the crystals sides. Reason is that (seen from the crystal), the two capacitors are in series to each other and parallel to the crystal. […] Subtract the ~2pF pin capacitance of the MSPs pins and you get [two separate] 22pF [for] external capacitors, resulting in 12pF load. The XCAP settings already include pin capacitance and the /2 factor."A bit more information and great advice from Jens-Michael!
"Experiments have shown that the G devices (in opposition to the AFE2x and some other x2 family devices without HFXT1 input) will accept a high-frequency TTL clock signal (e.g. from a self-oscillating quartz oscillator) on the XTIN pin, when in bypass mode. The Datasheet limits external clock to 50kHz, but there were no problems with 16MHz."
Conclusion
As you can see by the following screenshot from my oscilloscope, this timer is pretty darn accurate.Hopefully you enjoyed this post and found it informative. I am going to try to keep things a bit more bite-sized from now on. Let me know what you think.
Post links to your projects which use the ACLK or the 32kHz crystal in the comment section below!
Subscribe to:
Posts (Atom)