rename pwmtest->pwmdmatest, add pwmtest

This commit is contained in:
Edward Emelianov
2021-01-26 23:12:32 +03:00
parent 40cf0e7cf3
commit ad5e9d5abf
22 changed files with 1619 additions and 129 deletions

View File

@@ -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).

View File

@@ -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(){

View File

@@ -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__

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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.

View File

@@ -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:

View File

@@ -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__