Jump to content
43oh

campbellsan

Members
  • Content Count

    34
  • Joined

  • Last visited

  • Days Won

    3

Reputation Activity

  1. Like
    campbellsan got a reaction from Automate in YABB - Breadboard Booster on steroids   
    I now have SPI communication going between the MSP MCU and the CPLD.
     
    I am using the slave part of this open source SPI implementation:
     
    http://opencores.org/project,spi_master_slave
  2. Like
    campbellsan got a reaction from bluehash in YABB - Breadboard Booster on steroids   
    So here is some VHDL I used to test our prototype boards:
     

    library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity tester is Port ( clock : in STD_LOGIC; p1_drivers : OUT std_logic_vector(15 downto 0) := (others => 'Z'); p2_drivers : OUT std_logic_vector(14 downto 0) := (others => 'Z') ); end tester; architecture behavioral of tester is signal counter : std_logic_vector (21 downto 0); begin process (clock) begin if (rising_edge(clock)) then counter <= counter + 1; p1_drivers <= counter (21 downto 6); p2_drivers <= counter (21 downto 7); end if; end process; end behavioral;
     
    What it implements is a very simple clock divider in the form of a 22 bit counter. I could have allocated all 22 bits to different pins to generate a different frequency on each pin. However, I found that doing that spread the frequency range so far that my oscilloscope could not see the fastest signals while the slowest were below 0.25 Hz and time consuming to measure. Accordingly I allocated the outputs to a useful range inside the counter.
     
    I left the fitter to map external pins for me since I did not care which pin came out where:
     

    NET "clock" BUFG = "CLK"; CONFIG PROHIBIT = "P13"; CONFIG PROHIBIT = "P14";
     
    The first line tells the silicon compiler that I want clock to be a, well, clock. The fitter then automatically assigns special pin GCK1 to this signal. This is in turn wired on the booster pack to P1.4 of the MCU, which just happens to be a special purpose I/O clock output pin on the MSP430G2553. The two PROHIBIT lines prevent the compiler using pins P13 and P14 which interfere with the UART functions on the MSP430 as discussed earlier in this thread (and now fixed in the production design).
     
    Even this bit of test code has its uses. While its unlikely you'd ever want 30 odd different clock frequencies, its not uncommon to need two or three and there is plenty of room left over in the CPLD for it to do more stuff at the same time.
  3. Like
    campbellsan reacted to Automate in YABB - Breadboard Booster on steroids   
    This is TI's recomendations on the new 40 Pin Boosters http://processors.wiki.ti.com/index.php/BYOBThe extra rows of pins are inside of the existing MSP430 Booster rows.
  4. Like
    campbellsan got a reaction from Automate in YABB - Breadboard Booster on steroids   
    Progress ...
     
    Everything seems to be working on all boards except pins 13 and 14.
     
    This is because they are connected to P1.1 and P1.2 of the MCU. When these pins are used in a design they glitch at a certain point during the programming cycle which is enough to upset the UART connection from the host to the MCU. This happens even if they are set to a 'Z' initial state or used as an input.
     
    So unfortunately for the prototype boards, these two pins cannot be used on the CPLD. I will consider what this means for the next revision of the board. One possibility is to add two more jumpers, another is to just accept that the MCU will not talk to the CPLD on these two pins. I think the jumper solution is preferable, since P1.1 and P1.2 have SOMI and SIMO SPI functions that would be nice to have available.
     
    I also discovered that attempting to use these two pins can also brick the board, since UART communications are interrupted if the pins are active. I created an eraseCPLD MCU program that recovers from this if anyone runs into it. MSP430 JTAG programming is not affected, so a self contained CPLD reset program still works and is small enough to not need the UART.
     
    It is easy to exclude these two pins from the design, you just add:
     
    CONFIG PROHIBIT = "P13";
    CONFIG PROHIBIT = "P14";
     
    to the constraints file.
     
    While on the subject of SPI (the other MSP SPI port should work fine, even with these prototype boards), I have found an open source SPI VHDL implementation that should fit into one of these devices nicely, which will make this board into an awesome I/O expander amongst its many uses.
     
    @Automate, I will ship you a board tomorrow.
  5. Like
    campbellsan got a reaction from xpg in YABB - Breadboard Booster on steroids   
    Finally got parts and made one of these and it works great.
     
    I also set up the Xilinx development flow on Linux and proved it works (I only used the windows version in the past).
     
    For those of you on Linux, Xilinx only supports SUSE 11 and RHEL 5 and 6. Since neither of these are free, I tried OpenSUSE 11 which worked fine (OpenSUSE12 had dynamic library conflicts). A Fedora distro from around the RHEL 5 era might work, but I did not try it. For other distros, you'd be on your own though I'll help if I have the knowledge.
     
    To whet your appetite here is some simple Hello world VHDL:
     
    entity light_LED_PB is Port (S2: in STD_LOGIC:='H'; LED1 : out STD_LOGIC:='0';
    LED2 : out STD_LOGIC:='0');
    end light_LED_PB;
     
    architecture Behavioral of light_LED_PB is
    begin
     
    LED1 <= S2;
    LED2 <= not S2;
     
    end Behavioral;
     
    This will alternate between the green and red Launchpad LED's when Launchpad button S2 is pressed. For it to work, you need to set the host MSP430 to all inputs with pull-up resistors enabled (the loader program does this when it completes). It also works with no processor installed* (just to prove its really the CPLD that is doing the work).
    * You do need a pull-up resistor on Port 1.3 in this case
     
    You use a User Constraint File to tell the Xilinx tool chain which pins correspond to which signals in the design, so:
     
    #PINLOCK_BEGIN
     
    NET "S2" LOC = "S:PIN40";
    NET "LED1" LOC = "S:PIN18";
    NET "LED2" LOC = "S:PIN39";
     
    #PINLOCK_END
     
    ... does this for you. You then build it using the Xilinx tool chain and use an MSP430 program and Java based front end to load the resulting xsvf file into CPLD. That's it! That's how easy it is to design and program this part.
     
    I have enough parts to make another 8 or so of these boards. Because these are prototype boards and have a strap on them, I'd be willing to make a few available for free if anyone is interested in playing with an early version. Of course, I'll be making the support software I have created available, including the CPLD programmer for the MSP430 and the Java front end.
     
    I have the final board routed and ready to go, but I'd like to play around with this version and get some feedback before making more boards. The final board will have the TQFP100 package which provides more I/O and offers a choice of 72 and 144 macrocell devices.
     
    Let me know if you'd like one of these prototypes. I would be grateful if some of those who take me up on this offer would help write some tutorial materials and document tool set up for different host OS's etc.
     
    Let me know what you think. Pictures coming soon.
  6. Like
    campbellsan got a reaction from gordon in YABB - Breadboard Booster on steroids   
    Here is the finished prototype board:
     

     
    Note the jumpers in the 'Program' state. Move them to the left if you wish to use those MSP pins in your project.
  7. Like
    campbellsan got a reaction from bluehash in YABB - Breadboard Booster on steroids   
    Here is the finished prototype board:
     

     
    Note the jumpers in the 'Program' state. Move them to the left if you wish to use those MSP pins in your project.
  8. Like
    campbellsan got a reaction from RobG in YABB - Breadboard Booster on steroids   
    And here is the underside with the CPLD:
     

     
    Oh my! The camera makes those CPLD solder joints look ugly. In real life they're just fine.
     
    Note the ugly strap which fixes the mistake I made in the design. Production boards won't have those of course.
     
    I put some hot glue on the strap to make sure the pin doesn't break off the CPLD.
  9. Like
    campbellsan got a reaction from RobG in YABB - Breadboard Booster on steroids   
    Here is the finished prototype board:
     

     
    Note the jumpers in the 'Program' state. Move them to the left if you wish to use those MSP pins in your project.
  10. Like
    campbellsan got a reaction from RobG in YABB - Breadboard Booster on steroids   
    Finally got parts and made one of these and it works great.
     
    I also set up the Xilinx development flow on Linux and proved it works (I only used the windows version in the past).
     
    For those of you on Linux, Xilinx only supports SUSE 11 and RHEL 5 and 6. Since neither of these are free, I tried OpenSUSE 11 which worked fine (OpenSUSE12 had dynamic library conflicts). A Fedora distro from around the RHEL 5 era might work, but I did not try it. For other distros, you'd be on your own though I'll help if I have the knowledge.
     
    To whet your appetite here is some simple Hello world VHDL:
     
    entity light_LED_PB is Port (S2: in STD_LOGIC:='H'; LED1 : out STD_LOGIC:='0';
    LED2 : out STD_LOGIC:='0');
    end light_LED_PB;
     
    architecture Behavioral of light_LED_PB is
    begin
     
    LED1 <= S2;
    LED2 <= not S2;
     
    end Behavioral;
     
    This will alternate between the green and red Launchpad LED's when Launchpad button S2 is pressed. For it to work, you need to set the host MSP430 to all inputs with pull-up resistors enabled (the loader program does this when it completes). It also works with no processor installed* (just to prove its really the CPLD that is doing the work).
    * You do need a pull-up resistor on Port 1.3 in this case
     
    You use a User Constraint File to tell the Xilinx tool chain which pins correspond to which signals in the design, so:
     
    #PINLOCK_BEGIN
     
    NET "S2" LOC = "S:PIN40";
    NET "LED1" LOC = "S:PIN18";
    NET "LED2" LOC = "S:PIN39";
     
    #PINLOCK_END
     
    ... does this for you. You then build it using the Xilinx tool chain and use an MSP430 program and Java based front end to load the resulting xsvf file into CPLD. That's it! That's how easy it is to design and program this part.
     
    I have enough parts to make another 8 or so of these boards. Because these are prototype boards and have a strap on them, I'd be willing to make a few available for free if anyone is interested in playing with an early version. Of course, I'll be making the support software I have created available, including the CPLD programmer for the MSP430 and the Java front end.
     
    I have the final board routed and ready to go, but I'd like to play around with this version and get some feedback before making more boards. The final board will have the TQFP100 package which provides more I/O and offers a choice of 72 and 144 macrocell devices.
     
    Let me know if you'd like one of these prototypes. I would be grateful if some of those who take me up on this offer would help write some tutorial materials and document tool set up for different host OS's etc.
     
    Let me know what you think. Pictures coming soon.
  11. Like
    campbellsan reacted to cde in YABB - Breadboard Booster on steroids   
    From the looks of it, the msp430 on the launchpad would be programmed as a pass through bit-banged jtag interface on pins p2.0-p2.2, based on the jumper selection made. Or are you asking how as in what the cpld would be used for?
  12. Like
    campbellsan got a reaction from Automate in YABB - Breadboard Booster on steroids   
    YABB - Yet Another Breadboard Booster (this time on steroids)
     
    Launchpad Booster integrated breadboards are undoubtedly very handy for learning and experimentation. However, there is no denying the obvious; you can't squeeze very many components onto a breadboard this size.
     
    So, what about this? A breadboard Booster Pack with a CPLD lurking beneath?
    I chose a Xilinx XC9572XL which is very inexpensive and puts 3,200 re-programmable logic gates at your fingertips. You'd need a breadboard the size of a desktop to lay out that much logic on a traditional breadboard!
     
    Because the CPLD is mounted on the reverse side of the board, it has exactly the same 50mm form factor as other conventional breadboard Boosters. It therefore leaves access to the Launchpad LEDs and buttons. I included space for an optional crystal to clock the programmable logic. SInce this can theoretically go up to 178 MHz, it gives access to solving problems an MSP430 on its own could never tackle.
     
    Even if you're not ready to start playing with programmable logic, this board would be worth having. The default CPLD configuration will be for the MSP430 pins to pass straight through, so it will be just like using a regular breadboard Booster. However once you're ready, this little board will grow with you.
     
    I have based the layout around a little breadboard from Seeed Studio which measures 3.5 x 4.5 cm, but of course I would be open to better suggestions.
     
    Let me know what you think. If there is interest, I will post the schematic and board files I have so far.
  13. Like
    campbellsan reacted to gordon in LP Booster Pack Eagle footprints   
    (OK, I have no idea where to put this. BH, feel free to move it around.)
     
    Continuing from viewtopic.php?f=9&t=1348&start=40#p11369
     
    This is the final version; I don't think it can be made any better as well as generic at the same time.
     
    All variants verified against the TI LaunchPad Eagle files.
     
    Thanks to SugarAddict for the corrections.
     
    Pro tip: make your References layer yellow (or any other unused bright color) while placing your components.
     
    Addendum: DipTrace users look here: viewtopic.php?f=35&t=2972
    TI_launchpad.zip
  14. Like
    campbellsan got a reaction from bluehash in Nokia 5110 Booster Pack   
    Hey Rob,
     
    If you would like a sample with a different pinout, please just let me know.
     
    I bought five, which is one more than I am likely to use.
  15. Like
    campbellsan got a reaction from bluehash in YABB - Breadboard Booster on steroids   
    Gerbers @ seeed.
     
    I'll keep you posted on progress.
  16. Like
    campbellsan got a reaction from RobG in YABB - Breadboard Booster on steroids   
    OK, here's the schema:
     

     
    and the laid out board:
     

  17. Like
    campbellsan got a reaction from xpg in YABB - Breadboard Booster on steroids   
    YABB - Yet Another Breadboard Booster (this time on steroids)
     
    Launchpad Booster integrated breadboards are undoubtedly very handy for learning and experimentation. However, there is no denying the obvious; you can't squeeze very many components onto a breadboard this size.
     
    So, what about this? A breadboard Booster Pack with a CPLD lurking beneath?
    I chose a Xilinx XC9572XL which is very inexpensive and puts 3,200 re-programmable logic gates at your fingertips. You'd need a breadboard the size of a desktop to lay out that much logic on a traditional breadboard!
     
    Because the CPLD is mounted on the reverse side of the board, it has exactly the same 50mm form factor as other conventional breadboard Boosters. It therefore leaves access to the Launchpad LEDs and buttons. I included space for an optional crystal to clock the programmable logic. SInce this can theoretically go up to 178 MHz, it gives access to solving problems an MSP430 on its own could never tackle.
     
    Even if you're not ready to start playing with programmable logic, this board would be worth having. The default CPLD configuration will be for the MSP430 pins to pass straight through, so it will be just like using a regular breadboard Booster. However once you're ready, this little board will grow with you.
     
    I have based the layout around a little breadboard from Seeed Studio which measures 3.5 x 4.5 cm, but of course I would be open to better suggestions.
     
    Let me know what you think. If there is interest, I will post the schematic and board files I have so far.
  18. Like
    campbellsan got a reaction from bluehash in YABB - Breadboard Booster on steroids   
    OK, here's the schema:
     

     
    and the laid out board:
     

  19. Like
    campbellsan got a reaction from bluehash in YABB - Breadboard Booster on steroids   
    Hi BH,
     
    The board is set up to use the Launchpad processor to do the programming.
     
    No additional kit needed, just move three jumpers to program, move 'em back to run.
     
    C.
  20. Like
    campbellsan got a reaction from bluehash in YABB - Breadboard Booster on steroids   
    YABB - Yet Another Breadboard Booster (this time on steroids)
     
    Launchpad Booster integrated breadboards are undoubtedly very handy for learning and experimentation. However, there is no denying the obvious; you can't squeeze very many components onto a breadboard this size.
     
    So, what about this? A breadboard Booster Pack with a CPLD lurking beneath?
    I chose a Xilinx XC9572XL which is very inexpensive and puts 3,200 re-programmable logic gates at your fingertips. You'd need a breadboard the size of a desktop to lay out that much logic on a traditional breadboard!
     
    Because the CPLD is mounted on the reverse side of the board, it has exactly the same 50mm form factor as other conventional breadboard Boosters. It therefore leaves access to the Launchpad LEDs and buttons. I included space for an optional crystal to clock the programmable logic. SInce this can theoretically go up to 178 MHz, it gives access to solving problems an MSP430 on its own could never tackle.
     
    Even if you're not ready to start playing with programmable logic, this board would be worth having. The default CPLD configuration will be for the MSP430 pins to pass straight through, so it will be just like using a regular breadboard Booster. However once you're ready, this little board will grow with you.
     
    I have based the layout around a little breadboard from Seeed Studio which measures 3.5 x 4.5 cm, but of course I would be open to better suggestions.
     
    Let me know what you think. If there is interest, I will post the schematic and board files I have so far.
  21. Like
    campbellsan got a reaction from gordon in YABB - Breadboard Booster on steroids   
    YABB - Yet Another Breadboard Booster (this time on steroids)
     
    Launchpad Booster integrated breadboards are undoubtedly very handy for learning and experimentation. However, there is no denying the obvious; you can't squeeze very many components onto a breadboard this size.
     
    So, what about this? A breadboard Booster Pack with a CPLD lurking beneath?
    I chose a Xilinx XC9572XL which is very inexpensive and puts 3,200 re-programmable logic gates at your fingertips. You'd need a breadboard the size of a desktop to lay out that much logic on a traditional breadboard!
     
    Because the CPLD is mounted on the reverse side of the board, it has exactly the same 50mm form factor as other conventional breadboard Boosters. It therefore leaves access to the Launchpad LEDs and buttons. I included space for an optional crystal to clock the programmable logic. SInce this can theoretically go up to 178 MHz, it gives access to solving problems an MSP430 on its own could never tackle.
     
    Even if you're not ready to start playing with programmable logic, this board would be worth having. The default CPLD configuration will be for the MSP430 pins to pass straight through, so it will be just like using a regular breadboard Booster. However once you're ready, this little board will grow with you.
     
    I have based the layout around a little breadboard from Seeed Studio which measures 3.5 x 4.5 cm, but of course I would be open to better suggestions.
     
    Let me know what you think. If there is interest, I will post the schematic and board files I have so far.
  22. Like
    campbellsan got a reaction from woodgrain in $4.30 LCD Booster - Sold Out   
    B#, I posted the code above as you suggested.
     
    I'd like to see it go into version control because I see more work being done on it and collaboration is more fun under a CM system.
     
    Note that this library relies on RobG's sed1565.h file available in the Code Vault. Thanks Rob.
    It does not rely on the sed1565.c file because I shamelessly plagiarised the parts of his excellent work I needed. Thanks Rob.
     
    This library also relies for text rendering on the font5x7.h file also available in the Code Vault. Another enhancement that occurs to me is to support all the fonts that I saw in there and have a font switch command, but that might start getting greedy on flash.
     
    Let's see where this goes. Enjoy!
     
    BTW, I'm sure no one expects any, but just in case, no warranties and all that usual stuff.... if heart monitor development is what you're doing with this, you're on your own. ;-)
  23. Like
    campbellsan got a reaction from woodgrain in $4.30 LCD Booster - Sold Out   
    Ok, good progress.
     
    I have a library that will plot lines, circles and text across the whole display.
     
    To use it, you make calls to draw_line, draw_text etc. and then call render. When render is called the resulting image is plotted to the screen. You can't make arbitrary draw calls straight to the screen for the reasons given earlier; it is not possible to read the controllers display memory, so plotting has to be done in SRAM before writing the data to the display. If we had enough SRAM, direct plotting would be possible. However 512 bytes in a MS430G2553 is only enough to cover less than 43 lines out of the possible 65 display lines and in any case, we need SRAM for variables and stack.
     
    Instead, I keep a command list and use a smaller buffer of 96 bytes. When you call a draw command, it is added to the command list. When render is called, the commands are plotted into the 96 byte buffer and written to the display. This is repeated 8 times to cover the whole display. This isn't CPU efficient, but it is SRAM efficient. With a little more work, the code could be enhanced so that you can trade off the size of the buffer against the number of times the renderer has to repeat the plotting behaviour and write the data. CPU efficiency aside, it manages 16 frames a second plotting a clock face. I have implemented some optimizations in the drawing algorithms so that overhead is reduced if a particular graphic has no presence in a particular strip of the output buffer, but more could be done.
     
    Another tradeoff which limits the complexity of the image is the size of the command buffer, this requires 5 bytes per command. I have thought about SRAM usage here too and provided an extended render command that allows the user to specify pre and post plot command lists which can be anywhere in memory, including flash. So you can plot a static background, some dynamic graphics and a static foreground. Of course, the back and foreground can be semi static, since you don't have to point at the same static command lists for every render_extended call.
     
    I found that the pixels are not square, so circles come out as ellipses. I added a y axis scaling factor to rectify this. You will be able to turn this off at compile time if you don't want it, but it is quite quick. Text output is not scaled so that the fonts look as they were intended to.
     
    Apart from the SRAM/CPU tradeoff enhancement, I was thinking of adding a simple draw_point command, an XOR drawing mode and shape fill capabilities.
     
    Please let me know what you would like to see.
     
    Library source:

    /* * sed1565gr.c * * Created on: May 12, 2012 * Author: campbell */ #include "msp430g2553.h" #include "sed1565gr.h" #include "sed1565.h" #include "stddef.h" #include"msp_intrinsics.h" #include"font_5x7.h" #include #include #define MAX_COMMANDS 10 #define BUFFER_SIZE 96 #define PAGE_SIZE 8 #define NUM_PAGES 9 // private data GR_CMD dynamic_command_list[MAX_COMMANDS]; char buffer[bUFFER_SIZE]; int cmd_index = 0; int extent_y; // private functions int buffers_remain(void); void prepare_context(void); void perform(GR_CMD command); void write_lcd(); void perform_line(DRAW_LINE_CMD* cmd); void perform_circle(DRAW_CIRCLE_CMD* cmd); void perform_text(DRAW_TEXT_CMD* cmd); void inline plot(int x, int y); void inline plot_raw(int x, int y); int abs(int arg); void clear() { cmd_index = 0; } void draw_line(char start_x, char start_y, char end_x, char end_y) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_LINE; dynamic_command_list[cmd_index].dlc.start_x = start_x; dynamic_command_list[cmd_index].dlc.start_y = start_y; dynamic_command_list[cmd_index].dlc.end_x = end_x; dynamic_command_list[cmd_index].dlc.end_y = end_y; cmd_index++; } } void draw_circle(char center_x, char center_y, char radius) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_CIRCLE; dynamic_command_list[cmd_index].dcc.center_x = center_x; dynamic_command_list[cmd_index].dcc.center_y = center_y; dynamic_command_list[cmd_index].dcc.radius = radius; cmd_index++; } } void draw_text(char x, char y, char *str) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_TEXT; dynamic_command_list[cmd_index].dtc.x = x; dynamic_command_list[cmd_index].dtc.y = y; dynamic_command_list[cmd_index].dtc.string = str; cmd_index++; } } void render() { render_extended(0, NULL, 0, NULL); } int bfr_indx; void render_extended(char pre_cmd_count, GR_CMD* pre_cmd_list, char post_cmd_count, GR_CMD* post_cmd_list) { prepare_context(); while (buffers_remain()) { bfr_indx = 0; // First render the passed command list, if one was passed while (bfr_indx < pre_cmd_count) { perform(pre_cmd_list[bfr_indx++]); } // Now render the dynamic command list bfr_indx = 0; while (bfr_indx < cmd_index) { perform(dynamic_command_list[bfr_indx++]); } // Now render the second passed command list, if there is one bfr_indx = 0; while (bfr_indx < post_cmd_count) { perform(post_cmd_list[bfr_indx++]); } write_lcd(); } clear(); } void perform(GR_CMD command) { switch (command.cmd) { case CMD_LINE: perform_line(&(command.dlc)); break; case CMD_CIRCLE: perform_circle(&(command.dcc)); break; case CMD_TEXT: perform_text(&(command.dtc)); break; default: //error case, halt break; } } int inline abs(int arg) { if (arg > 0) { return arg; } else { return -arg; } } void inline swap(int *a, int * { int temp = *a; *a = *b; *b = temp; } void perform_line(DRAW_LINE_CMD* cmd) { // Bresenham line algorithm int dx = (cmd->end_x - cmd->start_x); int dy = (cmd->end_y - cmd->start_y); int x = cmd->start_x; int y = cmd->start_y; int endx = cmd->end_x; int endy = cmd->end_y; plot(x, y); if (abs(dx) > abs(dy)) { if (x > endx) { swap(&x, &endx); swap(&y, &endy); dx = -dx; dy = -dy; } int p = (dy - dx) << 1; while (x <= endx) { if (p < 0) { x = x + 1; p = p + (dy << 1); } else { x = x + 1; y = y + 1; p = p + ((dy - dx) << 1); } plot(x, y); } } else { if (y > endy) { swap(&x, &endx); swap(&y, &endy); dx = -dx; dy = -dy; } int p = (dx - dy) << 1; while (y <= endy) { if (p < 0) { y = y + 1; p = p + (dx << 1); } else { x = x + 1; y = y + 1; p = p + ((dx - dy) << 1); } plot(x, y); } } } void perform_circle(DRAW_CIRCLE_CMD* cmd) { int x = 0; int y = cmd->radius; plot(cmd->center_x + x, cmd->center_y - y); int p = 3 - (cmd->radius << 1); for (x = 0; x <= y; x++) { if (p < 0) { p = (p + (x << 2) + 6); } else { y = y - 1; p = p + ((((x - y) << 2) + 10)); } plot(cmd->center_x + x, cmd->center_y - y); plot(cmd->center_x - x, cmd->center_y - y); plot(cmd->center_x + x, cmd->center_y + y); plot(cmd->center_x - x, cmd->center_y + y); plot(cmd->center_x + y, cmd->center_y - x); plot(cmd->center_x - y, cmd->center_y - x); plot(cmd->center_x + y, cmd->center_y + x); plot(cmd->center_x - y, cmd->center_y + x); } } void perform_text(DRAW_TEXT_CMD* cmd) { if (cmd->y >= extent_y - 7 && cmd->y < extent_y + PAGE_SIZE) { char * str = cmd->string; int col = 0; while (*str) { char c = *str++; int char_col = 0; while (char_col < 5) { char slice = font[c - 0x20][char_col]; int row = 0; while (row < 7) { if (slice & 0x01) { plot_raw(cmd->x + col, cmd->y + row); } slice = slice >> 1; row++; } char_col++; col++; } col++; } } } void inline plot(int x, int y) { #ifndef DONT_SCALE y -= (y >> 3); #endif plot_raw(x, y); } void inline plot_raw(int x, int y) { if (y >= extent_y && y < (extent_y + PAGE_SIZE)) { buffer[x] |= 1 << (y - extent_y); } } void prepare_context() { extent_y = -PAGE_SIZE; } void write_lcd() { int i = 0; // Set column to 0x12, the left hand edge of the LCD writeCommand(0x02); // Column Address LSN writeCommand(0x11); // Column Address HSN writeCommand(0xB0 | (extent_y >> 3)); // Page Address for (; i < BUFFER_SIZE; i++) { writeData(buffer[i]); } } int buffers_remain() { memset(buffer, 0, BUFFER_SIZE); extent_y += PAGE_SIZE; return NUM_PAGES - (extent_y >> 3); } void initLCD() { P2OUT |= LCD_ON_PIN + LCD_BL_PIN; _delay_cycles(0xffff); P1OUT |= LCD_RESET_PIN; writeCommand(DISPLAY_REVERSE); writeCommand(LCD_BIAS_1_7); writeCommand(ADC_SELECT_REVERSE); writeCommand(COMMON_OUTPUT_MODE_NORMAL); writeCommand(V5_VOLTAGE_REG_RATIO | 2); writeCommand(ELECTRONIC_VOLUME); writeCommand(0x2E); // 2E writeCommand(POWER_CONTROLLER_ALL_ON); writeCommand(0x40); // display start line 0 writeCommand(DISPLAY_ON); } void write(char data, char registerSelect) { char c = 0; LCD_SELECT; registerSelect ? (SET_DATA) : (SET_COMMAND); while (c < 8) { (data & BIT7) ? (P2OUT |= LCD_SDATA_PIN) : (P2OUT &= ~LCD_SDATA_PIN); LCD_CLOCK; data <<= 1; c++; } LCD_DESELECT; }
     
    Library Header:

    /* * sed1565gr.h * * Created on: May 12, 2012 * Author: campbell */ #ifndef SED1565GR_H_ #define SED1565GR_H_ #include "sed1565.h" #include enum CMD { CMD_LINE, CMD_CIRCLE, CMD_ELLIPSE, CMD_TEXT }; typedef struct { char start_x; char start_y; char end_x; char end_y; } DRAW_LINE_CMD; typedef struct { char center_x; char center_y; char radius; } DRAW_CIRCLE_CMD; typedef struct { char center_x; char center_y; char focus_a; char focus_b; } DRAW_ELLIPSE_CMD; typedef struct { char x; char y; char * string; } DRAW_TEXT_CMD; union { DRAW_LINE_CMD dlc; DRAW_CIRCLE_CMD dcc; DRAW_ELLIPSE_CMD dec; DRAW_TEXT_CMD dtc; } DRAW_CMD_DATA; typedef struct { enum CMD cmd; union { DRAW_LINE_CMD dlc; DRAW_CIRCLE_CMD dcc; DRAW_ELLIPSE_CMD dec; DRAW_TEXT_CMD dtc; }; } GR_CMD; void clear(); void draw_line(char start_x, char start_y, char end_x, char end_y); void draw_circle(char center_x, char center_y, char radius); void draw_ellipse(char center_x, char center_y, char focus_a, char focus_; void draw_text(char x, char y, char * str); void render(); void render_extended(char pre_cmd_count, GR_CMD* pre_commmand_list, char post_cmd_count, GR_CMD* post_command_list); #endif /* SED1565GR_H_ */
     
    Example main:

    /* * main.c */ #include "msp430g2553.h" #include "msp_intrinsics.h" #include #include "sed1565gr.h" char * msg = "www.43oh.com"; void main(void) { WDTCTL = WDTPW + WDTHOLD; // disable WDT BCSCTL1 = CALBC1_16MHZ; // 16MHz clock DCOCTL = CALDCO_16MHZ; P1OUT &= ~LCD_RESET_PIN; // keep low until init P1DIR |= LCD_RESET_PIN; P2OUT |= LCD_SCLK_PIN + LCD_CS_PIN + LCD_SDATA_PIN + LCD_DC_PIN + LCD_ON_PIN + LCD_BL_PIN; P2DIR |= LCD_SCLK_PIN + LCD_CS_PIN + LCD_SDATA_PIN + LCD_DC_PIN + LCD_ON_PIN + LCD_BL_PIN; initLCD(); draw_circle(48, 37, 36); draw_line(48, 38, 46, 5); draw_line(48, 38, 60, 55); draw_text(14, 48, msg); render(); }
  24. Like
    campbellsan got a reaction from RobG in $4.30 LCD Booster - Sold Out   
    B#, I posted the code above as you suggested.
     
    I'd like to see it go into version control because I see more work being done on it and collaboration is more fun under a CM system.
     
    Note that this library relies on RobG's sed1565.h file available in the Code Vault. Thanks Rob.
    It does not rely on the sed1565.c file because I shamelessly plagiarised the parts of his excellent work I needed. Thanks Rob.
     
    This library also relies for text rendering on the font5x7.h file also available in the Code Vault. Another enhancement that occurs to me is to support all the fonts that I saw in there and have a font switch command, but that might start getting greedy on flash.
     
    Let's see where this goes. Enjoy!
     
    BTW, I'm sure no one expects any, but just in case, no warranties and all that usual stuff.... if heart monitor development is what you're doing with this, you're on your own. ;-)
  25. Like
    campbellsan got a reaction from RobG in $4.30 LCD Booster - Sold Out   
    Ok, good progress.
     
    I have a library that will plot lines, circles and text across the whole display.
     
    To use it, you make calls to draw_line, draw_text etc. and then call render. When render is called the resulting image is plotted to the screen. You can't make arbitrary draw calls straight to the screen for the reasons given earlier; it is not possible to read the controllers display memory, so plotting has to be done in SRAM before writing the data to the display. If we had enough SRAM, direct plotting would be possible. However 512 bytes in a MS430G2553 is only enough to cover less than 43 lines out of the possible 65 display lines and in any case, we need SRAM for variables and stack.
     
    Instead, I keep a command list and use a smaller buffer of 96 bytes. When you call a draw command, it is added to the command list. When render is called, the commands are plotted into the 96 byte buffer and written to the display. This is repeated 8 times to cover the whole display. This isn't CPU efficient, but it is SRAM efficient. With a little more work, the code could be enhanced so that you can trade off the size of the buffer against the number of times the renderer has to repeat the plotting behaviour and write the data. CPU efficiency aside, it manages 16 frames a second plotting a clock face. I have implemented some optimizations in the drawing algorithms so that overhead is reduced if a particular graphic has no presence in a particular strip of the output buffer, but more could be done.
     
    Another tradeoff which limits the complexity of the image is the size of the command buffer, this requires 5 bytes per command. I have thought about SRAM usage here too and provided an extended render command that allows the user to specify pre and post plot command lists which can be anywhere in memory, including flash. So you can plot a static background, some dynamic graphics and a static foreground. Of course, the back and foreground can be semi static, since you don't have to point at the same static command lists for every render_extended call.
     
    I found that the pixels are not square, so circles come out as ellipses. I added a y axis scaling factor to rectify this. You will be able to turn this off at compile time if you don't want it, but it is quite quick. Text output is not scaled so that the fonts look as they were intended to.
     
    Apart from the SRAM/CPU tradeoff enhancement, I was thinking of adding a simple draw_point command, an XOR drawing mode and shape fill capabilities.
     
    Please let me know what you would like to see.
     
    Library source:

    /* * sed1565gr.c * * Created on: May 12, 2012 * Author: campbell */ #include "msp430g2553.h" #include "sed1565gr.h" #include "sed1565.h" #include "stddef.h" #include"msp_intrinsics.h" #include"font_5x7.h" #include #include #define MAX_COMMANDS 10 #define BUFFER_SIZE 96 #define PAGE_SIZE 8 #define NUM_PAGES 9 // private data GR_CMD dynamic_command_list[MAX_COMMANDS]; char buffer[bUFFER_SIZE]; int cmd_index = 0; int extent_y; // private functions int buffers_remain(void); void prepare_context(void); void perform(GR_CMD command); void write_lcd(); void perform_line(DRAW_LINE_CMD* cmd); void perform_circle(DRAW_CIRCLE_CMD* cmd); void perform_text(DRAW_TEXT_CMD* cmd); void inline plot(int x, int y); void inline plot_raw(int x, int y); int abs(int arg); void clear() { cmd_index = 0; } void draw_line(char start_x, char start_y, char end_x, char end_y) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_LINE; dynamic_command_list[cmd_index].dlc.start_x = start_x; dynamic_command_list[cmd_index].dlc.start_y = start_y; dynamic_command_list[cmd_index].dlc.end_x = end_x; dynamic_command_list[cmd_index].dlc.end_y = end_y; cmd_index++; } } void draw_circle(char center_x, char center_y, char radius) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_CIRCLE; dynamic_command_list[cmd_index].dcc.center_x = center_x; dynamic_command_list[cmd_index].dcc.center_y = center_y; dynamic_command_list[cmd_index].dcc.radius = radius; cmd_index++; } } void draw_text(char x, char y, char *str) { if (cmd_index < MAX_COMMANDS) { dynamic_command_list[cmd_index].cmd = CMD_TEXT; dynamic_command_list[cmd_index].dtc.x = x; dynamic_command_list[cmd_index].dtc.y = y; dynamic_command_list[cmd_index].dtc.string = str; cmd_index++; } } void render() { render_extended(0, NULL, 0, NULL); } int bfr_indx; void render_extended(char pre_cmd_count, GR_CMD* pre_cmd_list, char post_cmd_count, GR_CMD* post_cmd_list) { prepare_context(); while (buffers_remain()) { bfr_indx = 0; // First render the passed command list, if one was passed while (bfr_indx < pre_cmd_count) { perform(pre_cmd_list[bfr_indx++]); } // Now render the dynamic command list bfr_indx = 0; while (bfr_indx < cmd_index) { perform(dynamic_command_list[bfr_indx++]); } // Now render the second passed command list, if there is one bfr_indx = 0; while (bfr_indx < post_cmd_count) { perform(post_cmd_list[bfr_indx++]); } write_lcd(); } clear(); } void perform(GR_CMD command) { switch (command.cmd) { case CMD_LINE: perform_line(&(command.dlc)); break; case CMD_CIRCLE: perform_circle(&(command.dcc)); break; case CMD_TEXT: perform_text(&(command.dtc)); break; default: //error case, halt break; } } int inline abs(int arg) { if (arg > 0) { return arg; } else { return -arg; } } void inline swap(int *a, int * { int temp = *a; *a = *b; *b = temp; } void perform_line(DRAW_LINE_CMD* cmd) { // Bresenham line algorithm int dx = (cmd->end_x - cmd->start_x); int dy = (cmd->end_y - cmd->start_y); int x = cmd->start_x; int y = cmd->start_y; int endx = cmd->end_x; int endy = cmd->end_y; plot(x, y); if (abs(dx) > abs(dy)) { if (x > endx) { swap(&x, &endx); swap(&y, &endy); dx = -dx; dy = -dy; } int p = (dy - dx) << 1; while (x <= endx) { if (p < 0) { x = x + 1; p = p + (dy << 1); } else { x = x + 1; y = y + 1; p = p + ((dy - dx) << 1); } plot(x, y); } } else { if (y > endy) { swap(&x, &endx); swap(&y, &endy); dx = -dx; dy = -dy; } int p = (dx - dy) << 1; while (y <= endy) { if (p < 0) { y = y + 1; p = p + (dx << 1); } else { x = x + 1; y = y + 1; p = p + ((dx - dy) << 1); } plot(x, y); } } } void perform_circle(DRAW_CIRCLE_CMD* cmd) { int x = 0; int y = cmd->radius; plot(cmd->center_x + x, cmd->center_y - y); int p = 3 - (cmd->radius << 1); for (x = 0; x <= y; x++) { if (p < 0) { p = (p + (x << 2) + 6); } else { y = y - 1; p = p + ((((x - y) << 2) + 10)); } plot(cmd->center_x + x, cmd->center_y - y); plot(cmd->center_x - x, cmd->center_y - y); plot(cmd->center_x + x, cmd->center_y + y); plot(cmd->center_x - x, cmd->center_y + y); plot(cmd->center_x + y, cmd->center_y - x); plot(cmd->center_x - y, cmd->center_y - x); plot(cmd->center_x + y, cmd->center_y + x); plot(cmd->center_x - y, cmd->center_y + x); } } void perform_text(DRAW_TEXT_CMD* cmd) { if (cmd->y >= extent_y - 7 && cmd->y < extent_y + PAGE_SIZE) { char * str = cmd->string; int col = 0; while (*str) { char c = *str++; int char_col = 0; while (char_col < 5) { char slice = font[c - 0x20][char_col]; int row = 0; while (row < 7) { if (slice & 0x01) { plot_raw(cmd->x + col, cmd->y + row); } slice = slice >> 1; row++; } char_col++; col++; } col++; } } } void inline plot(int x, int y) { #ifndef DONT_SCALE y -= (y >> 3); #endif plot_raw(x, y); } void inline plot_raw(int x, int y) { if (y >= extent_y && y < (extent_y + PAGE_SIZE)) { buffer[x] |= 1 << (y - extent_y); } } void prepare_context() { extent_y = -PAGE_SIZE; } void write_lcd() { int i = 0; // Set column to 0x12, the left hand edge of the LCD writeCommand(0x02); // Column Address LSN writeCommand(0x11); // Column Address HSN writeCommand(0xB0 | (extent_y >> 3)); // Page Address for (; i < BUFFER_SIZE; i++) { writeData(buffer[i]); } } int buffers_remain() { memset(buffer, 0, BUFFER_SIZE); extent_y += PAGE_SIZE; return NUM_PAGES - (extent_y >> 3); } void initLCD() { P2OUT |= LCD_ON_PIN + LCD_BL_PIN; _delay_cycles(0xffff); P1OUT |= LCD_RESET_PIN; writeCommand(DISPLAY_REVERSE); writeCommand(LCD_BIAS_1_7); writeCommand(ADC_SELECT_REVERSE); writeCommand(COMMON_OUTPUT_MODE_NORMAL); writeCommand(V5_VOLTAGE_REG_RATIO | 2); writeCommand(ELECTRONIC_VOLUME); writeCommand(0x2E); // 2E writeCommand(POWER_CONTROLLER_ALL_ON); writeCommand(0x40); // display start line 0 writeCommand(DISPLAY_ON); } void write(char data, char registerSelect) { char c = 0; LCD_SELECT; registerSelect ? (SET_DATA) : (SET_COMMAND); while (c < 8) { (data & BIT7) ? (P2OUT |= LCD_SDATA_PIN) : (P2OUT &= ~LCD_SDATA_PIN); LCD_CLOCK; data <<= 1; c++; } LCD_DESELECT; }
     
    Library Header:

    /* * sed1565gr.h * * Created on: May 12, 2012 * Author: campbell */ #ifndef SED1565GR_H_ #define SED1565GR_H_ #include "sed1565.h" #include enum CMD { CMD_LINE, CMD_CIRCLE, CMD_ELLIPSE, CMD_TEXT }; typedef struct { char start_x; char start_y; char end_x; char end_y; } DRAW_LINE_CMD; typedef struct { char center_x; char center_y; char radius; } DRAW_CIRCLE_CMD; typedef struct { char center_x; char center_y; char focus_a; char focus_b; } DRAW_ELLIPSE_CMD; typedef struct { char x; char y; char * string; } DRAW_TEXT_CMD; union { DRAW_LINE_CMD dlc; DRAW_CIRCLE_CMD dcc; DRAW_ELLIPSE_CMD dec; DRAW_TEXT_CMD dtc; } DRAW_CMD_DATA; typedef struct { enum CMD cmd; union { DRAW_LINE_CMD dlc; DRAW_CIRCLE_CMD dcc; DRAW_ELLIPSE_CMD dec; DRAW_TEXT_CMD dtc; }; } GR_CMD; void clear(); void draw_line(char start_x, char start_y, char end_x, char end_y); void draw_circle(char center_x, char center_y, char radius); void draw_ellipse(char center_x, char center_y, char focus_a, char focus_; void draw_text(char x, char y, char * str); void render(); void render_extended(char pre_cmd_count, GR_CMD* pre_commmand_list, char post_cmd_count, GR_CMD* post_command_list); #endif /* SED1565GR_H_ */
     
    Example main:

    /* * main.c */ #include "msp430g2553.h" #include "msp_intrinsics.h" #include #include "sed1565gr.h" char * msg = "www.43oh.com"; void main(void) { WDTCTL = WDTPW + WDTHOLD; // disable WDT BCSCTL1 = CALBC1_16MHZ; // 16MHz clock DCOCTL = CALDCO_16MHZ; P1OUT &= ~LCD_RESET_PIN; // keep low until init P1DIR |= LCD_RESET_PIN; P2OUT |= LCD_SCLK_PIN + LCD_CS_PIN + LCD_SDATA_PIN + LCD_DC_PIN + LCD_ON_PIN + LCD_BL_PIN; P2DIR |= LCD_SCLK_PIN + LCD_CS_PIN + LCD_SDATA_PIN + LCD_DC_PIN + LCD_ON_PIN + LCD_BL_PIN; initLCD(); draw_circle(48, 37, 36); draw_line(48, 38, 46, 5); draw_line(48, 38, 60, 55); draw_text(14, 48, msg); render(); }
×
×
  • Create New...