#include <stdio.h>
#include <stm32f10x.h>
#define RS_0 (GPIOA->BRR = GPIO_BRR_BR0)
#define RS_1 (GPIOA->BSRR = GPIO_BSRR_BS0)
#define RW_0 (GPIOA->BRR = GPIO_BRR_BR1)
#define RW_1 (GPIOA->BSRR = GPIO_BSRR_BS1)
#define E_0 (GPIOA->BRR = GPIO_BRR_BR2)
#define E_1 (GPIOA->BSRR = GPIO_BSRR_BS2)
#define FILE_USART (FILE *)10
struct fifo
{
uint8_t buf[127];
uint8_t front;
uint8_t rear;
};
uint32_t num = 0;
uint8_t seg8[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
struct fifo usartbuf;
/*
// 正常的液晶延时函数
void delay(void)
{
uint8_t i;
for (i = 0; i < 10; i++);
}
*/
// 故意减慢液晶接收字符的速度
void delay(void)
{
uint32_t i;
for (i = 0; i < 100000; i++);
}
void delay2(void)
{
uint32_t i;
for (i = 0; i < 3000000; i++);
}
void fifo_init(struct fifo *f)
{
f->front = f->rear = 0;
}
uint8_t fifo_full(struct fifo *f)
{
return f->front == (f->rear + 1) % sizeof(f->buf);
}
uint8_t fifo_empty(struct fifo *f)
{
return f->front == f->rear;
}
uint8_t fifo_in(struct fifo *f, uint8_t data)
{
if (fifo_full(f))
return 0;
f->buf[f->rear] = data;
f->rear = (f->rear + 1) % sizeof(f->buf);
return 1;
}
uint8_t fifo_out(struct fifo *f, uint8_t *data)
{
if (fifo_empty(f))
return 0;
*data = f->buf[f->front];
f->front = (f->front + 1) % sizeof(f->buf);
return 1;
}
void LCD1602_BusyWait(void)
{
RS_0;
RW_1;
E_1;
GPIOC->CRL = 0x44444444; // 读端口
while (GPIOC->IDR & GPIO_IDR_IDR7);
GPIOC->CRL = 0x33333333;
E_0;
}
void LCD1602_WriteCmd(uint8_t cmd)
{
LCD1602_BusyWait();
RS_0;
RW_0;
__disable_irq();
GPIOC->ODR = (GPIOC->ODR & 0xff00) | cmd;
__enable_irq();
E_1;
delay();
E_0;
}
void LCD1602_WriteData(uint8_t data)
{
LCD1602_BusyWait();
RS_1;
RW_0;
__disable_irq();
GPIOC->ODR = (GPIOC->ODR & 0xff00) | data;
__enable_irq();
E_1;
delay();
E_0;
}
void LCD1602_Init(void)
{
LCD1602_WriteCmd(0x38);
LCD1602_WriteCmd(0x01);
LCD1602_WriteCmd(0x0c);
// 设置背光电压
DAC->CR = DAC_CR_EN1;
DAC->DHR8R1 = 140;
}
int fputc(int ch, FILE *fp)
{
if (fp == &__stderr || fp == FILE_USART)
{
USART1->DR = ch;
while ((USART1->SR & USART_SR_TXE) == 0);
}
else if (fp == &__stdout)
{
if (ch == '\n')
LCD1602_WriteCmd(0x01); // 遇到\n就清屏
else
LCD1602_WriteData(ch);
}
return ch;
}
uint8_t PCF8591_ADC(void)
{
uint8_t temp;
I2C1->CR1 |= I2C_CR1_START; // 开始信号
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x90; // 地址码(写)
while ((I2C1->SR1 & I2C_SR1_ADDR) == 0); // PB6~7必须接外部上拉电阻, 否则程序会卡死在这里
temp = I2C1->SR2; // 读SR2, 清ADDR
I2C1->DR = 0x04; // 四路单输入, 通道0, 自动增益
while ((I2C1->SR1 & I2C_SR1_TXE) == 0);
I2C1->CR1 |= I2C_CR1_START; // 重新发送开始信号
while ((I2C1->SR1 & I2C_SR1_SB) == 0);
I2C1->DR = 0x91; // 地址码(读)
while ((I2C1->SR1 & I2C_SR1_ADDR) == 0);
temp = I2C1->SR2; // 读SR2, 清ADDR
while ((I2C1->SR1 & I2C_SR1_RXNE) == 0);
temp = I2C1->DR;
I2C1->CR1 |= I2C_CR1_STOP; // 停止信号
while (I2C1->CR1 & I2C_CR1_STOP);
return temp;
}
int main(void)
{
uint8_t value;
uint8_t pos = 0;
uint8_t display_finish = 1;
RCC->APB1ENR = RCC_APB1ENR_DACEN | RCC_APB1ENR_I2C1EN | RCC_APB1ENR_TIM2EN;
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_USART1EN;
GPIOA->CRL = 0x00000333; // 1602液晶控制端
GPIOA->CRH = 0x000004b0; // USART端口
GPIOB->CRL = 0xff000000; // I2C总线: PB6~7设为复用开漏50MHz输出
GPIOB->CRH = 0x33333333; // 数码管段选
GPIOC->CRL = 0x33333333; // 1602液晶数据端
GPIOC->ODR = 0x3000; // 熄灭数码管
GPIOC->CRH = 0x00333333; // 数码管位选
// 数码管扫描中断配置
TIM2->ARR = 29;
TIM2->PSC = 7199;
TIM2->EGR = TIM_EGR_UG; // 立即触发TIM2中断, 点亮数码管
TIM2->DIER = TIM_DIER_UIE;
NVIC_EnableIRQ(TIM2_IRQn); // 该函数位于core_cm3.h, 是MDK自带的函数, 不是库函数
TIM2->CR1 |= TIM_CR1_CEN;
fifo_init(&usartbuf);
// MAX232的电源必须接3.3V, 否则PC端会收到很多奇怪的数据, 例如0xfb, 0xff, 0xbf等
USART1->BRR = 0x271;
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
NVIC_SetPriority(USART1_IRQn, 1); // 数码管中断的优先级为0, 因此可以抢占串口接收中断, 防止数码管闪烁
NVIC_EnableIRQ(USART1_IRQn);
I2C1->CR2 = 36; // FREQ=100100(36MHz)
I2C1->CCR = 90; // CCR=FREQ/2f, f=200kHz
I2C1->CR1 = I2C_CR1_PE; // 启动总线
LCD1602_Init();
perror("Hello World!");
while (1)
{
while (fifo_out(&usartbuf, &value))
{
display_finish = 0;
if (value == '\n')
pos = 0;
putchar(value);
pos++;
if (pos == 16)
LCD1602_WriteCmd(0xc0);
else if (pos == 32)
{
pos = 0;
LCD1602_WriteCmd(0x80);
}
}
if (display_finish == 0)
{
// 液晶字符显示完毕时, 向串口发送AD转换值
value = PCF8591_ADC();
fprintf(FILE_USART, "ADC Value:%d\r\nusartbuf.front=%d, usartbuf.rear=%d\r\n", value, usartbuf.front, usartbuf.rear); // 必须用\r\n才能正确换行
display_finish = 1;
}
}
}
// 数码管动态扫描(高优先级中断)
void TIM2_IRQHandler(void)
{
static uint8_t pos = 5;
static uint32_t numbuf;
if (TIM2->SR & TIM_SR_UIF) // 定时器2溢出
{
TIM2->SR &= ~TIM_SR_UIF;
if (pos == 5)
numbuf = num;
else
numbuf /= 10;
GPIOC->ODR = (GPIOC->ODR & 0xf0ff) | 0x3000; // 关闭位选
GPIOB->ODR = (GPIOB->ODR & 0xff) | (seg8[numbuf % 10] << 8); // 设置显示字符
if (pos <= 3)
GPIOC->ODR |= 1 << (pos + 8); // 位选直连I/O端
else
GPIOC->ODR &= ~(1 << (pos + 8)); // 三极管驱动的位选端
if (pos == 0)
pos = 5;
else
pos--;
}
}
// 串口接收中断(较低优先级中断)
void USART1_IRQHandler(void)
{
uint8_t data;
if (USART1->SR & USART_SR_RXNE)
{
data = USART1->DR;
num = (num + data) % 1000000;
fifo_in(&usartbuf, data);
}
}