Jump to content
43oh

oPossum

Members
  • Content Count

    925
  • Joined

  • Last visited

  • Days Won

    103

Reputation Activity

  1. Like
    oPossum reacted to VMM in Another 430 Watch   
    Well, it's up and running.  All the hardware seems to check out.  Display and accelerometer are playing nice on a shared spi bus.  The FR5738 RTC is keeping time.  LIS3DH initialized and generating interrupts properly.  Lots of programming to do now for the interface, calibrations, etc.

  2. Like
    oPossum reacted to Fred in Adjustable MSP430F5529 Launchpad enclosure   
    I'm working on a project that should end up with a nice etched F5529 board, but for now it's going to just have a Launchpad stuck in there, I needed a quick enclosure for it so knocked up an adjustable one in OpenSCAD. I know a few people have (or are thinking about) a 3D printer so thought I'd share. It allows you to adjust the space on any side of the LaunchPad for anything else you want to include. It could do with some improvements - e.g the little locating pins are easy to snap off. Feel free to amend if the SCAD file if the available adjustments don't suit you.
     

     
    Here's a photo of it, with the partly-finished project in the background and the printer busy printing the lid...

  3. Like
    oPossum reacted to yyrkoon in Arduino TRE   
    Really ? 
     
    Seriously, you should try and run software on ARM Linux, and note the differences between x86 Linux. You'll run into all sorts of issues outside of your package manager. Then perhaps even using package repo's from your distro.
     
    For example I want to make a Linux appliance out of my Beagelbone. First thought is using Snorkel(embedded web server SDK - cross platform type). Snorkel does not "support" hardfloat ARM( eabihf ), and what it does "support" is still in beta( has been for a very long time ). Next thought was perhaps nodejs would work. Which in fact works great now. BUt it is not a simple task to get things working. You have to compile on the target. This is not far from normal. But you can not always use the latest sources. This is trial and error. Passed that if you use npm to install modules, you better watch it because there is a chance it'll install x86 binaries . . . No one says that every software "developer" has to be smart.
     
    This is just one small example, of many potential "issues" that will arise while working with Linux on ARM.
     
    EDIT
     
    Anyway, the feeling I get now is as it was 10+ years ago using some Linux distro's. The OS almost seems to want to fight against you sometimes. Usually when it is something important, that you *have* to use. 
  4. Like
    oPossum got a reaction from roadrunner84 in Nokia LCD not working when CS tied to ground? [Solved]   
    CS will enforce SPI sync. If CS is not used then you must make sure the SPI clock is perfect. If the LCD has not fully reset when SPI comms begin, then sync is messed up.
  5. Like
    oPossum got a reaction from cde in Nokia LCD not working when CS tied to ground? [Solved]   
    CS will enforce SPI sync. If CS is not used then you must make sure the SPI clock is perfect. If the LCD has not fully reset when SPI comms begin, then sync is messed up.
  6. Like
    oPossum got a reaction from cde in Nokia LCD not working when CS tied to ground? [Solved]   
    Try adding some delays to reset.
     

    // Reset LCD DISPLAY_PORT |= RES; // Pull Reset High to Known State, Probably Unneeded DISPLAY_PORT &= ~RES; // Pull Reset Low to Reset LCD __delay_cycles(100000); DISPLAY_PORT |= RES; // Pull Reset High to Complete Reset __delay_cycles(100000);
  7. Like
    oPossum reacted to Fred in New MSP430F5529 USB Launchpad Released   
    My second board arrived today. The silkscreen has changed quite a bit - especially on the back where there are 3 rockets rather than 1. Both marked as Rev 1.4.
     
     
    The only other difference I can spot is the revision of the F5529 itself - up from E to H.

  8. Like
    oPossum reacted to Jaspreet@DTU in MSP430 ControlWare Project   
    Hi all!  This is my first post on 43oh forum. I am currently working on a project which i think the 43oh community members will like.   :grin: 
     
    Project Description: The main aim of the project was to create a Simple, Easy to control Graphic User Interface (GUI) to control various pins on MSP430 Launchpad (MSP430g2553 uC). The GUI for this purpose was created using Visual C# and can be used on any Windows based System.
     
    (GUI in action)
                                                                                                                                                                             
     


    Features:
    The GUI allows the user to test and develop hardware without having to write any code for the device. Pins configured GPIO can be turned "LOW" or "HIGH". The present version of the GUI enables real time data acquisition and plotting. The data obtained from the pins configured as "ADC" can also be stored in an Excel Sheet. Data from Internal Temperature Sensor of the uC can be obtained and plotted as well. The GUI enables the user to Generate three independent PWM Signals and vary the Duty Cycle. Instructions:  You need to upload a code to the MSP430g2553 microcontroller for working with the GUI. The code and the application GUI can be downloaded from the link given below. Extract the contents of the download file and load the MSP430_ControlWare project in Code Composer Studio and upload the code to the MSP430 Launchpad (MSP430g2553 uC). You can also view the video for the demonstration of the GUI. 
     
    Future Upgrades:  First thing that i will do this weekend is to implement a scheme for making the serial communication more efficient. There are way too many comparisons in the current code and i would definitely like to reduce that by a significant amount. Secondly, i will improve the PWM part to allow user to select the frequency as well. I would like to have suggestions on what more i should do with the User Interface.
     
    For any queries and suggestions, feel free to drop me a mail or reply to this post.
     
    MSP430 ControlWare.rar 
  9. Like
    oPossum reacted to pabigot in Playing with CC3000 on MSP430F5529 with Energia   
    Not sure it fits your plans, but I've got a git repository with the TI Host Driver and a few patches at https://github.com/pabigot/cc3000 that works well with mspgcc. This is only Host Driver version 13 (corresponds to SP 1.11) since they haven't released 14 (for SP 1.11.1) yet in the standalone version. But it's been working fine for me with the 1.24 (SP 1.11.1) firmware. (Or at least as fine as SimpleLink works: it's not a product-ready system, but is worlds better than it was last year.)
     
    There's an example CC3000 program documented at http://pabigot.github.io/bsp430/ex_rf_cc3000.html. There are more features in the newer version in the bsp430 git repository.
     
    Unfortunately I'm not scheduled to get a 5529LP until sometime in the second half of October, so the example only works on the F5438A-based experimenter boards (unless you disable enough commands to make it fit in either the Fraunchpad or G-series Launchpad).
  10. Like
    oPossum got a reaction from timotet in Regulating power for RF24l01// PB with auto reset   
    The useful life of an alkaline cell is about 1.55V (new) to 900 mV (under 10% capacity). At 1.25 volts there is still a lot of capacity left in the cell.
     
    So if you want to use the full capacity of two AA cells, then the circuit must operate on 1.8V to 3.1V.
     
    Low voltage boost converters that can boost a single AA/AAA/AAAA cell to 3.3V are becoming increasingly popular. Microchip (MCP1623/24/40), TI (TPS61220/21/22), and others make them.
  11. Like
    oPossum reacted to spirilis in DLP Design Giveaway - DLP-7970ABP NFC/RFID Reader BoosterPack   
    Also appears these are for sale, $25:
     
    http://www.dlpdesign.com/ (product page)
     
    http://www.mouser.com/ProductDetail/DLP-Design/DLP-7970ABP/?qs=%2fha2pyFaduhDAR%2fQzTpWeB0f%2fkxQp6p8dOsBghyoqnk%3d
    http://www.digikey.com/product-search/en?WT.z_header=search_go〈=en&site=us&keywords=7970abp&formaction=on&x=0&y=0
  12. Like
    oPossum got a reaction from spirilis in Regulating power for RF24l01// PB with auto reset   
    The useful life of an alkaline cell is about 1.55V (new) to 900 mV (under 10% capacity). At 1.25 volts there is still a lot of capacity left in the cell.
     
    So if you want to use the full capacity of two AA cells, then the circuit must operate on 1.8V to 3.1V.
     
    Low voltage boost converters that can boost a single AA/AAA/AAAA cell to 3.3V are becoming increasingly popular. Microchip (MCP1623/24/40), TI (TPS61220/21/22), and others make them.
  13. Like
    oPossum reacted to simpleavr in Simulating a TI calculator with crazy 11-bit opcodes   
    It's bound to happen.
     

     
    Currently I am re-using a UI shield from my ez430trainer project. http://forum.43oh.com/topic/3013-ez430trainer-a-retro-style-single-board-computer/
     
    I am trying to locate those bubble led displays. It would be interesting to have a single PCB build for a daily use calculator.
  14. Like
    oPossum reacted to Rickta59 in Simplest code, oddest behavior   
    Yes, comment was to you @@igendel. Those IDEs make it easier to put your finger on the source of a register define or function call with a click of a button. There is a lot of good information in the comments of the msp430g2553.h header. Also, code completion saves an huge amount of time and mistakes. And you can't beat delayed hover help, it just makes life less confusing.
     
    I've also been coding C/C++ for a long time ( more than 30 years ). It wasn't until I became interested in embedded development that I started to look at every asm instruction the compiler was generating. Now I fit entire programs in less flash memory than I used to use for a network socket work buffer.
     
    With regards to the compiler options in Energia. When it all started, most people had version 1.4 of the msp430g2231 Launchpad. That chip had 2k of flash and 128 bytes of ram. If we had turned off optimization, a simple serial hello world program would have exhausted all the flash and ram.
     
    -rick
  15. Like
    oPossum got a reaction from igendel in Simplest code, oddest behavior   
    It is quite slow unless a power of 2 is used, for example n % 512
     

    try 200000UL and 2000000UL
  16. Like
    oPossum got a reaction from energia in Change Frequency to SI570 DXO on I2C. (not fully fonctionnal).   
    Try this:
    const uint64_t RFREQ_INIT =      ((uint64_t)(i2cInitBuf[2] & 0x3F) << 32)   | ((uint32_t)i2cReadBuf[3] << 24)   | ((uint32_t)i2cReadBuf[4] << 16)   | ((uint16_t)i2cReadBuf[5] << 8)   | i2cReadBuf[6];       const float FREQ_MHZ_INIT = (float)RFEQ_INIT / 268435456.0;  // 268435456 = 2^28
  17. Like
    oPossum got a reaction from f4dtr in Change Frequency to SI570 DXO on I2C. (not fully fonctionnal).   
    Try this:
    const uint64_t RFREQ_INIT =      ((uint64_t)(i2cInitBuf[2] & 0x3F) << 32)   | ((uint32_t)i2cReadBuf[3] << 24)   | ((uint32_t)i2cReadBuf[4] << 16)   | ((uint16_t)i2cReadBuf[5] << 8)   | i2cReadBuf[6];       const float FREQ_MHZ_INIT = (float)RFEQ_INIT / 268435456.0;  // 268435456 = 2^28
  18. Like
    oPossum got a reaction from xv4y in Change Frequency to SI570 DXO on I2C. (not fully fonctionnal).   
    Try this:
    const uint64_t RFREQ_INIT =      ((uint64_t)(i2cInitBuf[2] & 0x3F) << 32)   | ((uint32_t)i2cReadBuf[3] << 24)   | ((uint32_t)i2cReadBuf[4] << 16)   | ((uint16_t)i2cReadBuf[5] << 8)   | i2cReadBuf[6];       const float FREQ_MHZ_INIT = (float)RFEQ_INIT / 268435456.0;  // 268435456 = 2^28
  19. Like
    oPossum reacted to chicken in Pervasive Displays EPaper Boosterpack   
    I wasn't aware of these epaper BoosterPacks by Pervasive Displays. Did anyone already play with these?
     
    http://www.pervasivedisplays.com/kits/ext_kit
     

     

     
    Seems to be identical with Adafruit's offering, at least as seen from the top
    http://www.adafruit.com/category/63_150
     

    Also available at Digikey, but Adafruit is approx. $5 cheaper.
     
    PS: Not listed on TI's "official" BoosterPack list yet, someone nudging the right people please
     
     
  20. Like
    oPossum got a reaction from agaelema in Tiny printf() - C version   
    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.
     
    There are 7 format specifiers:
    %c - Character
    %s - String
    %i - signed Integer (16 bit)
    %u - Unsigned integer (16 bit)
    %l - signed Long (32 bit)
    %n - uNsigned loNg (32 bit)
    %x - heXadecimal (16 bit)
     
    Field width, floating point and other standard printf() features are not supported.
     
    printf() code

    #include "msp430g2231.h" #include "stdarg.h" void putc(unsigned); void puts(char *); static const unsigned long dv[] = { // 4294967296 // 32 bit unsigned max 1000000000, // +0 100000000, // +1 10000000, // +2 1000000, // +3 100000, // +4 // 65535 // 16 bit unsigned max 10000, // +5 1000, // +6 100, // +7 10, // +8 1, // +9 }; static void xtoa(unsigned long x, const unsigned long *dp) { char c; unsigned long d; if(x) { while(x < *dp) ++dp; do { d = *dp++; c = '0'; while(x >= d) ++c, x -= d; putc(c); } while(!(d & 1)); } else putc('0'); } static void puth(unsigned n) { static const char hex[16] = { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; putc(hex[n & 15]); } void printf(char *format, ...) { char c; int i; long n; va_list a; va_start(a, format); while(c = *format++) { if(c == '%') { switch(c = *format++) { case 's': // String puts(va_arg(a, char*)); break; case 'c': // Char putc(va_arg(a, char)); break; case 'i': // 16 bit Integer case 'u': // 16 bit Unsigned i = va_arg(a, int); if(c == 'i' && i < 0) i = -i, putc('-'); xtoa((unsigned)i, dv + 5); break; case 'l': // 32 bit Long case 'n': // 32 bit uNsigned loNg n = va_arg(a, long); if(c == 'l' && n < 0) n = -n, putc('-'); xtoa((unsigned long)n, dv); break; case 'x': // 16 bit heXadecimal i = va_arg(a, int); puth(i >> 12); puth(i >> 8); puth(i >> 4); puth(i); break; case 0: return; default: goto bad_fmt; } } else bad_fmt: putc(c); } va_end(a); }
     
    test code

    #include "msp430g2231.h" void serial_setup(unsigned out_mask, unsigned in_mask, unsigned duration); void printf(char *, ...); void main(void) { char *s; char c; int i; unsigned u; long int l; long unsigned n; unsigned x; // Disable watchdog WDTCTL = WDTPW + WDTHOLD; // Use 1 MHz DCO factory calibration DCOCTL = 0; BCSCTL1 = CALBC1_1MHZ; DCOCTL = CALDCO_1MHZ; // Setup the serial port // Serial out: P1.1 (BIT1) // Serial in: P1.2 (BIT2) // Bit rate: 9600 (CPU freq / bit rate) serial_setup(BIT1, BIT2, 1000000 / 9600); printf("%s", "\r\n*** printf() test ***\r\n"); s = "test"; c = 'X'; i = -12345; u = 12345; l = -1234567890; n = 1234567890; x = 0xABCD; printf("String %s\r\n", s); printf("Char %c\r\n", c); printf("Integer %i\r\n", i); printf("Unsigned %u\r\n", u); printf("Long %l\r\n", l); printf("uNsigned loNg %n\r\n", n); printf("heX %x\r\n", x); printf("multiple args %s %c %i %u %l %n %x\r\n", s, c, i, u, l, n, x); printf("\r\n*** Done ***\r\n"); for(;; }

  21. Like
    oPossum reacted to simpleavr in Simulating a TI calculator with crazy 11-bit opcodes   
    And has been made available. http://files.righto.com/calculator/sinclair_scientific_simulator.html
     
    Someone has to realize this on msp430.
  22. Like
    oPossum reacted to larsie in PCBA at Seeed with MSP430?   
    Thanks Rob. I tried Elecrow as you suggested. Really nice results. Boards came to about 10 USD per board, with components, dhl shipping etc, when making 10 boards. Quite a lot of small components, connectors etc, so I think it's a good price. They also soldered 0402 components very nicely. 
  23. Like
    oPossum got a reaction from GeekDoc in 15 channel PWM with async serial control   
    This code really isn't suitable for use with RC servos. They typically want a ~20 ms frame rate to work properly, and are usually addressed sequentially rather than concurrently.
     
    See this thread for some more appropriate code.
  24. Like
    oPossum got a reaction from GeekDoc in 15 channel PWM with async serial control   
    This is derived from 16 channel PWM with a single timer.
     
    The ISR has be extended to handle PWM and async serial reception. The first timer capture/compare is used for timing the serial bits and the second is used for PWM.
     
    This is the ISR...

    __interrupt void Timer0_A1(void) { static unsigned bit_mask = 0; // Serial rx data bitmask static unsigned rx_data; // Serial rx data // const unsigned x = TAIV; // Clear interrupt flag, get vector // if(x == 4) { // --- CCR2 - PWM P1OUT &= ~pa->port_off; // Port pins off P2OUT &= ~pa->port_off >> 8; // P1OUT |= pa->port_on; // Port pins on P2OUT |= pa->port_on >> 8; // pa = pa->next; // Next node in list TACCR2 = pa->time; // Update timer compare time // } else if(x == 2) { // --- CCR1 - Serial rx if(TACCTL1 & CAP) { // Start bit (capture mode) rx_data = 0; // Clear rx data bit_mask = 1; // Begin with bit 0 TACCTL1 &= ~CAP; // Switch from capture to compare mode TACCR1 += 2500; // Set timer for middle of first data bit } else { // Data/stop (compare mode) if(bit_mask & 0x0100) { // Stop bit? *head++ = rx_data; // Add rx data to ring buffer if(head >= &rx_buf[sizeof(rx_buf)/sizeof(rx_buf[0])]) head = rx_buf; TACCTL1 |= CAP; // Switch from compare to capture mode } else { // Data bit if(TACCTL1 & SCCI) rx_data |= bit_mask; // Update rx data bit_mask <<= 1; // Next data bit TACCR1 += 1667; // Set timer for middle of next bit } } } }
     
    The PWM code is unchanged, but now is only executed when the interrupt vector indicates capture/compare 2 match.
    The serial code begins in capture mode. When a falling edge is detected the mode is changed to compare to sample in the middle of each data bit and the stop bit. A ring buffer is used to prevent loss of received data.
     
    Two test cases are provided. The first allows the brightness of the Lauchpads LED to be set using 0 to 9 and a to j.
    The second test case implements a simple serial protocol to set the PWM value of all 15 outputs. The packet format is...
    0x55 0xAA PWM0 PWM1 ...... PWM13 PWM14 Checksum
    Addressing could easily be added to allow multiple MSP430 on the same serial line.
     
    Since this code uses only a single TimerA module, it can run on any MSP430 with a TimerA.
     

    #include "msp430g2553.h" #include "string.h" typedef struct { // PWM ISR linked list node struct unsigned time; // Time for on/off action to occur unsigned port_off; // Port pins to turn off unsigned port_on; // Port pins to turn on void *next; // Next node in linked list } TPWM; // TPWM pw[16], *pi; // Array and inactive list head volatile TPWM *pa; // Active node unsigned char rx_buf[32]; unsigned char *head, *tail; void pwm_set(const unsigned pin, const unsigned time) { const unsigned mask = 1 << pin; // const unsigned tm = time & 0xFF00; // Limit closeness of entries TPWM *b, *p, *n; // // // -- Try to find existing active node for this pin for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) { if(p->port_off & mask) { // Found it if(p->time == tm) return; // - Time is the same, nothing to do, return... while(pa != p); // Wait for this node to be active while(pa == p); // Wait for this node to become inactive // Safe to remove now if(p->port_off == mask) { // - Node is only used for this pin, remove it b->next = p->next; // Remove link p->next = pi; // Add to inactive list pi = p; // } else { // - Node is used for multiple pins p->port_off &= ~mask; // Remove this pin from the node } // break; // Found the pin, so stop searching } // } // // - Update first node in list if(tm == 0) { // If time is 0, turn off and return pw[0].port_on &= ~mask; // pw[0].port_off |= mask; // return; // } else { // If time is non-zero, turn on pw[0].port_on |= mask; // pw[0].port_off &= ~mask; // if(time == 0xFFFF) return; // If max, no need to turn off, so return } // // // Find where the new turn off node will go for(b = &pw[0], p = b->next; p != &pw[0]; b = p, p = b->next) if(p->time >= tm) break; // Stop when an entry of >= time is found // if(p->time == tm) { // If same time, use existing node p->port_off |= mask; // Add this pin return; // All done... } // // n = pi; // Get a node from the inactive list pi = n->next; // // n->port_off = mask; // Setup new node n->port_on = 0; // n->time = tm; // // n->next = p; // Insert node in to active list b->next = n; // } void pwm_init(void) { unsigned n; memset(pw, 0, sizeof(pw)); // Clear entire array pa = &pw[0]; // Active list always begins with first array element pa->next = &pw[0]; // Begin with only 1 node in list pi = &pw[1]; // First inactive node is second array element for(n = 1; n < sizeof(pw)/sizeof(TPWM) - 1; ++n) // Link the inactive nodes pw[n].next = &pw[n + 1]; // // TACTL = TASSEL_2 + MC_2 + ID_0; // Setup timer, continuous mode, SMCLK/1 TACCTL2 = CCIE; // Enable timer interrupt _EINT(); // Enable interrupts } #pragma vector = TIMER0_A1_VECTOR __interrupt void Timer0_A1(void) { static unsigned bit_mask = 0; // Serial rx data bitmask static unsigned rx_data; // Serial rx data // const unsigned x = TAIV; // Clear interrupt flag, get vector // if(x == 4) { // --- CCR2 - PWM P1OUT &= ~pa->port_off; // Port pins off P2OUT &= ~pa->port_off >> 8; // P1OUT |= pa->port_on; // Port pins on P2OUT |= pa->port_on >> 8; // pa = pa->next; // Next node in list TACCR2 = pa->time; // Update timer compare time // } else if(x == 2) { // --- CCR1 - Serial rx if(TACCTL1 & CAP) { // Start bit (capture mode) rx_data = 0; // Clear rx data bit_mask = 1; // Begin with bit 0 TACCTL1 &= ~CAP; // Switch from capture to compare mode TACCR1 += 2500; // Set timer for middle of first data bit } else { // Data/stop (compare mode) if(bit_mask & 0x0100) { // Stop bit? *head++ = rx_data; // Add rx data to ring buffer if(head >= &rx_buf[sizeof(rx_buf)/sizeof(rx_buf[0])]) head = rx_buf; TACCTL1 |= CAP; // Switch from compare to capture mode } else { // Data bit if(TACCTL1 & SCCI) rx_data |= bit_mask; // Update rx data bit_mask <<= 1; // Next data bit TACCR1 += 1667; // Set timer for middle of next bit } } } } unsigned char get_rx(void) { const unsigned char c = *tail++; if(tail >= &rx_buf[sizeof(rx_buf)/sizeof(rx_buf[0])]) tail = rx_buf; return c; } void protocol(void) { static unsigned state = 0; unsigned n; unsigned char pwm[15]; unsigned char checksum; unsigned char c; for(; { c = get_rx(); switch(state) { case 0: // First byte of packet must be 0x55 if(c == 0x55) ++state; break; case 1: // Second byte of packet must be 0xAA if(c == 0xAA) { n = 0; checksum = 0; ++state; } else { state = 0; } break; case 2: // Get 15 PWM bytes pwm[n++] = c; checksum += c; if(n >= 15) ++state; break; case 3: // Get checksum if(c == checksum) { // Update PWM if checksum is valid for(n = 0; n < 15; ++n) { pwm_set((n < 2) ? n : n + 1, (unsigned)pwm[n] << 8); } } state = 0; // Next packet break; } } } void main(void) { unsigned char c; WDTCTL = WDTPW | WDTHOLD; // Disable watchdog DCOCTL = 0; // Run at 16 MHz BCSCTL1 = CALBC1_16MHZ; // DCOCTL = CALDCO_16MHZ; // P1OUT = P2OUT = 0; // P1DIR = 0xFF ^ BIT2; // P1.2 is serial in, all other output P1SEL = BIT2; // Enable capture interrupt P2SEL = 0; // Allow P2.6 and P2.7 to be used as GPIO P2DIR = 0xFF; // Enable all P2 pins as output pwm_init(); // Initialize software PWM TACCTL1 = CM_2 | CAP | CCIE; // Enable capture on falling edge head = tail = rx_buf; // Init rx ring buffer pointers #if 0 protocol(); // Serial control of all 15 PWM outputs #else for(; { // Control LP LEDs with 0-9 and a-j if(head != tail) { c = get_rx(); if(c >= '0' && c <= '9') { pwm_set(0, (c - '0') * 7000); } else if(c >= 'a' && c <= 'j') { pwm_set(6, (c - 'a') * 7000); } } } #endif }
  25. Like
    oPossum reacted to GeekDoc in Budget Workbench Necessites   
    BRILLIANT!  We need a "Cheese Profile" for the optimal quesadillas and nachos!
×
×
  • Create New...