mirror of
https://github.com/eddyem/stm32samples.git
synced 2026-03-22 01:31:21 +03:00
rename pwmtest->pwmdmatest, add pwmtest
This commit is contained in:
@@ -1,2 +1 @@
|
||||
Generate signals on PA8 by PWM @ TIM1_CC1.
|
||||
Example for slow and high speed. High speed example works with 1-meter WS2815 LED strip (60 LEDs).
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
*/
|
||||
|
||||
#include "hardware.h"
|
||||
#include "proto.h"
|
||||
#include "usb.h"
|
||||
|
||||
static uint32_t PWM_freq = 80000; // 80kHz
|
||||
static uint32_t PWM_duty = 50; // PWM duty cycle in promille
|
||||
|
||||
static inline void gpio_setup(){
|
||||
// Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG
|
||||
@@ -26,33 +31,66 @@ static inline void gpio_setup(){
|
||||
AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15
|
||||
// Set led as opendrain output
|
||||
GPIOC->CRH |= CRH(13, CNF_ODOUTPUT|MODE_SLOW);
|
||||
// USB pullup (PA15) - pushpull output
|
||||
// USB pullup (PA15) - pushpull output, PA8 - alternate
|
||||
GPIOA->CRH = CRH(15, CNF_PPOUTPUT|MODE_SLOW) | CRH(8, CNF_AFPP | MODE_FAST);
|
||||
}
|
||||
|
||||
void hw_setup(){
|
||||
gpio_setup();
|
||||
static inline void tim1_setup(){
|
||||
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // enable TIM1 clocking
|
||||
TIM1->ARR = 8; // 9 ticks till UEV
|
||||
#ifdef HIGHSPEED
|
||||
TIM1->PSC = 7;
|
||||
#else
|
||||
TIM1->PSC = 6999;
|
||||
#endif
|
||||
TIM1->PSC = 8; // 8MHz
|
||||
TIM1->ARR = 99; // 100 ticks for 80kHz
|
||||
TIM1->CCR1 = 49; // 50%
|
||||
// PWM mode 1 (active->inactive)
|
||||
TIM1->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1;
|
||||
TIM1->CCMR1 = TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1PE;
|
||||
// main output
|
||||
TIM1->BDTR = TIM_BDTR_MOE;
|
||||
// main PWM output
|
||||
TIM1->CCER = TIM_CCER_CC1E;
|
||||
#ifndef HIGHSPEED
|
||||
NVIC_EnableIRQ(TIM1_CC_IRQn);
|
||||
#endif
|
||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // DMA1 clocking
|
||||
// memsize 8bit, periphsize 16bit, memincr, circular, mem2periph, half & full transfer interrupt
|
||||
DMA1_Channel5->CCR = DMA_CCR_PSIZE_0 | DMA_CCR_CIRC | DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_HTIE;
|
||||
DMA1_Channel5->CPAR = (uint32_t)&TIM1->CCR1;
|
||||
NVIC_EnableIRQ(DMA1_Channel5_IRQn);
|
||||
// turn it on
|
||||
TIM1->CR1 = TIM_CR1_CEN;// | TIM_CR1_ARPE;
|
||||
TIM1->EGR |= TIM_EGR_UG; // generate update event to refresh all
|
||||
}
|
||||
|
||||
static void refresh_ccr1(){
|
||||
if(PWM_duty == 0){
|
||||
TIM1->CCR1 = 0;
|
||||
return;
|
||||
}
|
||||
uint32_t ccr1 = (PWM_duty*(TIM1->ARR+1.))/1000;
|
||||
USB_send("New CCR1: "); USB_send(u2str(ccr1)); USB_send("\n");
|
||||
TIM1->CCR1 = ccr1;
|
||||
}
|
||||
|
||||
// return 0 if can't change or return 1
|
||||
int changePWMfreq(uint32_t freq){
|
||||
if(!freq) return 0; // zero frequency
|
||||
uint32_t ARRPSC = (uint32_t)TIM1FREQ / freq;
|
||||
// both ARR and PSC have 16 bits
|
||||
if(ARRPSC < 100) return 0; // frequency too big
|
||||
// 1hz: ARRPSC=72000000, ARR=1098, PSC=65513; Freq=72000000/1099/65514=1.0000016Hz
|
||||
// 98kHz: ARRPSC=734, ARR=0, PSC=733; Freq=72000000/1/734=98092Hz
|
||||
uint32_t PSC = ARRPSC >> 16;
|
||||
uint32_t ARR = ARRPSC / (PSC+1) - 1;
|
||||
PWM_freq = TIM1FREQ/(ARR+1.)/(PSC+1.);
|
||||
TIM1->ARR = ARR;
|
||||
TIM1->PSC = PSC;
|
||||
USB_send("New PSC: "); USB_send(u2str(PSC));
|
||||
USB_send("\nNew ARR: "); USB_send(u2str(ARR)); USB_send("\n");
|
||||
refresh_ccr1();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// change duty; return 0 if wrong, 1 if OK (duty in promille)
|
||||
int changePWMduty(uint32_t duty){
|
||||
if(duty > 1000) return 0; // wrong duty
|
||||
PWM_duty = duty;
|
||||
refresh_ccr1();
|
||||
return 1;
|
||||
}
|
||||
|
||||
void hw_setup(){
|
||||
gpio_setup();
|
||||
tim1_setup();
|
||||
}
|
||||
|
||||
void iwdg_setup(){
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
#include "stm32f1.h"
|
||||
|
||||
#define HIGHSPEED
|
||||
#define LEDS_NUM (60)
|
||||
// frequency of TIM1 clocking
|
||||
#define TIM1FREQ (72000000)
|
||||
|
||||
// LED0 - PC13 (bluepill), blinking each second
|
||||
#define LED0_port GPIOC
|
||||
@@ -42,4 +42,10 @@
|
||||
void hw_setup();
|
||||
void iwdg_setup();
|
||||
|
||||
#define PWM_ON() do{TIM1->CR1 = TIM_CR1_CEN | TIM_CR1_ARPE;}while(0)
|
||||
#define PWM_OFF() do{TIM1->CR1 = TIM_CR1_ARPE;}while(0)
|
||||
|
||||
int changePWMfreq(uint32_t freq);
|
||||
int changePWMduty(uint32_t duty);
|
||||
|
||||
#endif // __HARDWARE_H__
|
||||
|
||||
@@ -48,80 +48,6 @@ char *get_USB(){
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef HIGHSPEED
|
||||
void tim1_cc_isr(){
|
||||
TIM1->SR = 0;
|
||||
TIM1->CCR1 = 0;
|
||||
TIM1->DIER = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int chkctr(){
|
||||
static int LED_ctr = 0;
|
||||
if(++LED_ctr > LEDS_NUM){
|
||||
TIM1->CR1 |= TIM_CR1_OPM;
|
||||
// THIS IS A DIRTY HACK! IT WORKS ONLY @ HIGH SPEEDS, WHEN THERE'S NO TIME TO GO INTO IRQ
|
||||
// On small frequencies comment this line and allow CC1 IRQ
|
||||
#ifdef HIGHSPEED
|
||||
TIM1->CCR1 = 0;
|
||||
#else
|
||||
TIM1->DIER = TIM_DIER_CC1IE;
|
||||
#endif
|
||||
LED_ctr = 0;
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define _1 (6)
|
||||
#define _0 (3)
|
||||
|
||||
// R -> B (GRB)
|
||||
static uint8_t dmabuff[] = {
|
||||
_0,_0,_0,_0,_0,_0,_0,_0,
|
||||
_0,_0,_0,_0,_0,_0,_0,_0,
|
||||
_0,_0,_0,_0,_0,_0,_0,_0,
|
||||
_0,_0,_0,_0,_1,_1,_1,_1,
|
||||
_0,_0,_0,_0,_0,_0,_0,_0,
|
||||
_0,_0,_0,_0,_0,_0,_0,_0,
|
||||
};
|
||||
|
||||
void dma1_channel5_isr(){
|
||||
if(DMA1->ISR & DMA_ISR_TCIF5){ // transfer complete
|
||||
if(chkctr()){
|
||||
for(int i = 28; i < 32; ++i) // invert G in second half
|
||||
if(dmabuff[i] == _0) dmabuff[i] = _1;
|
||||
else dmabuff[i] = _0;
|
||||
}
|
||||
}else if(DMA1->ISR & DMA_ISR_HTIF5){ // half transfer complete
|
||||
if(chkctr()){
|
||||
for(int i = 20; i < 24; ++i) // invert B in first half
|
||||
if(dmabuff[i] == _0) dmabuff[i] = _1;
|
||||
else dmabuff[i] = _0;
|
||||
}
|
||||
}
|
||||
DMA1->IFCR = DMA_IFCR_CGIF5;
|
||||
}
|
||||
|
||||
static void sendone(){
|
||||
static uint8_t ctr = 7;
|
||||
TIM1->CR1 = 0; // stop
|
||||
DMA1_Channel5->CCR &= ~DMA_CCR_EN; // disable DMA to reconfigure
|
||||
// change values of dmabuff: G in first half & R in second
|
||||
if(--ctr == 1) ctr = 6; // 6..2
|
||||
for(int i = 0; i < 8; ++i){
|
||||
uint8_t val = (i == ctr) ? _1 : _0;
|
||||
dmabuff[i] = val;
|
||||
dmabuff[32+i] = val;
|
||||
}
|
||||
TIM1->DIER = TIM_DIER_UDE; // enable DMA requests
|
||||
DMA1->IFCR = DMA_IFCR_CGIF5;
|
||||
DMA1_Channel5->CNDTR = 48;
|
||||
DMA1_Channel5->CMAR = (uint32_t)dmabuff;
|
||||
DMA1_Channel5->CCR |= DMA_CCR_EN; // start DMA
|
||||
TIM1->CR1 = TIM_CR1_CEN | TIM_CR1_URS;// | TIM_CR1_DIR;
|
||||
}
|
||||
|
||||
int main(void){
|
||||
uint32_t lastT = 0;
|
||||
sysreset();
|
||||
@@ -141,18 +67,13 @@ int main(void){
|
||||
if(lastT > Tms || Tms - lastT > 499){
|
||||
LED_blink(LED0);
|
||||
lastT = Tms;
|
||||
sendone();
|
||||
}
|
||||
usb_proc();
|
||||
char *txt, *ans;
|
||||
if((txt = get_USB())){
|
||||
IWDG->KR = IWDG_REFRESH;
|
||||
ans = (char*)parse_cmd(txt);
|
||||
if(ans){
|
||||
uint16_t l = 0; char *p = ans;
|
||||
while(*p++) l++;
|
||||
USB_send(ans, l);
|
||||
}
|
||||
if(ans) USB_send(ans);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -18,28 +18,79 @@
|
||||
|
||||
#include "proto.h"
|
||||
#include "usb.h"
|
||||
#include "hardware.h"
|
||||
|
||||
/**
|
||||
* @brief getnum - get uint32_t
|
||||
* @param buf (i) - string with text
|
||||
* @param N (o) - number found
|
||||
* @return 1 if all OK
|
||||
*/
|
||||
static int getnum(const char *buf, uint32_t *N){
|
||||
char c;
|
||||
int found = 0;
|
||||
uint32_t val = 0;
|
||||
while((c = *buf)){
|
||||
if(c == '\t' || c == ' ') ++buf;
|
||||
else break;
|
||||
}
|
||||
while((c = *buf++)){
|
||||
if(c < '0' || c > '9') break;
|
||||
val = val * 10 + (uint32_t)(c - '0');
|
||||
found = 1;
|
||||
}
|
||||
if(found){
|
||||
*N = val;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#define USND(str) do{USB_send(str, sizeof(str)-1);}while(0)
|
||||
const char *parse_cmd(const char *buf){
|
||||
if(buf[1] != '\n') return buf;
|
||||
uint32_t U = 0;
|
||||
#define GETN() getnum(buf+1, &U)
|
||||
if(buf[1] != '\n'){
|
||||
switch(*buf){ // multisymbol commands
|
||||
case 'D': // change duty
|
||||
if(GETN()){
|
||||
if(changePWMduty(U)) return "OK\n";
|
||||
else return "Wrong\n";
|
||||
}else return "Wrong number\n";
|
||||
break;
|
||||
case 'F': // change frequency
|
||||
if(GETN()){
|
||||
if(changePWMfreq(U)) return "OK\n";
|
||||
else return "Wrong\n";
|
||||
}else return "Wrong number\n";
|
||||
break;
|
||||
default:
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
switch(*buf){
|
||||
/*case 'p':
|
||||
pin_toggle(USBPU_port, USBPU_pin);
|
||||
USND("USB pullup is ");
|
||||
if(pin_read(USBPU_port, USBPU_pin)) USND("off\n");
|
||||
else USND("on\n");
|
||||
return NULL;
|
||||
break;*/
|
||||
case '0':
|
||||
PWM_OFF();
|
||||
return "PWM is off\n";
|
||||
break;
|
||||
case '1':
|
||||
PWM_ON();
|
||||
return "PWM is on\n";
|
||||
break;
|
||||
case 'R':
|
||||
USND("Soft reset\n");
|
||||
USB_send("Soft reset\n");
|
||||
NVIC_SystemReset();
|
||||
break;
|
||||
case 'W':
|
||||
USND("Wait for reboot\n");
|
||||
USB_send("Wait for reboot\n");
|
||||
while(1){nop();};
|
||||
break;
|
||||
default: // help
|
||||
return
|
||||
"'0' - turn OFF PWM\n"
|
||||
"'1' - turn ON PWM\n"
|
||||
"'D n' - change duty (promille)\n"
|
||||
"'F n' - change freq (Hz)\n"
|
||||
"'R' - software reset\n"
|
||||
"'W' - test watchdog\n"
|
||||
;
|
||||
@@ -47,3 +98,19 @@ const char *parse_cmd(const char *buf){
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char strbuf[11];
|
||||
// return string buffer (strbuf) with val
|
||||
char *u2str(uint32_t val){
|
||||
char *bufptr = &strbuf[10];
|
||||
*bufptr = 0;
|
||||
if(!val){
|
||||
*(--bufptr) = '0';
|
||||
}else{
|
||||
while(val){
|
||||
*(--bufptr) = val % 10 + '0';
|
||||
val /= 10;
|
||||
}
|
||||
}
|
||||
return bufptr;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#ifndef PROTO_H__
|
||||
#define PROTO_H__
|
||||
|
||||
#include <stm32f1.h>
|
||||
|
||||
const char *parse_cmd(const char *buf);
|
||||
char *u2str(uint32_t val);
|
||||
|
||||
#endif // PROTO_H__
|
||||
|
||||
Binary file not shown.
@@ -16,9 +16,18 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//#include <string.h>
|
||||
|
||||
#include "usb.h"
|
||||
#include "usb_lib.h"
|
||||
|
||||
static int sstrlen(const char *s){
|
||||
if(!s) return 0;
|
||||
int l = 0;
|
||||
while(*s++) ++l;
|
||||
return l;
|
||||
}
|
||||
|
||||
static volatile uint8_t tx_succesfull = 1;
|
||||
static volatile uint8_t rxNE = 0;
|
||||
|
||||
@@ -87,22 +96,8 @@ static void send_next(){
|
||||
buflen = 0;
|
||||
}
|
||||
|
||||
// unblocking sending - just fill a buffer
|
||||
void USB_send(const char *buf, uint16_t len){
|
||||
if(!usbON || !len) return;
|
||||
if(len > USB_TXBUFSZ-1 - buflen){
|
||||
usbwr(usbbuff, buflen);
|
||||
buflen = 0;
|
||||
}
|
||||
if(len > USB_TXBUFSZ-1){
|
||||
USB_send_blk(buf, len);
|
||||
return;
|
||||
}
|
||||
while(len--) usbbuff[buflen++] = *buf++;
|
||||
}
|
||||
|
||||
// blocking sending
|
||||
void USB_send_blk(const char *buf, uint16_t len){
|
||||
static void _USB_send_blk(const char *buf, int len){
|
||||
if(!usbON || !len) return; // USB disconnected
|
||||
if(buflen){
|
||||
usbwr(usbbuff, buflen);
|
||||
@@ -121,6 +116,26 @@ void USB_send_blk(const char *buf, uint16_t len){
|
||||
}
|
||||
}
|
||||
|
||||
// unblocking sending - just fill a buffer
|
||||
void USB_send(const char *buf){
|
||||
if(!usbON || !buf || !*buf) return;
|
||||
int len = sstrlen(buf);
|
||||
if(len > USB_TXBUFSZ-1 - buflen){
|
||||
usbwr(usbbuff, buflen);
|
||||
buflen = 0;
|
||||
}
|
||||
if(len > USB_TXBUFSZ-1){
|
||||
_USB_send_blk(buf, len);
|
||||
return;
|
||||
}
|
||||
while(len--) usbbuff[buflen++] = *buf++;
|
||||
}
|
||||
|
||||
void USB_send_blk(const char *buf){
|
||||
int len = sstrlen(buf);
|
||||
_USB_send_blk(buf, len);
|
||||
}
|
||||
|
||||
void usb_proc(){
|
||||
switch(USB_Dev.USB_Status){
|
||||
case USB_STATE_CONFIGURED:
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
void USB_setup();
|
||||
void usb_proc();
|
||||
void USB_send(const char *buf, uint16_t len);
|
||||
void USB_send_blk(const char *buf, uint16_t len);
|
||||
void USB_send(const char *buf);
|
||||
void USB_send_blk(const char *buf);
|
||||
uint8_t USB_receive(uint8_t *buf);
|
||||
|
||||
#endif // __USB_H__
|
||||
|
||||
Reference in New Issue
Block a user