Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 05:51 26 Nov 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Maximite PWM

Author Message
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 12:27pm 02 Aug 2011
Copy link to clipboard 
Print this post

Hi All.

I have modified the Maximite firmware to provide a PWM capability.

The PWM output is available on logical pins 7 - 10. The base PWM frequency can be from 1Hz to 5000Hz. The duty time can be set from 0% to 100%. However, at frequencies above 500Hz the duty time percentage will be rounded (eg at 1000Hz the increments are at 2% and at 5000Hz the increments are 10%).

To set a pin to PWM mode, you use the SETPIN command. The format is:

SETPIN pin,cfg,frequency,pwm
where pin is 7 - 10
cfg is 10 for PWM mode
frequency is 1 - 5000 (for 1Hz to 5000Hz)
pwm is 0 - 100 (for 0% to 100% duty time)

To modify the duty time just issue the SETPIN command again with a different duty time percentage.

An example command:

SETPIN 7,10,500,25 -> set pin 7 to PWM with a frequency of 500Hz and a duty time of 25%
SETPIN 7,10,500,50 -> change the duty time to 50%

As this firmware uses the timer in the keyboard routine, I have incorporated Jim's (Jimbo) updated keyboard scan code routine (see Keyboard function key mappings post).

Firmware download: Maximite.hex

Please try and report any issues.

Regards

Gerard (vk3cg/vk3grs)Edited by seco61 2011-08-03

Regards

Gerard (vk3cg/vk3grs)
 
Gizmo

Admin Group

Joined: 05/06/2004
Location: Australia
Posts: 5078
Posted: 12:51pm 02 Aug 2011
Copy link to clipboard 
Print this post

Hi Gerard

Good work! Quick question for those wondering, does the PWM output run independently from normal program operation, ie, is it running in the background.

And will any other commands affect it, or will it affect any commands?

With I2C and PWM, the Maximite is turning into one versatile little beastie

Glenn
The best time to plant a tree was twenty years ago, the second best time is right now.
JAQ
 
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 01:06pm 02 Aug 2011
Copy link to clipboard 
Print this post

  Gizmo said   Hi Gerard

Good work! Quick question for those wondering, does the PWM output run independently from normal program operation, ie, is it running in the background.

And will any other commands affect it, or will it affect any commands?
Glenn


Hi Glenn.

The PWM output runs in the background. It should have no impact on any running program - though as there are a couple of extra lines of C code in the timer interrupt routine the MMBAsic interpreter throughput may drop slightly.

All 4 PWM pins can be running simultaneously, at different frequencies and duty times.

Regards

Gerard (vk3cg/vk3grs)

Regards

Gerard (vk3cg/vk3grs)
 
Gizmo

Admin Group

Joined: 05/06/2004
Location: Australia
Posts: 5078
Posted: 01:18pm 02 Aug 2011
Copy link to clipboard 
Print this post

Its a work of art Gerard!

I connected a LED and 330ohm in series to pin 8, then ran the following.

10 for i=1 to 100
20 setpin 8,10,2000,i
30 pause 100
40 next

And the LED progressively got brighter over a few seconds.


Glenn
The best time to plant a tree was twenty years ago, the second best time is right now.
JAQ
 
Gizmo

Admin Group

Joined: 05/06/2004
Location: Australia
Posts: 5078
Posted: 11:12pm 02 Aug 2011
Copy link to clipboard 
Print this post

Did some more testing this morning and have noticed a problem. There is a bit of video flicker, especially once a PWM command has been issued. Random lines seam to jump sideways by several pixels.

I think Geoff has come across this before, something to do with timings.

Glenn
The best time to plant a tree was twenty years ago, the second best time is right now.
JAQ
 
Dave Everett
Regular Member

Joined: 24/06/2011
Location: Australia
Posts: 43
Posted: 11:18pm 02 Aug 2011
Copy link to clipboard 
Print this post

  seco61 said   Hi All.

I have modified the Maximite firmware to provide a PWM capability.

The PWM output is available on logical pins 7 - 10. The base PWM frequency can be from 1Hz to 5000Hz. The duty time can be set from 0% to 100%. However, at frequencies above 500Hz the duty time percentage will be rounded (eg at 1000Hz the increments are at 2% and at 5000Hz the increments are 10%).



Gerard, I've been looking at doing this as well, however I want to use the onboard PWM. There are 2 of the 4 PWM pins available, and this way the frequency can be much higher. I need something between 20 and 32khz. This would mean modifiying the PCB, I intend to reuse 2 of the 3 ground lines on the 26 way to take the PWM output pins.

Dave
 
bigmik

Guru

Joined: 20/06/2011
Location: Australia
Posts: 2914
Posted: 01:16am 03 Aug 2011
Copy link to clipboard 
Print this post

  seco61 said   Hi All.

The duty time can be set from 0% to 100%.


Love it,

Thanks Gerard,

Just a question that will probably show how dumb I am but at I assume that a 50% setting means a `proper' square wave ie. high 50% (presume 3.3v) and low 50%. does that mean then that 100% is Always on?

Regards,

Mick

Mick's uMite Stuff can be found >>> HERE (Kindly hosted by Dontronics) <<<
 
haiqu

Senior Member

Joined: 30/07/2011
Location: Australia
Posts: 152
Posted: 01:25am 03 Aug 2011
Copy link to clipboard 
Print this post

Mick,

Correct!

Rob

unzip, strip, touch, finger, grep, mount, fsck, more, yes, fsck, fsck, fsck, umount, sleep
 
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 03:37am 03 Aug 2011
Copy link to clipboard 
Print this post

  Dave Everett said  Gerard, I've been looking at doing this as well, however I want to use the onboard PWM. There are 2 of the 4 PWM pins available, and this way the frequency can be much higher. I need something between 20 and 32khz. This would mean modifiying the PCB, I intend to reuse 2 of the 3 ground lines on the 26 way to take the PWM output pins.

Dave


Hi Dave.

I have also looked at this, but it would probably have to be a custom implementation as it involves modifying the base Maximite and soldering directly to a MCU pin. Whilst I have done this quite a few times, it will not be an option for many - hence the current implementation.

Having said that, the overhead of adding a custom command to use the OC1 pin as a PWM output would be slight and as it would not interfere with anything else I may as well code it and then run it by Geoff to see if he is happy to have it as an "optional" command (similar to I2C in an appendix!). Either way I will have a go and post the modified code soon.

As timer 3 is used by the video subsystem, this only leaves timer 2 for the PWM modules. So if you were to use both OC1 and OC4, they would have to run at the same frequency.

I would also love to have a RS232 capability using the onboard peripherals. I know Geoff is going to be coding a "bit banging" solution (at least I think that is the case) as the UART peripheral pins are not available at the I/O connector. However, for those who are able to solder directly to the MCU pins, pins 21 and 51 could be used to provide the Rx and Tx capability respectively.

Regards

Gerard (vk3cg/vk3grs)Edited by seco61 2011-08-04

Regards

Gerard (vk3cg/vk3grs)
 
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 01:20pm 03 Aug 2011
Copy link to clipboard 
Print this post

  seco61 said  Having said that, the overhead of adding a custom command to use the OC1 pin as a PWM output would be slight and as it would not interfere with anything else I may as well code it and then run it by Geoff to see if he is happy to have it as an "optional" command (similar to I2C in an appendix!). Either way I will have a go and post the modified code soon.


Hi Dave.

Here is my version of a PWM command that uses the OC1 output (pin 46).

The firmware is the current v2.5 with my earlier PWM, Jim's keyboard scan update and the new "external" PWM command: Maximite.hex

For the time being I have placed the external PWM code into Custom.c and Custom.h (see below).

The new command is:

PWM freq,duty
where freq is 5 - 800000 (5Hz to 800kHz) or 0 to disable
duty is 0 - 100 for 0% to 100% duty cycle

Custom.c

/*********************************************************** ************************************************************
MMBasic

custom.c

Handles all the custom commands and functions in MMBasic. These are commands and functions that are not
normally part of the core BASIC language. This is a good place to insert your own customised commands.

Copyright 2011 Geoff Graham - http://geoffg.net
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details. You should have received a copy of the GNU General Public License along with this program.
If not, see <http://www.gnu.org/licenses/>.

************************************************************ ************************************************************ /

#include <p32xxxx.h> // device specific defines
#include <plib.h> // peripheral libraries

#define INCLUDE_FUNCTION_DEFINES
#include "Maximite.h"
#include "MMBasic.h"
#include "Operators.h"
#include "Commands.h"
#include "External.h"
#include "Misc.h"
#include "Files.h"
#include "Custom.h"


/*********************************************************** ************************************************************ *********************
custom commands and functions
each function is responsible for decoding a command
all function names are in the form cmd_xxxx() (for a basic command) or fun_xxxx() (for a basic function) so, if you want to search for the
function responsible for the NAME command look for cmd_name

There are 4 items of information that are setup before the command is run.
All these are globals.

int cmdtoken This is the token number of the command (some commands can handle multiple
statement types and this helps them differentiate)

char *cmdline This is the command line terminated with a zero char and trimmed of leading
spaces. It may exist anywhere in memory (or even ROM).

char *nextstmt This is a pointer to the next statement to be executed. The only thing a
command can do with it is save it or change it to some other location.

int CurrentLineNbr This is read only and is set to zero if the command is in immediate mode.

The only actions a command can do to change the program flow is to change nextstmt or
execute longjmp(mark, 1) if it wants to abort the program.

******************** ************************************************************ ************************************************************ /

void cmd_pwm(void) {
static unsigned int pwm_enabled, pwm_freq;
unsigned int freq, pwm;

getargs(&cmdline, 3, ",");
if (argc != 3) error("Invalid syntax");
freq = getinteger(argv[0]);
if (freq == 0) {
OC1CONCLR = 0x0000ffff; // Disable the output compare module
T2CONCLR = 0x0000ffff; // Disable timer 2 module
pwm_enabled = 0;
pwm_freq = 0;
return;
}
if (freq < 5 || freq > 800000) error("Invalid frequency (must be 5 - 800000, or 0 to disable)");
pwm = getinteger(argv[2]);
if (pwm < 0 || pwm > 100) error("Invalid PWM (must be 0 - 100)");
if (!pwm_enabled || pwm_freq != freq) {
pwm_enabled = 0;
pwm_freq = freq; // Save current frequency
OC1CONCLR = 0x0000ffff; // Disable the output compare module
T2CONCLR = 0x0000ffff; // Disable timer 2 module
if (freq >= 1280) {}
else if (freq >= 640) {freq <<= 1; T2CONSET = 0x00000010;}
else if (freq >= 320) {freq <<= 2; T2CONSET = 0x00000020;}
else if (freq >= 160) {freq <<= 3; T2CONSET = 0x00000030;}
else if (freq >= 80) {freq <<= 4; T2CONSET = 0x00000040;}
else if (freq >= 40) {freq <<= 5; T2CONSET = 0x00000050;}
else if (freq >= 20) {freq <<= 6; T2CONSET = 0x00000060;}
else {freq <<= 8; T2CONSET = 0x00000070;}
TMR2 = 0; // Clear timer 2 counter
PR2 = ((BUSFREQ + (freq >> 1)) / freq) - 1; // Set timer 2 period
}
OC1RS = ((pwm * (PR2 + 1)) + 50) / 100; // Set duty period
if (!pwm_enabled) {
pwm_enabled = 1;
OC1R = OC1RS; // Set initial duty period
OC1CON = 0x00000006; // Enable PWM mode
OC1CONSET = 0x00008000; // Enable output compare module
T2CONSET = 0x00008000; // Enable timer 2
}
}


Custom.h

/*********************************************************** ************************************************************
MMBasic

custom.h

Include file that contains the globals and defines for custom.c in MMBasic.

Copyright 2011 Geoff Graham - http://geoffg.net
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details. You should have received a copy of the GNU General Public License along with this program.
If not, see <http://www.gnu.org/licenses/>.

************************************************************ ************************************************************ /



/*********************************************************** ***********************
All command tokens tokens (eg, PRINT, FOR, etc) should be inserted in this table
************************************************************ **********************/
#ifdef INCLUDE_COMMAND_TABLE
// the format is:
// TEXT TYPE P FUNCTION TO CALL
// where type is always T_CMD
// and P is the precedence (which is only used for operators and not commands)

{ "PWM", T_CMD, 0, cmd_pwm },

#endif


/*********************************************************** ***********************
All other tokens (keywords, functions, operators) should be inserted in this table
************************************************************ **********************/
#ifdef INCLUDE_TOKEN_TABLE
// the format is:
// TEXT TYPE P FUNCTION TO CALL
// where type is T_NA, T_FUN, T_FNA or T_OPER argumented by the types T_STR and/or T_NBR
// and P is the precedence (which is only used for operators)

#endif

/*********************************************************** ***********************
the C language function associated with commands, functions or operators should be
declared here
Also general definitions used by other modules
************************************************************ **********************/
#ifdef INCLUDE_FUNCTION_DEFINES

#ifndef CUSTOM_HEADER
#define CUSTOM_HEADER

// format:
// void cmd_???(void)
// void fun_???(void)
// void op_???(void)

void cmd_pwm(void);

#endif
#endif


Regards

Gerard (vk3cg/vk3grs)



Regards

Gerard (vk3cg/vk3grs)
 
Dave Everett
Regular Member

Joined: 24/06/2011
Location: Australia
Posts: 43
Posted: 12:52am 04 Aug 2011
Copy link to clipboard 
Print this post

  seco61 said  
Hi Dave.

As timer 3 is used by the video subsystem, this only leaves timer 2 for the PWM modules. So if you were to use both OC1 and OC4, they would have to run at the same frequency.


Yes that's right mate. I don't think it's a major issue. Certainly for the applications I'm thinking of (robotics) as both drive motors would be running at the same frequency anyway.

I appreciate the work you are doing.

Dave
 
Dave Everett
Regular Member

Joined: 24/06/2011
Location: Australia
Posts: 43
Posted: 01:04am 04 Aug 2011
Copy link to clipboard 
Print this post

  seco61 said  
Hi Dave.

Here is my version of a PWM command that uses the OC1 output (pin 46).


Fantastic Gerard, I hadn't put much thought into how the command would work, but the way you've handled it would also make it easy to add OC4 as another argument.

I'll get a chance to play with your hex file over the weekend, thanks mate.

Dave
 
seco61
Senior Member

Joined: 15/06/2011
Location: Australia
Posts: 205
Posted: 03:59am 04 Aug 2011
Copy link to clipboard 
Print this post

  Dave Everett said  Fantastic Gerard, I hadn't put much thought into how the command would work, but the way you've handled it would also make it easy to add OC4 as another argument.

I'll get a chance to play with your hex file over the weekend, thanks mate.

Dave


Hi Dave.

I modified the code to take an extra parameter:

PWM module,freq,pwm
where module is 1 or 4 (1 - OC1 on pin 46, 4 - OC4 on pin 51)
freq is 5 - 800000 (5Hz to 800kHz) or 0 to disable the module
pwm is 0 - 100 (for 0% to 100% duty cycle)

The firmware has been updated: Maximite.hex


The modified Custom.c


/*********************************************************** ************************************************************
MMBasic

custom.c

Handles all the custom commands and functions in MMBasic. These are commands and functions that are not
normally part of the core BASIC language. This is a good place to insert your own customised commands.

Copyright 2011 Geoff Graham - http://geoffg.net
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General
Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details. You should have received a copy of the GNU General Public License along with this program.
If not, see <http://www.gnu.org/licenses/>.

************************************************************ ************************************************************ /

#include <p32xxxx.h> // device specific defines
#include <plib.h> // peripheral libraries

#define INCLUDE_FUNCTION_DEFINES
#include "Maximite.h"
#include "MMBasic.h"
#include "Operators.h"
#include "Commands.h"
#include "External.h"
#include "Misc.h"
#include "Files.h"
#include "Custom.h"


/*********************************************************** ************************************************************ *********************
custom commands and functions
each function is responsible for decoding a command
all function names are in the form cmd_xxxx() (for a basic command) or fun_xxxx() (for a basic function) so, if you want to search for the
function responsible for the NAME command look for cmd_name

There are 4 items of information that are setup before the command is run.
All these are globals.

int cmdtoken This is the token number of the command (some commands can handle multiple
statement types and this helps them differentiate)

char *cmdline This is the command line terminated with a zero char and trimmed of leading
spaces. It may exist anywhere in memory (or even ROM).

char *nextstmt This is a pointer to the next statement to be executed. The only thing a
command can do with it is save it or change it to some other location.

int CurrentLineNbr This is read only and is set to zero if the command is in immediate mode.

The only actions a command can do to change the program flow is to change nextstmt or
execute longjmp(mark, 1) if it wants to abort the program.

******************** ************************************************************ ************************************************************ /

static unsigned int pwm_enabled, pwm_freq;
void cmd_pwm(void) {
unsigned int module, freq, pwm;

getargs(&cmdline, 5, ",");
if (argc != 5) error("Invalid syntax");
module = getinteger(argv[0]);
if (module != 1 && module != 4) error("Output Compare module must be 1 or 4");
freq = getinteger(argv[2]);
if (freq == 0) {
if (module == 1) OC1CONCLR = 0x0000ffff; // Disable output compare module 1
else OC4CONCLR = 0x0000ffff; // Disable output compare module 4
pwm_enabled &= ~module;
if (!pwm_enabled) {
pwm_freq = 0; // Clear saved frequency
T2CONCLR = 0x0000ffff; // Disable timer 2 module
}
return;
}
if (freq < 5 || freq > 800000) error("Invalid frequency (must be 5 - 800000, or 0 to disable)");
pwm = getinteger(argv[4]);
if (pwm < 0 || pwm > 100) error("Invalid PWM (must be 0 - 100)");
if (pwm_freq != freq) {
if (pwm_enabled & ~module) error("Cannot change frequency when other PWM output is enabled");
pwm_enabled = 0; // Clear status byte
pwm_freq = freq; // Save current frequency
OC1CONCLR = 0x0000ffff; // Disable output compare module 1
OC4CONCLR = 0x0000ffff; // Disable output compare module 4
T2CONCLR = 0x0000ffff; // Disable timer 2 module
if (freq >= 1280) {}
else if (freq >= 640) {freq <<= 1; T2CONSET = 0x00000010;}
else if (freq >= 320) {freq <<= 2; T2CONSET = 0x00000020;}
else if (freq >= 160) {freq <<= 3; T2CONSET = 0x00000030;}
else if (freq >= 80) {freq <<= 4; T2CONSET = 0x00000040;}
else if (freq >= 40) {freq <<= 5; T2CONSET = 0x00000050;}
else if (freq >= 20) {freq <<= 6; T2CONSET = 0x00000060;}
else {freq <<= 8; T2CONSET = 0x00000070;}
TMR2 = 0; // Clear timer 2 counter
PR2 = ((BUSFREQ + (freq >> 1)) / freq) - 1; // Set timer 2 period
}
if (module == 1) OC1RS = ((pwm * (PR2 + 1)) + 50) / 100; // Set duty period for output compare module 1
else OC4RS = ((pwm * (PR2 + 1)) + 50) / 100; // Set duty period for output compare module 4
if (!(pwm_enabled & module)) {
pwm_enabled |= module;
if (module == 1) {
OC1R = OC1RS; // Set initial duty period for output compare module 1
OC1CON = 0x00000006; // Enable PWM mode on output compare module 1
OC1CONSET = 0x00008000; // Enable output compare module 1
} else {
OC4R = OC1RS; // Set initial duty period for output compare module 4
OC4CON = 0x00000006; // Enable PWM mode on output compare module 4
OC4CONSET = 0x00008000; // Enable output compare module 4
}
if (pwm_enabled == module) T2CONSET = 0x00008000; // Enable timer 2
}
}


void pwm_disable() {
pwm_enabled = 0; // Clear status bits
pwm_freq = 0; // Clear current frequency
OC1CONCLR = 0x0000ffff; // Disable output compare module 1
OC4CONCLR = 0x0000ffff; // Disable output compare module 4
T2CONCLR = 0x0000ffff; // Disable timer 2 module
}


Custom.h was also modified to add the declaration for pwm_disable.
The pwm_disable function is called from the ClearExternalIO function to disable the PWM module when the running MMBasic program ends.

Regards

Gerard (vk3cg/vk3grs)Edited by seco61 2011-08-05

Regards

Gerard (vk3cg/vk3grs)
 
Blackened

Regular Member

Joined: 11/08/2012
Location: Australia
Posts: 66
Posted: 04:59am 13 Aug 2012
Copy link to clipboard 
Print this post

  seco61 said   Hi All.

I have modified the Maximite firmware to provide a PWM capability.

The PWM output is available on logical pins 7 - 10. The base PWM frequency can be from 1Hz to 5000Hz. The duty time can be set from 0% to 100%. However, at frequencies above 500Hz the duty time percentage will be rounded (eg at 1000Hz the increments are at 2% and at 5000Hz the increments are 10%).

*snip*

Firmware download: Maximite.hex

Please try and report any issues.

Regards

Gerard (vk3cg/vk3grs)


Hey Gerard

This is an old thread so I'm not expecting much, but I thought I'd ask. I have a Duinomite Mega and I was hoping to achieve something like what you have done. I only need a single PWM output pin. How hard would it be for me to incorporate your work into the DM 3.2C firmware? I'm completely clueless about this at the moment. I'm not sure if I'll spend the time to learn what I need to learn, or just use an external circuit on the audio out jack to switch from speaker to PWM out with amplification. A software solution would be my preference though.

I don't suppose anyone has already done this? Grasping at straws here I know

Cheers
Peter
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024